build(deps): bump github.com/open-policy-agent/opa from 1.4.2 to 1.5.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.4.2 to 1.5.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.4.2...v1.5.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
+1
-1
@@ -34,6 +34,6 @@ import (
|
||||
// Sets are considered equal if and only if the symmetric difference of a and b
|
||||
// is empty.
|
||||
// Other comparisons are consistent but not defined.
|
||||
func Compare(a, b interface{}) int {
|
||||
func Compare(a, b any) int {
|
||||
return v1.Compare(a, b)
|
||||
}
|
||||
|
||||
+1
-1
@@ -41,6 +41,6 @@ type ErrorDetails = v1.ErrorDetails
|
||||
type Error = v1.Error
|
||||
|
||||
// NewError returns a new Error object.
|
||||
func NewError(code string, loc *Location, f string, a ...interface{}) *Error {
|
||||
func NewError(code string, loc *Location, f string, a ...any) *Error {
|
||||
return v1.NewError(code, loc, f, a...)
|
||||
}
|
||||
|
||||
+2
-2
@@ -211,7 +211,7 @@ func NewBody(exprs ...*Expr) Body {
|
||||
}
|
||||
|
||||
// NewExpr returns a new Expr object.
|
||||
func NewExpr(terms interface{}) *Expr {
|
||||
func NewExpr(terms any) *Expr {
|
||||
return v1.NewExpr(terms)
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ func NewBuiltinExpr(terms ...*Term) *Expr {
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of the AST node x. If x is not an AST node, x is returned unmodified.
|
||||
func Copy(x interface{}) interface{} {
|
||||
func Copy(x any) any {
|
||||
return v1.Copy(x)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -13,6 +13,6 @@ import (
|
||||
// Pretty writes a pretty representation of the AST rooted at x to w.
|
||||
//
|
||||
// This is function is intended for debug purposes when inspecting ASTs.
|
||||
func Pretty(w io.Writer, x interface{}) {
|
||||
func Pretty(w io.Writer, x any) {
|
||||
v1.Pretty(w, x)
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,6 +9,6 @@ import (
|
||||
)
|
||||
|
||||
// TypeName returns a human readable name for the AST element type.
|
||||
func TypeName(x interface{}) string {
|
||||
func TypeName(x any) string {
|
||||
return v1.TypeName(x)
|
||||
}
|
||||
|
||||
+11
-11
@@ -30,7 +30,7 @@ func NewLocation(text []byte, file string, row int, col int) *Location {
|
||||
type Value = v1.Value
|
||||
|
||||
// InterfaceToValue converts a native Go value x to a Value.
|
||||
func InterfaceToValue(x interface{}) (Value, error) {
|
||||
func InterfaceToValue(x any) (Value, error) {
|
||||
return v1.InterfaceToValue(x)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func ValueFromReader(r io.Reader) (Value, error) {
|
||||
}
|
||||
|
||||
// As converts v into a Go native type referred to by x.
|
||||
func As(v Value, x interface{}) error {
|
||||
func As(v Value, x any) error {
|
||||
return v1.As(v, x)
|
||||
}
|
||||
|
||||
@@ -62,13 +62,13 @@ func IsUnknownValueErr(err error) bool {
|
||||
// ValueToInterface returns the Go representation of an AST value. The AST
|
||||
// value should not contain any values that require evaluation (e.g., vars,
|
||||
// comprehensions, etc.)
|
||||
func ValueToInterface(v Value, resolver Resolver) (interface{}, error) {
|
||||
func ValueToInterface(v Value, resolver Resolver) (any, error) {
|
||||
return v1.ValueToInterface(v, resolver)
|
||||
}
|
||||
|
||||
// JSON returns the JSON representation of v. The value must not contain any
|
||||
// refs or terms that require evaluation (e.g., vars, comprehensions, etc.)
|
||||
func JSON(v Value) (interface{}, error) {
|
||||
func JSON(v Value) (any, error) {
|
||||
return v1.JSON(v)
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ type JSONOpt = v1.JSONOpt
|
||||
|
||||
// JSONWithOpt returns the JSON representation of v. The value must not contain any
|
||||
// refs or terms that require evaluation (e.g., vars, comprehensions, etc.)
|
||||
func JSONWithOpt(v Value, opt JSONOpt) (interface{}, error) {
|
||||
func JSONWithOpt(v Value, opt JSONOpt) (any, error) {
|
||||
return v1.JSONWithOpt(v, opt)
|
||||
}
|
||||
|
||||
@@ -85,14 +85,14 @@ func JSONWithOpt(v Value, opt JSONOpt) (interface{}, error) {
|
||||
// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) If
|
||||
// the conversion fails, this function will panic. This function is mostly for
|
||||
// test purposes.
|
||||
func MustJSON(v Value) interface{} {
|
||||
func MustJSON(v Value) any {
|
||||
return v1.MustJSON(v)
|
||||
}
|
||||
|
||||
// MustInterfaceToValue converts a native Go value x to a Value. If the
|
||||
// conversion fails, this function will panic. This function is mostly for test
|
||||
// purposes.
|
||||
func MustInterfaceToValue(x interface{}) Value {
|
||||
func MustInterfaceToValue(x any) Value {
|
||||
return v1.MustInterfaceToValue(x)
|
||||
}
|
||||
|
||||
@@ -115,17 +115,17 @@ func IsComprehension(x Value) bool {
|
||||
}
|
||||
|
||||
// ContainsRefs returns true if the Value v contains refs.
|
||||
func ContainsRefs(v interface{}) bool {
|
||||
func ContainsRefs(v any) bool {
|
||||
return v1.ContainsRefs(v)
|
||||
}
|
||||
|
||||
// ContainsComprehensions returns true if the Value v contains comprehensions.
|
||||
func ContainsComprehensions(v interface{}) bool {
|
||||
func ContainsComprehensions(v any) bool {
|
||||
return v1.ContainsComprehensions(v)
|
||||
}
|
||||
|
||||
// ContainsClosures returns true if the Value v contains closures.
|
||||
func ContainsClosures(v interface{}) bool {
|
||||
func ContainsClosures(v any) bool {
|
||||
return v1.ContainsClosures(v)
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ func ObjectTerm(o ...[2]*Term) *Term {
|
||||
return v1.ObjectTerm(o...)
|
||||
}
|
||||
|
||||
func LazyObject(blob map[string]interface{}) Object {
|
||||
func LazyObject(blob map[string]any) Object {
|
||||
return v1.LazyObject(blob)
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -16,22 +16,22 @@ type Transformer = v1.Transformer
|
||||
|
||||
// Transform iterates the AST and calls the Transform function on the
|
||||
// Transformer t for x before recursing.
|
||||
func Transform(t Transformer, x interface{}) (interface{}, error) {
|
||||
func Transform(t Transformer, x any) (any, error) {
|
||||
return v1.Transform(t, x)
|
||||
}
|
||||
|
||||
// TransformRefs calls the function f on all references under x.
|
||||
func TransformRefs(x interface{}, f func(Ref) (Value, error)) (interface{}, error) {
|
||||
func TransformRefs(x any, f func(Ref) (Value, error)) (any, error) {
|
||||
return v1.TransformRefs(x, f)
|
||||
}
|
||||
|
||||
// TransformVars calls the function f on all vars under x.
|
||||
func TransformVars(x interface{}, f func(Var) (Value, error)) (interface{}, error) {
|
||||
func TransformVars(x any, f func(Var) (Value, error)) (any, error) {
|
||||
return v1.TransformVars(x, f)
|
||||
}
|
||||
|
||||
// TransformComprehensions calls the functio nf on all comprehensions under x.
|
||||
func TransformComprehensions(x interface{}, f func(interface{}) (Value, error)) (interface{}, error) {
|
||||
func TransformComprehensions(x any, f func(any) (Value, error)) (any, error) {
|
||||
return v1.TransformComprehensions(x, f)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,6 @@ type GenericTransformer = v1.GenericTransformer
|
||||
|
||||
// NewGenericTransformer returns a new GenericTransformer that will transform
|
||||
// AST nodes using the function f.
|
||||
func NewGenericTransformer(f func(x interface{}) (interface{}, error)) *GenericTransformer {
|
||||
func NewGenericTransformer(f func(x any) (any, error)) *GenericTransformer {
|
||||
return v1.NewGenericTransformer(f)
|
||||
}
|
||||
|
||||
+13
-13
@@ -21,68 +21,68 @@ type BeforeAndAfterVisitor = v1.BeforeAndAfterVisitor
|
||||
// Walk iterates the AST by calling the Visit function on the Visitor
|
||||
// v for x before recursing.
|
||||
// Deprecated: use GenericVisitor.Walk
|
||||
func Walk(v Visitor, x interface{}) {
|
||||
func Walk(v Visitor, x any) {
|
||||
v1.Walk(v, x)
|
||||
}
|
||||
|
||||
// WalkBeforeAndAfter iterates the AST by calling the Visit function on the
|
||||
// Visitor v for x before recursing.
|
||||
// Deprecated: use GenericVisitor.Walk
|
||||
func WalkBeforeAndAfter(v BeforeAndAfterVisitor, x interface{}) {
|
||||
func WalkBeforeAndAfter(v BeforeAndAfterVisitor, x any) {
|
||||
v1.WalkBeforeAndAfter(v, x)
|
||||
}
|
||||
|
||||
// WalkVars calls the function f on all vars under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkVars(x interface{}, f func(Var) bool) {
|
||||
func WalkVars(x any, f func(Var) bool) {
|
||||
v1.WalkVars(x, f)
|
||||
}
|
||||
|
||||
// WalkClosures calls the function f on all closures under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkClosures(x interface{}, f func(interface{}) bool) {
|
||||
func WalkClosures(x any, f func(any) bool) {
|
||||
v1.WalkClosures(x, f)
|
||||
}
|
||||
|
||||
// WalkRefs calls the function f on all references under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkRefs(x interface{}, f func(Ref) bool) {
|
||||
func WalkRefs(x any, f func(Ref) bool) {
|
||||
v1.WalkRefs(x, f)
|
||||
}
|
||||
|
||||
// WalkTerms calls the function f on all terms under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkTerms(x interface{}, f func(*Term) bool) {
|
||||
func WalkTerms(x any, f func(*Term) bool) {
|
||||
v1.WalkTerms(x, f)
|
||||
}
|
||||
|
||||
// WalkWiths calls the function f on all with modifiers under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkWiths(x interface{}, f func(*With) bool) {
|
||||
func WalkWiths(x any, f func(*With) bool) {
|
||||
v1.WalkWiths(x, f)
|
||||
}
|
||||
|
||||
// WalkExprs calls the function f on all expressions under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkExprs(x interface{}, f func(*Expr) bool) {
|
||||
func WalkExprs(x any, f func(*Expr) bool) {
|
||||
v1.WalkExprs(x, f)
|
||||
}
|
||||
|
||||
// WalkBodies calls the function f on all bodies under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkBodies(x interface{}, f func(Body) bool) {
|
||||
func WalkBodies(x any, f func(Body) bool) {
|
||||
v1.WalkBodies(x, f)
|
||||
}
|
||||
|
||||
// WalkRules calls the function f on all rules under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkRules(x interface{}, f func(*Rule) bool) {
|
||||
func WalkRules(x any, f func(*Rule) bool) {
|
||||
v1.WalkRules(x, f)
|
||||
}
|
||||
|
||||
// WalkNodes calls the function f on all nodes under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkNodes(x interface{}, f func(Node) bool) {
|
||||
func WalkNodes(x any, f func(Node) bool) {
|
||||
v1.WalkNodes(x, f)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ type GenericVisitor = v1.GenericVisitor
|
||||
|
||||
// NewGenericVisitor returns a new GenericVisitor that will invoke the function
|
||||
// f on AST nodes.
|
||||
func NewGenericVisitor(f func(x interface{}) bool) *GenericVisitor {
|
||||
func NewGenericVisitor(f func(x any) bool) *GenericVisitor {
|
||||
return v1.NewGenericVisitor(f)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ type BeforeAfterVisitor = v1.BeforeAfterVisitor
|
||||
|
||||
// NewBeforeAfterVisitor returns a new BeforeAndAfterVisitor that
|
||||
// will invoke the functions before and after AST nodes.
|
||||
func NewBeforeAfterVisitor(before func(x interface{}) bool, after func(x interface{})) *BeforeAfterVisitor {
|
||||
func NewBeforeAfterVisitor(before func(x any) bool, after func(x any)) *BeforeAfterVisitor {
|
||||
return v1.NewBeforeAfterVisitor(before, after)
|
||||
}
|
||||
|
||||
|
||||
+32
-3
@@ -7,6 +7,7 @@ package bundle
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/storage"
|
||||
v1 "github.com/open-policy-agent/opa/v1/bundle"
|
||||
)
|
||||
@@ -70,7 +71,7 @@ func ReadBundleRevisionFromStore(ctx context.Context, store storage.Store, txn s
|
||||
// ReadBundleMetadataFromStore returns the metadata in the specified bundle.
|
||||
// If the bundle is not activated, this function will return
|
||||
// storage NotFound error.
|
||||
func ReadBundleMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (map[string]interface{}, error) {
|
||||
func ReadBundleMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (map[string]any, error) {
|
||||
return v1.ReadBundleMetadataFromStore(ctx, store, txn, name)
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@ type ActivateOpts = v1.ActivateOpts
|
||||
// Activate the bundle(s) by loading into the given Store. This will load policies, data, and record
|
||||
// the manifest in storage. The compiler provided will have had the polices compiled on it.
|
||||
func Activate(opts *ActivateOpts) error {
|
||||
return v1.Activate(opts)
|
||||
return v1.Activate(setActivateDefaultRegoVersion(opts))
|
||||
}
|
||||
|
||||
// DeactivateOpts defines options for the Deactivate API call
|
||||
@@ -95,7 +96,7 @@ type DeactivateOpts = v1.DeactivateOpts
|
||||
|
||||
// Deactivate the bundle(s). This will erase associated data, policies, and the manifest entry from the store.
|
||||
func Deactivate(opts *DeactivateOpts) error {
|
||||
return v1.Deactivate(opts)
|
||||
return v1.Deactivate(setDeactivateDefaultRegoVersion(opts))
|
||||
}
|
||||
|
||||
// LegacyWriteManifestToStore will write the bundle manifest to the older single (unnamed) bundle manifest location.
|
||||
@@ -121,3 +122,31 @@ func LegacyReadRevisionFromStore(ctx context.Context, store storage.Store, txn s
|
||||
func ActivateLegacy(opts *ActivateOpts) error {
|
||||
return v1.ActivateLegacy(opts)
|
||||
}
|
||||
|
||||
func setActivateDefaultRegoVersion(opts *ActivateOpts) *ActivateOpts {
|
||||
if opts == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.ParserOptions.RegoVersion == ast.RegoUndefined {
|
||||
cpy := *opts
|
||||
cpy.ParserOptions.RegoVersion = ast.DefaultRegoVersion
|
||||
return &cpy
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func setDeactivateDefaultRegoVersion(opts *DeactivateOpts) *DeactivateOpts {
|
||||
if opts == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if opts.ParserOptions.RegoVersion == ast.RegoUndefined {
|
||||
cpy := *opts
|
||||
cpy.ParserOptions.RegoVersion = ast.DefaultRegoVersion
|
||||
return &cpy
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
+4849
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -70,7 +70,7 @@ func ParseServicesConfig(opts ServiceOptions) (map[string]rest.Client, error) {
|
||||
// read from disk (if specified) and overrides will be applied. If no config file is
|
||||
// specified, the overrides can still be applied to an empty config.
|
||||
func Load(configFile string, overrides []string, overrideFiles []string) ([]byte, error) {
|
||||
baseConf := map[string]interface{}{}
|
||||
baseConf := map[string]any{}
|
||||
|
||||
// User specified config file
|
||||
if configFile != "" {
|
||||
@@ -88,7 +88,7 @@ func Load(configFile string, overrides []string, overrideFiles []string) ([]byte
|
||||
}
|
||||
}
|
||||
|
||||
overrideConf := map[string]interface{}{}
|
||||
overrideConf := map[string]any{}
|
||||
|
||||
// User specified a config override via --set
|
||||
for _, override := range overrides {
|
||||
@@ -100,7 +100,7 @@ func Load(configFile string, overrides []string, overrideFiles []string) ([]byte
|
||||
|
||||
// User specified a config override value via --set-file
|
||||
for _, override := range overrideFiles {
|
||||
reader := func(rs []rune) (interface{}, error) {
|
||||
reader := func(rs []rune) (any, error) {
|
||||
bytes, err := os.ReadFile(string(rs))
|
||||
value := strings.TrimSpace(string(bytes))
|
||||
return value, err
|
||||
@@ -141,21 +141,21 @@ func subEnvVars(s string) string {
|
||||
}
|
||||
|
||||
// mergeValues will merge source and destination map, preferring values from the source map
|
||||
func mergeValues(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
|
||||
func mergeValues(dest map[string]any, src map[string]any) map[string]any {
|
||||
for k, v := range src {
|
||||
// If the key doesn't exist already, then just set the key to that value
|
||||
if _, exists := dest[k]; !exists {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
nextMap, ok := v.(map[string]interface{})
|
||||
nextMap, ok := v.(map[string]any)
|
||||
// If it isn't another map, overwrite the value
|
||||
if !ok {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
// Edge case: If the key exists in the destination, but isn't a map
|
||||
destMap, isMap := dest[k].(map[string]interface{})
|
||||
destMap, isMap := dest[k].(map[string]any)
|
||||
// If the source map has a map for this key, prefer it
|
||||
if !isMap {
|
||||
dest[k] = v
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
// Debug allows printing debug messages.
|
||||
type Debug interface {
|
||||
// Printf prints, with a short file:line-number prefix
|
||||
Printf(format string, args ...interface{})
|
||||
Printf(format string, args ...any)
|
||||
// Writer returns the writer being written to, which may be
|
||||
// `io.Discard` if no debug output is requested.
|
||||
Writer() io.Writer
|
||||
|
||||
+8
-8
@@ -5,25 +5,25 @@
|
||||
package deepcopy
|
||||
|
||||
// DeepCopy performs a recursive deep copy for nested slices/maps and
|
||||
// returns the copied object. Supports []interface{}
|
||||
// and map[string]interface{} only
|
||||
func DeepCopy(val interface{}) interface{} {
|
||||
// returns the copied object. Supports []any
|
||||
// and map[string]any only
|
||||
func DeepCopy(val any) any {
|
||||
switch val := val.(type) {
|
||||
case []interface{}:
|
||||
cpy := make([]interface{}, len(val))
|
||||
case []any:
|
||||
cpy := make([]any, len(val))
|
||||
for i := range cpy {
|
||||
cpy[i] = DeepCopy(val[i])
|
||||
}
|
||||
return cpy
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
return Map(val)
|
||||
default:
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
func Map(val map[string]interface{}) map[string]interface{} {
|
||||
cpy := make(map[string]interface{}, len(val))
|
||||
func Map(val map[string]any) map[string]any {
|
||||
cpy := make(map[string]any, len(val))
|
||||
for k := range val {
|
||||
cpy[k] = DeepCopy(val[k])
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ func shiftLower(bit byte, b []byte) byte {
|
||||
// position of the first byte in the slice.
|
||||
// This returns the bit that was shifted off the last byte.
|
||||
func shiftHigher(bit byte, b []byte) byte {
|
||||
for i := 0; i < len(b); i++ {
|
||||
for i := range b {
|
||||
newByte := b[i] << 1
|
||||
newByte |= bit
|
||||
bit = (b[i] & 0x80) >> 7
|
||||
|
||||
+5
-3
@@ -723,15 +723,17 @@ func (e *EditTree) Unfold(path ast.Ref) (*EditTree, error) {
|
||||
return child.Unfold(path[1:])
|
||||
}
|
||||
|
||||
idxt := ast.InternedIntNumberTerm(idx)
|
||||
|
||||
// Fall back to looking up the key in e.value.
|
||||
// Extend the tree if key is present. Error otherwise.
|
||||
if v, err := x.Find(ast.Ref{ast.InternedIntNumberTerm(idx)}); err == nil {
|
||||
if v, err := x.Find(ast.Ref{idxt}); err == nil {
|
||||
// TODO: Consider a more efficient "Replace" function that special-cases this for arrays instead?
|
||||
_, err := e.Delete(ast.InternedIntNumberTerm(idx))
|
||||
_, err := e.Delete(idxt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
child, err := e.Insert(ast.IntNumberTerm(idx), ast.NewTerm(v))
|
||||
child, err := e.Insert(idxt, ast.NewTerm(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+3
-5
@@ -19,14 +19,12 @@ func FilterFutureImports(imps []*ast.Import) []*ast.Import {
|
||||
return ret
|
||||
}
|
||||
|
||||
var keywordsTerm = ast.StringTerm("keywords")
|
||||
|
||||
// IsAllFutureKeywords returns true if the passed *ast.Import is `future.keywords`
|
||||
func IsAllFutureKeywords(imp *ast.Import) bool {
|
||||
path := imp.Path.Value.(ast.Ref)
|
||||
return len(path) == 2 &&
|
||||
ast.FutureRootDocument.Equal(path[0]) &&
|
||||
path[1].Equal(keywordsTerm)
|
||||
path[1].Equal(ast.InternedStringTerm("keywords"))
|
||||
}
|
||||
|
||||
// IsFutureKeyword returns true if the passed *ast.Import is `future.keywords.{kw}`
|
||||
@@ -34,7 +32,7 @@ func IsFutureKeyword(imp *ast.Import, kw string) bool {
|
||||
path := imp.Path.Value.(ast.Ref)
|
||||
return len(path) == 3 &&
|
||||
ast.FutureRootDocument.Equal(path[0]) &&
|
||||
path[1].Equal(keywordsTerm) &&
|
||||
path[1].Equal(ast.InternedStringTerm("keywords")) &&
|
||||
path[2].Equal(ast.StringTerm(kw))
|
||||
}
|
||||
|
||||
@@ -42,7 +40,7 @@ func WhichFutureKeyword(imp *ast.Import) (string, bool) {
|
||||
path := imp.Path.Value.(ast.Ref)
|
||||
if len(path) == 3 &&
|
||||
ast.FutureRootDocument.Equal(path[0]) &&
|
||||
path[1].Equal(keywordsTerm) {
|
||||
path[1].Equal(ast.InternedStringTerm("keywords")) {
|
||||
if str, ok := path[2].Value.(ast.String); ok {
|
||||
return string(str), true
|
||||
}
|
||||
|
||||
+2
-2
@@ -86,12 +86,12 @@ func (dc draftConfigs) GetSchemaURL(draft Draft) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseSchemaURL(documentNode interface{}) (string, *Draft, error) {
|
||||
func parseSchemaURL(documentNode any) (string, *Draft, error) {
|
||||
if _, ok := documentNode.(bool); ok {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
m, ok := documentNode.(map[string]interface{})
|
||||
m, ok := documentNode.(map[string]any)
|
||||
if !ok {
|
||||
return "", nil, errors.New("schema is invalid")
|
||||
}
|
||||
|
||||
+1
-1
@@ -212,7 +212,7 @@ type (
|
||||
)
|
||||
|
||||
// newError takes a ResultError type and sets the type, context, description, details, value, and field
|
||||
func newError(err ResultError, context *JSONContext, value interface{}, locale locale, details ErrorDetails) {
|
||||
func newError(err ResultError, context *JSONContext, value any, locale locale, details ErrorDetails) {
|
||||
var t string
|
||||
var d string
|
||||
switch err.(type) {
|
||||
|
||||
+16
-16
@@ -14,7 +14,7 @@ type (
|
||||
// FormatChecker is the interface all formatters added to FormatCheckerChain must implement
|
||||
FormatChecker interface {
|
||||
// IsFormat checks if input has the correct format
|
||||
IsFormat(input interface{}) bool
|
||||
IsFormat(input any) bool
|
||||
}
|
||||
|
||||
// FormatCheckerChain holds the formatters
|
||||
@@ -174,7 +174,7 @@ func (c *FormatCheckerChain) Has(name string) bool {
|
||||
|
||||
// IsFormat will check an input against a FormatChecker with the given name
|
||||
// to see if it is the correct format
|
||||
func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
|
||||
func (c *FormatCheckerChain) IsFormat(name string, input any) bool {
|
||||
lock.RLock()
|
||||
f, ok := c.formatters[name]
|
||||
lock.RUnlock()
|
||||
@@ -188,7 +188,7 @@ func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted e-mail address
|
||||
func (f EmailFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f EmailFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -199,7 +199,7 @@ func (f EmailFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted IPv4-address
|
||||
func (f IPV4FormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f IPV4FormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -211,7 +211,7 @@ func (f IPV4FormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted IPv6=address
|
||||
func (f IPV6FormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f IPV6FormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -223,7 +223,7 @@ func (f IPV6FormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted date/time per RFC3339 5.6
|
||||
func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f DateTimeFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -247,7 +247,7 @@ func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted date (YYYY-MM-DD)
|
||||
func (f DateFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f DateFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -257,7 +257,7 @@ func (f DateFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input correctly formatted time (HH:MM:SS or HH:MM:SSZ-07:00)
|
||||
func (f TimeFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f TimeFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -272,7 +272,7 @@ func (f TimeFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is correctly formatted URI with a valid Scheme per RFC3986
|
||||
func (f URIFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f URIFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -288,7 +288,7 @@ func (f URIFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted URI or relative-reference per RFC3986
|
||||
func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f URIReferenceFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -299,7 +299,7 @@ func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted URI template per RFC6570
|
||||
func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f URITemplateFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -314,7 +314,7 @@ func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted hostname
|
||||
func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f HostnameFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -324,7 +324,7 @@ func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted UUID
|
||||
func (f UUIDFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f UUIDFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -334,7 +334,7 @@ func (f UUIDFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted regular expression
|
||||
func (f RegexFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f RegexFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -348,7 +348,7 @@ func (f RegexFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted JSON Pointer per RFC6901
|
||||
func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f JSONPointerFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
@@ -358,7 +358,7 @@ func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
|
||||
}
|
||||
|
||||
// IsFormat checks if input is a correctly formatted relative JSON Pointer
|
||||
func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
|
||||
func (f RelativeJSONPointerFormatChecker) IsFormat(input any) bool {
|
||||
asString, ok := input.(string)
|
||||
if !ok {
|
||||
return true
|
||||
|
||||
+1
-1
@@ -32,6 +32,6 @@ import (
|
||||
|
||||
const internalLogEnabled = false
|
||||
|
||||
func internalLog(format string, v ...interface{}) {
|
||||
func internalLog(format string, v ...any) {
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
|
||||
+24
-24
@@ -77,8 +77,8 @@ var osFS = osFileSystem(os.Open)
|
||||
|
||||
// JSONLoader defines the JSON loader interface
|
||||
type JSONLoader interface {
|
||||
JSONSource() interface{}
|
||||
LoadJSON() (interface{}, error)
|
||||
JSONSource() any
|
||||
LoadJSON() (any, error)
|
||||
JSONReference() (gojsonreference.JsonReference, error)
|
||||
LoaderFactory() JSONLoaderFactory
|
||||
}
|
||||
@@ -130,7 +130,7 @@ type jsonReferenceLoader struct {
|
||||
source string
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) JSONSource() interface{} {
|
||||
func (l *jsonReferenceLoader) JSONSource() any {
|
||||
return l.source
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonReferenceLoader) LoadJSON() (any, error) {
|
||||
|
||||
var err error
|
||||
|
||||
@@ -207,7 +207,7 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
||||
return nil, fmt.Errorf("remote reference loading disabled: %s", reference.String())
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
|
||||
func (l *jsonReferenceLoader) loadFromHTTP(address string) (any, error) {
|
||||
|
||||
resp, err := http.Get(address)
|
||||
if err != nil {
|
||||
@@ -227,7 +227,7 @@ func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error)
|
||||
return decodeJSONUsingNumber(bytes.NewReader(bodyBuff))
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
|
||||
func (l *jsonReferenceLoader) loadFromFile(path string) (any, error) {
|
||||
f, err := l.fs.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -249,7 +249,7 @@ type jsonStringLoader struct {
|
||||
source string
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) JSONSource() interface{} {
|
||||
func (l *jsonStringLoader) JSONSource() any {
|
||||
return l.source
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ func NewStringLoader(source string) JSONLoader {
|
||||
return &jsonStringLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonStringLoader) LoadJSON() (any, error) {
|
||||
|
||||
return decodeJSONUsingNumber(strings.NewReader(l.JSONSource().(string)))
|
||||
|
||||
@@ -278,7 +278,7 @@ type jsonBytesLoader struct {
|
||||
source []byte
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) JSONSource() interface{} {
|
||||
func (l *jsonBytesLoader) JSONSource() any {
|
||||
return l.source
|
||||
}
|
||||
|
||||
@@ -295,18 +295,18 @@ func NewBytesLoader(source []byte) JSONLoader {
|
||||
return &jsonBytesLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonBytesLoader) LoadJSON() (any, error) {
|
||||
return decodeJSONUsingNumber(bytes.NewReader(l.JSONSource().([]byte)))
|
||||
}
|
||||
|
||||
// JSON Go (types) loader
|
||||
// used to load JSONs from the code as maps, interface{}, structs ...
|
||||
// used to load JSONs from the code as maps, any, structs ...
|
||||
|
||||
type jsonGoLoader struct {
|
||||
source interface{}
|
||||
source any
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) JSONSource() interface{} {
|
||||
func (l *jsonGoLoader) JSONSource() any {
|
||||
return l.source
|
||||
}
|
||||
|
||||
@@ -319,11 +319,11 @@ func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory {
|
||||
}
|
||||
|
||||
// NewGoLoader creates a new JSONLoader from a given Go struct
|
||||
func NewGoLoader(source interface{}) JSONLoader {
|
||||
func NewGoLoader(source any) JSONLoader {
|
||||
return &jsonGoLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonGoLoader) LoadJSON() (any, error) {
|
||||
|
||||
// convert it to a compliant JSON first to avoid types "mismatches"
|
||||
|
||||
@@ -352,11 +352,11 @@ func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) {
|
||||
return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) JSONSource() interface{} {
|
||||
func (l *jsonIOLoader) JSONSource() any {
|
||||
return l.buf.String()
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonIOLoader) LoadJSON() (any, error) {
|
||||
return decodeJSONUsingNumber(l.buf)
|
||||
}
|
||||
|
||||
@@ -369,21 +369,21 @@ func (l *jsonIOLoader) LoaderFactory() JSONLoaderFactory {
|
||||
}
|
||||
|
||||
// JSON raw loader
|
||||
// In case the JSON is already marshalled to interface{} use this loader
|
||||
// In case the JSON is already marshalled to any use this loader
|
||||
// This is used for testing as otherwise there is no guarantee the JSON is marshalled
|
||||
// "properly" by using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
|
||||
type jsonRawLoader struct {
|
||||
source interface{}
|
||||
source any
|
||||
}
|
||||
|
||||
// NewRawLoader creates a new JSON raw loader for the given source
|
||||
func NewRawLoader(source interface{}) JSONLoader {
|
||||
func NewRawLoader(source any) JSONLoader {
|
||||
return &jsonRawLoader{source: source}
|
||||
}
|
||||
func (l *jsonRawLoader) JSONSource() interface{} {
|
||||
func (l *jsonRawLoader) JSONSource() any {
|
||||
return l.source
|
||||
}
|
||||
func (l *jsonRawLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonRawLoader) LoadJSON() (any, error) {
|
||||
return l.source, nil
|
||||
}
|
||||
func (l *jsonRawLoader) JSONReference() (gojsonreference.JsonReference, error) {
|
||||
@@ -393,9 +393,9 @@ func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func decodeJSONUsingNumber(r io.Reader) (interface{}, error) {
|
||||
func decodeJSONUsingNumber(r io.Reader) (any, error) {
|
||||
|
||||
var document interface{}
|
||||
var document any
|
||||
|
||||
decoder := json.NewDecoder(r)
|
||||
decoder.UseNumber()
|
||||
|
||||
+7
-7
@@ -33,7 +33,7 @@ import (
|
||||
type (
|
||||
// ErrorDetails is a map of details specific to each error.
|
||||
// While the values will vary, every error will contain a "field" value
|
||||
ErrorDetails map[string]interface{}
|
||||
ErrorDetails map[string]any
|
||||
|
||||
// ResultError is the interface that library errors must implement
|
||||
ResultError interface {
|
||||
@@ -57,9 +57,9 @@ type (
|
||||
// DescriptionFormat returns the format for the description in the default text/template format
|
||||
DescriptionFormat() string
|
||||
// SetValue sets the value related to the error
|
||||
SetValue(interface{})
|
||||
SetValue(any)
|
||||
// Value returns the value related to the error
|
||||
Value() interface{}
|
||||
Value() any
|
||||
// SetDetails sets the details specific to the error
|
||||
SetDetails(ErrorDetails)
|
||||
// Details returns details about the error
|
||||
@@ -76,7 +76,7 @@ type (
|
||||
context *JSONContext // Tree like notation of the part that failed the validation. ex (root).a.b ...
|
||||
description string // A human readable error message
|
||||
descriptionFormat string // A format for human readable error message
|
||||
value interface{} // Value given by the JSON file that is the source of the error
|
||||
value any // Value given by the JSON file that is the source of the error
|
||||
details ErrorDetails
|
||||
}
|
||||
|
||||
@@ -136,12 +136,12 @@ func (v *ResultErrorFields) DescriptionFormat() string {
|
||||
}
|
||||
|
||||
// SetValue sets the value related to the error
|
||||
func (v *ResultErrorFields) SetValue(value interface{}) {
|
||||
func (v *ResultErrorFields) SetValue(value any) {
|
||||
v.value = value
|
||||
}
|
||||
|
||||
// Value returns the value related to the error
|
||||
func (v *ResultErrorFields) Value() interface{} {
|
||||
func (v *ResultErrorFields) Value() any {
|
||||
return v.value
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ func (v *Result) AddError(err ResultError, details ErrorDetails) {
|
||||
v.errors = append(v.errors, err)
|
||||
}
|
||||
|
||||
func (v *Result) addInternalError(err ResultError, context *JSONContext, value interface{}, details ErrorDetails) {
|
||||
func (v *Result) addInternalError(err ResultError, context *JSONContext, value any, details ErrorDetails) {
|
||||
newError(err, context, value, Locale, details)
|
||||
v.errors = append(v.errors, err)
|
||||
v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function
|
||||
|
||||
+31
-31
@@ -58,7 +58,7 @@ type Schema struct {
|
||||
ReferencePool *schemaReferencePool
|
||||
}
|
||||
|
||||
func (d *Schema) parse(document interface{}, draft Draft) error {
|
||||
func (d *Schema) parse(document any, draft Draft) error {
|
||||
d.RootSchema = &SubSchema{Property: StringRootSchemaProperty, Draft: &draft}
|
||||
return d.parseSchema(document, d.RootSchema)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func (d *Schema) SetRootSchemaName(name string) {
|
||||
// Pretty long function ( sorry :) )... but pretty straight forward, repetitive and boring
|
||||
// Not much magic involved here, most of the job is to validate the key names and their values,
|
||||
// then the values are copied into SubSchema struct
|
||||
func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema) error {
|
||||
func (d *Schema) parseSchema(documentNode any, currentSchema *SubSchema) error {
|
||||
|
||||
if currentSchema.Draft == nil {
|
||||
if currentSchema.Parent == nil {
|
||||
@@ -90,7 +90,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
}
|
||||
}
|
||||
|
||||
m, isMap := documentNode.(map[string]interface{})
|
||||
m, isMap := documentNode.(map[string]any)
|
||||
if !isMap {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.ParseError(),
|
||||
@@ -146,10 +146,10 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
// definitions
|
||||
if v, ok := m[KeyDefinitions]; ok {
|
||||
switch mt := v.(type) {
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
for _, dv := range mt {
|
||||
switch dv.(type) {
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyDefinitions, Parent: currentSchema}
|
||||
err := d.parseSchema(dv, newSchema)
|
||||
if err != nil {
|
||||
@@ -203,7 +203,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case []interface{}:
|
||||
case []any:
|
||||
for _, typeInArray := range t {
|
||||
s, isString := typeInArray.(string)
|
||||
if !isString {
|
||||
@@ -231,7 +231,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
switch v := additionalProperties.(type) {
|
||||
case bool:
|
||||
currentSchema.additionalProperties = v
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyAdditionalProperties, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
currentSchema.additionalProperties = newSchema
|
||||
err := d.parseSchema(v, newSchema)
|
||||
@@ -270,7 +270,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
// propertyNames
|
||||
if propertyNames, found := m[KeyPropertyNames]; found && *currentSchema.Draft >= Draft6 {
|
||||
switch propertyNames.(type) {
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyPropertyNames, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
currentSchema.propertyNames = newSchema
|
||||
err := d.parseSchema(propertyNames, newSchema)
|
||||
@@ -299,10 +299,10 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
// items
|
||||
if items, found := m[KeyItems]; found {
|
||||
switch i := items.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
for _, itemElement := range i {
|
||||
switch itemElement.(type) {
|
||||
case map[string]interface{}, bool:
|
||||
case map[string]any, bool:
|
||||
newSchema := &SubSchema{Parent: currentSchema, Property: KeyItems}
|
||||
newSchema.Ref = currentSchema.Ref
|
||||
currentSchema.ItemsChildren = append(currentSchema.ItemsChildren, newSchema)
|
||||
@@ -315,7 +315,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
}
|
||||
currentSchema.ItemsChildrenIsSingleSchema = false
|
||||
}
|
||||
case map[string]interface{}, bool:
|
||||
case map[string]any, bool:
|
||||
newSchema := &SubSchema{Parent: currentSchema, Property: KeyItems}
|
||||
newSchema.Ref = currentSchema.Ref
|
||||
currentSchema.ItemsChildren = append(currentSchema.ItemsChildren, newSchema)
|
||||
@@ -334,7 +334,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
switch i := additionalItems.(type) {
|
||||
case bool:
|
||||
currentSchema.additionalItems = i
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyAdditionalItems, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
currentSchema.additionalItems = newSchema
|
||||
err := d.parseSchema(additionalItems, newSchema)
|
||||
@@ -717,7 +717,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
|
||||
if vNot, found := m[KeyNot]; found {
|
||||
switch vNot.(type) {
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyNot, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
currentSchema.not = newSchema
|
||||
err := d.parseSchema(vNot, newSchema)
|
||||
@@ -735,7 +735,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
if *currentSchema.Draft >= Draft7 {
|
||||
if vIf, found := m[KeyIf]; found {
|
||||
switch vIf.(type) {
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyIf, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
currentSchema._if = newSchema
|
||||
err := d.parseSchema(vIf, newSchema)
|
||||
@@ -752,7 +752,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
|
||||
if then, found := m[KeyThen]; found {
|
||||
switch then.(type) {
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyThen, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
currentSchema._then = newSchema
|
||||
err := d.parseSchema(then, newSchema)
|
||||
@@ -769,7 +769,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
|
||||
if vElse, found := m[KeyElse]; found {
|
||||
switch vElse.(type) {
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
newSchema := &SubSchema{Property: KeyElse, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
currentSchema._else = newSchema
|
||||
err := d.parseSchema(vElse, newSchema)
|
||||
@@ -788,9 +788,9 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Schema) parseReference(_ interface{}, currentSchema *SubSchema) error {
|
||||
func (d *Schema) parseReference(_ any, currentSchema *SubSchema) error {
|
||||
var (
|
||||
refdDocumentNode interface{}
|
||||
refdDocumentNode any
|
||||
dsp *schemaPoolDocument
|
||||
err error
|
||||
)
|
||||
@@ -809,7 +809,7 @@ func (d *Schema) parseReference(_ interface{}, currentSchema *SubSchema) error {
|
||||
newSchema.Draft = dsp.Draft
|
||||
|
||||
switch refdDocumentNode.(type) {
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
// expected
|
||||
default:
|
||||
return errors.New(formatErrorDescription(
|
||||
@@ -829,8 +829,8 @@ func (d *Schema) parseReference(_ interface{}, currentSchema *SubSchema) error {
|
||||
|
||||
}
|
||||
|
||||
func (d *Schema) parseProperties(documentNode interface{}, currentSchema *SubSchema) error {
|
||||
m, isMap := documentNode.(map[string]interface{})
|
||||
func (d *Schema) parseProperties(documentNode any, currentSchema *SubSchema) error {
|
||||
m, isMap := documentNode.(map[string]any)
|
||||
if !isMap {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfType(),
|
||||
@@ -851,19 +851,19 @@ func (d *Schema) parseProperties(documentNode interface{}, currentSchema *SubSch
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *SubSchema) error {
|
||||
m, isMap := documentNode.(map[string]interface{})
|
||||
func (d *Schema) parseDependencies(documentNode any, currentSchema *SubSchema) error {
|
||||
m, isMap := documentNode.(map[string]any)
|
||||
if !isMap {
|
||||
return errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfType(),
|
||||
ErrorDetails{"key": KeyDependencies, "type": TypeObject},
|
||||
))
|
||||
}
|
||||
currentSchema.dependencies = make(map[string]interface{})
|
||||
currentSchema.dependencies = make(map[string]any)
|
||||
|
||||
for k := range m {
|
||||
switch values := m[k].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var valuesToRegister []string
|
||||
for _, value := range values {
|
||||
str, isString := value.(string)
|
||||
@@ -880,7 +880,7 @@ func (d *Schema) parseDependencies(documentNode interface{}, currentSchema *SubS
|
||||
currentSchema.dependencies[k] = valuesToRegister
|
||||
}
|
||||
|
||||
case bool, map[string]interface{}:
|
||||
case bool, map[string]any:
|
||||
depSchema := &SubSchema{Property: k, Parent: currentSchema, Ref: currentSchema.Ref}
|
||||
err := d.parseSchema(m[k], depSchema)
|
||||
if err != nil {
|
||||
@@ -913,7 +913,7 @@ func invalidType(expected, given string) error {
|
||||
))
|
||||
}
|
||||
|
||||
func getString(m map[string]interface{}, key string) (*string, error) {
|
||||
func getString(m map[string]any, key string) (*string, error) {
|
||||
v, found := m[key]
|
||||
if !found {
|
||||
// not found
|
||||
@@ -927,13 +927,13 @@ func getString(m map[string]interface{}, key string) (*string, error) {
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func getMap(m map[string]interface{}, key string) (map[string]interface{}, error) {
|
||||
func getMap(m map[string]any, key string) (map[string]any, error) {
|
||||
v, found := m[key]
|
||||
if !found {
|
||||
// not found
|
||||
return nil, nil
|
||||
}
|
||||
s, isMap := v.(map[string]interface{})
|
||||
s, isMap := v.(map[string]any)
|
||||
if !isMap {
|
||||
// wrong type
|
||||
return nil, invalidType(StringSchema, key)
|
||||
@@ -941,12 +941,12 @@ func getMap(m map[string]interface{}, key string) (map[string]interface{}, error
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func getSlice(m map[string]interface{}, key string) ([]interface{}, error) {
|
||||
func getSlice(m map[string]any, key string) ([]any, error) {
|
||||
v, found := m[key]
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
s, isArray := v.([]interface{})
|
||||
s, isArray := v.([]any)
|
||||
if !isArray {
|
||||
return nil, errors.New(formatErrorDescription(
|
||||
Locale.MustBeOfAn(),
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ func NewSchemaLoader() *SchemaLoader {
|
||||
return ps
|
||||
}
|
||||
|
||||
func (sl *SchemaLoader) validateMetaschema(documentNode interface{}) error {
|
||||
func (sl *SchemaLoader) validateMetaschema(documentNode any) error {
|
||||
|
||||
var (
|
||||
schema string
|
||||
@@ -158,7 +158,7 @@ func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) {
|
||||
d.DocumentReference = ref
|
||||
d.ReferencePool = newSchemaReferencePool()
|
||||
|
||||
var doc interface{}
|
||||
var doc any
|
||||
if ref.String() != "" {
|
||||
// Get document from schema pool
|
||||
spd, err := d.Pool.GetDocument(d.DocumentReference)
|
||||
|
||||
+6
-6
@@ -34,7 +34,7 @@ import (
|
||||
)
|
||||
|
||||
type schemaPoolDocument struct {
|
||||
Document interface{}
|
||||
Document any
|
||||
Draft *Draft
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ type schemaPool struct {
|
||||
autoDetect *bool
|
||||
}
|
||||
|
||||
func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.JsonReference, pooled bool) error {
|
||||
func (p *schemaPool) parseReferences(document any, ref gojsonreference.JsonReference, pooled bool) error {
|
||||
|
||||
var (
|
||||
draft *Draft
|
||||
@@ -72,7 +72,7 @@ func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.J
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonreference.JsonReference, draft *Draft) error {
|
||||
func (p *schemaPool) parseReferencesRecursive(document any, ref gojsonreference.JsonReference, draft *Draft) error {
|
||||
// parseReferencesRecursive parses a JSON document and resolves all $id and $ref references.
|
||||
// For $ref references it takes into account the $id scope it is in and replaces
|
||||
// the reference by the absolute resolved reference
|
||||
@@ -80,14 +80,14 @@ func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonre
|
||||
// When encountering errors it fails silently. Error handling is done when the schema
|
||||
// is syntactically parsed and any error encountered here should also come up there.
|
||||
switch m := document.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
for _, v := range m {
|
||||
err := p.parseReferencesRecursive(v, ref, draft)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
localRef := &ref
|
||||
|
||||
keyID := KeyIDNew
|
||||
@@ -129,7 +129,7 @@ func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonre
|
||||
// Something like a property or a dependency is not a valid schema, as it might describe properties named "$ref", "$id" or "const", etc
|
||||
// Therefore don't treat it like a schema.
|
||||
if k == KeyProperties || k == KeyDependencies || k == KeyPatternProperties {
|
||||
if child, ok := v.(map[string]interface{}); ok {
|
||||
if child, ok := v.(map[string]any); ok {
|
||||
for _, v := range child {
|
||||
err := p.parseReferencesRecursive(v, *localRef, draft)
|
||||
if err != nil {
|
||||
|
||||
+2
-7
@@ -28,6 +28,7 @@ package gojsonschema
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -58,13 +59,7 @@ func (t *jsonSchemaType) Add(etype string) error {
|
||||
|
||||
func (t *jsonSchemaType) Contains(etype string) bool {
|
||||
|
||||
for _, v := range t.types {
|
||||
if v == etype {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(t.types, etype)
|
||||
}
|
||||
|
||||
func (t *jsonSchemaType) String() string {
|
||||
|
||||
+3
-3
@@ -123,8 +123,8 @@ type SubSchema struct {
|
||||
maxProperties *int
|
||||
required []string
|
||||
|
||||
dependencies map[string]interface{}
|
||||
additionalProperties interface{}
|
||||
dependencies map[string]any
|
||||
additionalProperties any
|
||||
patternProperties map[string]*SubSchema
|
||||
propertyNames *SubSchema
|
||||
|
||||
@@ -134,7 +134,7 @@ type SubSchema struct {
|
||||
uniqueItems bool
|
||||
contains *SubSchema
|
||||
|
||||
additionalItems interface{}
|
||||
additionalItems any
|
||||
|
||||
// validation : all
|
||||
_const *string //const is a golang keyword
|
||||
|
||||
+14
-18
@@ -29,18 +29,14 @@ package gojsonschema
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func isStringInSlice(s []string, what string) bool {
|
||||
for i := range s {
|
||||
if s[i] == what {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(s, what)
|
||||
}
|
||||
|
||||
func marshalToJSONString(value interface{}) (*string, error) {
|
||||
func marshalToJSONString(value any) (*string, error) {
|
||||
|
||||
mBytes, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
@@ -51,7 +47,7 @@ func marshalToJSONString(value interface{}) (*string, error) {
|
||||
return &sBytes, nil
|
||||
}
|
||||
|
||||
func marshalWithoutNumber(value interface{}) (*string, error) {
|
||||
func marshalWithoutNumber(value any) (*string, error) {
|
||||
|
||||
// The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
|
||||
// This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1
|
||||
@@ -63,7 +59,7 @@ func marshalWithoutNumber(value interface{}) (*string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var document interface{}
|
||||
var document any
|
||||
|
||||
err = json.Unmarshal([]byte(*jsonString), &document)
|
||||
if err != nil {
|
||||
@@ -73,7 +69,7 @@ func marshalWithoutNumber(value interface{}) (*string, error) {
|
||||
return marshalToJSONString(document)
|
||||
}
|
||||
|
||||
func isJSONNumber(what interface{}) bool {
|
||||
func isJSONNumber(what any) bool {
|
||||
|
||||
switch what.(type) {
|
||||
|
||||
@@ -84,7 +80,7 @@ func isJSONNumber(what interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func checkJSONInteger(what interface{}) (isInt bool) {
|
||||
func checkJSONInteger(what any) (isInt bool) {
|
||||
|
||||
jsonNumber := what.(json.Number)
|
||||
|
||||
@@ -100,7 +96,7 @@ const (
|
||||
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
||||
)
|
||||
|
||||
func mustBeInteger(what interface{}) *int {
|
||||
func mustBeInteger(what any) *int {
|
||||
number, ok := what.(json.Number)
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -123,7 +119,7 @@ func mustBeInteger(what interface{}) *int {
|
||||
return &int32Value
|
||||
}
|
||||
|
||||
func mustBeNumber(what interface{}) *big.Rat {
|
||||
func mustBeNumber(what any) *big.Rat {
|
||||
number, ok := what.(json.Number)
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -136,11 +132,11 @@ func mustBeNumber(what interface{}) *big.Rat {
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertDocumentNode(val interface{}) interface{} {
|
||||
func convertDocumentNode(val any) any {
|
||||
|
||||
if lval, ok := val.([]interface{}); ok {
|
||||
if lval, ok := val.([]any); ok {
|
||||
|
||||
res := []interface{}{}
|
||||
res := []any{}
|
||||
for _, v := range lval {
|
||||
res = append(res, convertDocumentNode(v))
|
||||
}
|
||||
@@ -149,9 +145,9 @@ func convertDocumentNode(val interface{}) interface{} {
|
||||
|
||||
}
|
||||
|
||||
if mval, ok := val.(map[interface{}]interface{}); ok {
|
||||
if mval, ok := val.(map[any]any); ok {
|
||||
|
||||
res := map[string]interface{}{}
|
||||
res := map[string]any{}
|
||||
|
||||
for k, v := range mval {
|
||||
res[k.(string)] = convertDocumentNode(v)
|
||||
|
||||
+15
-15
@@ -54,21 +54,21 @@ func (v *Schema) Validate(l JSONLoader) (*Result, error) {
|
||||
return v.validateDocument(root), nil
|
||||
}
|
||||
|
||||
func (v *Schema) validateDocument(root interface{}) *Result {
|
||||
func (v *Schema) validateDocument(root any) *Result {
|
||||
result := &Result{}
|
||||
context := NewJSONContext(StringContextRoot, nil)
|
||||
v.RootSchema.validateRecursive(v.RootSchema, root, result, context)
|
||||
return result
|
||||
}
|
||||
|
||||
func (v *SubSchema) subValidateWithContext(document interface{}, context *JSONContext) *Result {
|
||||
func (v *SubSchema) subValidateWithContext(document any, context *JSONContext) *Result {
|
||||
result := &Result{}
|
||||
v.validateRecursive(v, document, result, context)
|
||||
return result
|
||||
}
|
||||
|
||||
// Walker function to validate the json recursively against the SubSchema
|
||||
func (v *SubSchema) validateRecursive(currentSubSchema *SubSchema, currentNode interface{}, result *Result, context *JSONContext) {
|
||||
func (v *SubSchema) validateRecursive(currentSubSchema *SubSchema, currentNode any, result *Result, context *JSONContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateRecursive %s", context.String())
|
||||
@@ -167,7 +167,7 @@ func (v *SubSchema) validateRecursive(currentSubSchema *SubSchema, currentNode i
|
||||
return
|
||||
}
|
||||
|
||||
castCurrentNode := currentNode.([]interface{})
|
||||
castCurrentNode := currentNode.([]any)
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
|
||||
|
||||
@@ -190,9 +190,9 @@ func (v *SubSchema) validateRecursive(currentSubSchema *SubSchema, currentNode i
|
||||
return
|
||||
}
|
||||
|
||||
castCurrentNode, ok := currentNode.(map[string]interface{})
|
||||
castCurrentNode, ok := currentNode.(map[string]any)
|
||||
if !ok {
|
||||
castCurrentNode = convertDocumentNode(currentNode).(map[string]interface{})
|
||||
castCurrentNode = convertDocumentNode(currentNode).(map[string]any)
|
||||
}
|
||||
|
||||
currentSubSchema.validateSchema(currentSubSchema, castCurrentNode, result, context)
|
||||
@@ -264,7 +264,7 @@ func (v *SubSchema) validateRecursive(currentSubSchema *SubSchema, currentNode i
|
||||
}
|
||||
|
||||
// Different kinds of validation there, SubSchema / common / array / object / string...
|
||||
func (v *SubSchema) validateSchema(currentSubSchema *SubSchema, currentNode interface{}, result *Result, context *JSONContext) {
|
||||
func (v *SubSchema) validateSchema(currentSubSchema *SubSchema, currentNode any, result *Result, context *JSONContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateSchema %s", context.String())
|
||||
@@ -349,14 +349,14 @@ func (v *SubSchema) validateSchema(currentSubSchema *SubSchema, currentNode inte
|
||||
}
|
||||
|
||||
if len(currentSubSchema.dependencies) > 0 {
|
||||
if currentNodeMap, ok := currentNode.(map[string]interface{}); ok {
|
||||
if currentNodeMap, ok := currentNode.(map[string]any); ok {
|
||||
for elementKey := range currentNodeMap {
|
||||
if dependency, ok := currentSubSchema.dependencies[elementKey]; ok {
|
||||
switch dependency := dependency.(type) {
|
||||
|
||||
case []string:
|
||||
for _, dependOnKey := range dependency {
|
||||
if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved {
|
||||
if _, dependencyResolved := currentNode.(map[string]any)[dependOnKey]; !dependencyResolved {
|
||||
result.addInternalError(
|
||||
new(MissingDependencyError),
|
||||
context,
|
||||
@@ -395,7 +395,7 @@ func (v *SubSchema) validateSchema(currentSubSchema *SubSchema, currentNode inte
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *SubSchema) validateCommon(currentSubSchema *SubSchema, value interface{}, result *Result, context *JSONContext) {
|
||||
func (v *SubSchema) validateCommon(currentSubSchema *SubSchema, value any, result *Result, context *JSONContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateCommon %s", context.String())
|
||||
@@ -452,7 +452,7 @@ func (v *SubSchema) validateCommon(currentSubSchema *SubSchema, value interface{
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *SubSchema) validateArray(currentSubSchema *SubSchema, value []interface{}, result *Result, context *JSONContext) {
|
||||
func (v *SubSchema) validateArray(currentSubSchema *SubSchema, value []any, result *Result, context *JSONContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateArray %s", context.String())
|
||||
@@ -578,7 +578,7 @@ func (v *SubSchema) validateArray(currentSubSchema *SubSchema, value []interface
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *SubSchema) validateObject(currentSubSchema *SubSchema, value map[string]interface{}, result *Result, context *JSONContext) {
|
||||
func (v *SubSchema) validateObject(currentSubSchema *SubSchema, value map[string]any, result *Result, context *JSONContext) {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validateObject %s", context.String())
|
||||
@@ -675,7 +675,7 @@ func (v *SubSchema) validateObject(currentSubSchema *SubSchema, value map[string
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *SubSchema) validatePatternProperty(currentSubSchema *SubSchema, key string, value interface{}, result *Result, context *JSONContext) bool {
|
||||
func (v *SubSchema) validatePatternProperty(currentSubSchema *SubSchema, key string, value any, result *Result, context *JSONContext) bool {
|
||||
|
||||
if internalLogEnabled {
|
||||
internalLog("validatePatternProperty %s", context.String())
|
||||
@@ -701,7 +701,7 @@ func (v *SubSchema) validatePatternProperty(currentSubSchema *SubSchema, key str
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *SubSchema) validateString(currentSubSchema *SubSchema, value interface{}, result *Result, context *JSONContext) {
|
||||
func (v *SubSchema) validateString(currentSubSchema *SubSchema, value any, result *Result, context *JSONContext) {
|
||||
|
||||
// Ignore JSON numbers
|
||||
stringValue, isString := value.(string)
|
||||
@@ -752,7 +752,7 @@ func (v *SubSchema) validateString(currentSubSchema *SubSchema, value interface{
|
||||
result.incrementScore()
|
||||
}
|
||||
|
||||
func (v *SubSchema) validateNumber(currentSubSchema *SubSchema, value interface{}, result *Result, context *JSONContext) {
|
||||
func (v *SubSchema) validateNumber(currentSubSchema *SubSchema, value any, result *Result, context *JSONContext) {
|
||||
|
||||
// Ignore non numbers
|
||||
number, isNumber := value.(json.Number)
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2018 Adam Scarr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package ast
|
||||
|
||||
func arg2map(defs ArgumentDefinitionList, args ArgumentList, vars map[string]interface{}) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
var err error
|
||||
|
||||
for _, argDef := range defs {
|
||||
var val interface{}
|
||||
var hasValue bool
|
||||
|
||||
if argValue := args.ForName(argDef.Name); argValue != nil {
|
||||
if argValue.Value.Kind == Variable {
|
||||
val, hasValue = vars[argValue.Value.Raw]
|
||||
} else {
|
||||
val, err = argValue.Value.Value(vars)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
hasValue = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasValue && argDef.DefaultValue != nil {
|
||||
val, err = argDef.DefaultValue.Value(vars)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
hasValue = true
|
||||
}
|
||||
|
||||
if hasValue {
|
||||
result[argDef.Name] = val
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
package ast
|
||||
|
||||
type FieldList []*FieldDefinition
|
||||
|
||||
func (l FieldList) ForName(name string) *FieldDefinition {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnumValueList []*EnumValueDefinition
|
||||
|
||||
func (l EnumValueList) ForName(name string) *EnumValueDefinition {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DirectiveList []*Directive
|
||||
|
||||
func (l DirectiveList) ForName(name string) *Directive {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l DirectiveList) ForNames(name string) []*Directive {
|
||||
resp := []*Directive{}
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
resp = append(resp, it)
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
type OperationList []*OperationDefinition
|
||||
|
||||
func (l OperationList) ForName(name string) *OperationDefinition {
|
||||
if name == "" && len(l) == 1 {
|
||||
return l[0]
|
||||
}
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FragmentDefinitionList []*FragmentDefinition
|
||||
|
||||
func (l FragmentDefinitionList) ForName(name string) *FragmentDefinition {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type VariableDefinitionList []*VariableDefinition
|
||||
|
||||
func (l VariableDefinitionList) ForName(name string) *VariableDefinition {
|
||||
for _, it := range l {
|
||||
if it.Variable == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ArgumentList []*Argument
|
||||
|
||||
func (l ArgumentList) ForName(name string) *Argument {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ArgumentDefinitionList []*ArgumentDefinition
|
||||
|
||||
func (l ArgumentDefinitionList) ForName(name string) *ArgumentDefinition {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SchemaDefinitionList []*SchemaDefinition
|
||||
|
||||
type DirectiveDefinitionList []*DirectiveDefinition
|
||||
|
||||
func (l DirectiveDefinitionList) ForName(name string) *DirectiveDefinition {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DefinitionList []*Definition
|
||||
|
||||
func (l DefinitionList) ForName(name string) *Definition {
|
||||
for _, it := range l {
|
||||
if it.Name == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type OperationTypeDefinitionList []*OperationTypeDefinition
|
||||
|
||||
func (l OperationTypeDefinitionList) ForType(name string) *OperationTypeDefinition {
|
||||
for _, it := range l {
|
||||
if it.Type == name {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ChildValueList []*ChildValue
|
||||
|
||||
func (v ChildValueList) ForName(name string) *Value {
|
||||
for _, f := range v {
|
||||
if f.Name == name {
|
||||
return f.Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func UnmarshalSelectionSet(b []byte) (SelectionSet, error) {
|
||||
var tmp []json.RawMessage
|
||||
|
||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result = make([]Selection, 0)
|
||||
for _, item := range tmp {
|
||||
var field Field
|
||||
if err := json.Unmarshal(item, &field); err == nil {
|
||||
result = append(result, &field)
|
||||
continue
|
||||
}
|
||||
var fragmentSpread FragmentSpread
|
||||
if err := json.Unmarshal(item, &fragmentSpread); err == nil {
|
||||
result = append(result, &fragmentSpread)
|
||||
continue
|
||||
}
|
||||
var inlineFragment InlineFragment
|
||||
if err := json.Unmarshal(item, &inlineFragment); err == nil {
|
||||
result = append(result, &inlineFragment)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *FragmentDefinition) UnmarshalJSON(b []byte) error {
|
||||
var tmp map[string]json.RawMessage
|
||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
for k := range tmp {
|
||||
switch k {
|
||||
case "Name":
|
||||
err := json.Unmarshal(tmp[k], &f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "VariableDefinition":
|
||||
err := json.Unmarshal(tmp[k], &f.VariableDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "TypeCondition":
|
||||
err := json.Unmarshal(tmp[k], &f.TypeCondition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Directives":
|
||||
err := json.Unmarshal(tmp[k], &f.Directives)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "SelectionSet":
|
||||
ss, err := UnmarshalSelectionSet(tmp[k])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SelectionSet = ss
|
||||
case "Definition":
|
||||
err := json.Unmarshal(tmp[k], &f.Definition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Position":
|
||||
err := json.Unmarshal(tmp[k], &f.Position)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *InlineFragment) UnmarshalJSON(b []byte) error {
|
||||
var tmp map[string]json.RawMessage
|
||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
for k := range tmp {
|
||||
switch k {
|
||||
case "TypeCondition":
|
||||
err := json.Unmarshal(tmp[k], &f.TypeCondition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Directives":
|
||||
err := json.Unmarshal(tmp[k], &f.Directives)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "SelectionSet":
|
||||
ss, err := UnmarshalSelectionSet(tmp[k])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SelectionSet = ss
|
||||
case "ObjectDefinition":
|
||||
err := json.Unmarshal(tmp[k], &f.ObjectDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Position":
|
||||
err := json.Unmarshal(tmp[k], &f.Position)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *OperationDefinition) UnmarshalJSON(b []byte) error {
|
||||
var tmp map[string]json.RawMessage
|
||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
for k := range tmp {
|
||||
switch k {
|
||||
case "Operation":
|
||||
err := json.Unmarshal(tmp[k], &f.Operation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Name":
|
||||
err := json.Unmarshal(tmp[k], &f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "VariableDefinitions":
|
||||
err := json.Unmarshal(tmp[k], &f.VariableDefinitions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Directives":
|
||||
err := json.Unmarshal(tmp[k], &f.Directives)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "SelectionSet":
|
||||
ss, err := UnmarshalSelectionSet(tmp[k])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SelectionSet = ss
|
||||
case "Position":
|
||||
err := json.Unmarshal(tmp[k], &f.Position)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Field) UnmarshalJSON(b []byte) error {
|
||||
var tmp map[string]json.RawMessage
|
||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
for k := range tmp {
|
||||
switch k {
|
||||
case "Alias":
|
||||
err := json.Unmarshal(tmp[k], &f.Alias)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Name":
|
||||
err := json.Unmarshal(tmp[k], &f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Arguments":
|
||||
err := json.Unmarshal(tmp[k], &f.Arguments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Directives":
|
||||
err := json.Unmarshal(tmp[k], &f.Directives)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "SelectionSet":
|
||||
ss, err := UnmarshalSelectionSet(tmp[k])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SelectionSet = ss
|
||||
case "Position":
|
||||
err := json.Unmarshal(tmp[k], &f.Position)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "Definition":
|
||||
err := json.Unmarshal(tmp[k], &f.Definition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "ObjectDefinition":
|
||||
err := json.Unmarshal(tmp[k], &f.ObjectDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
package ast
|
||||
|
||||
type DefinitionKind string
|
||||
|
||||
const (
|
||||
Scalar DefinitionKind = "SCALAR"
|
||||
Object DefinitionKind = "OBJECT"
|
||||
Interface DefinitionKind = "INTERFACE"
|
||||
Union DefinitionKind = "UNION"
|
||||
Enum DefinitionKind = "ENUM"
|
||||
InputObject DefinitionKind = "INPUT_OBJECT"
|
||||
)
|
||||
|
||||
// Definition is the core type definition object, it includes all of the definable types
|
||||
// but does *not* cover schema or directives.
|
||||
//
|
||||
// @vektah: Javascript implementation has different types for all of these, but they are
|
||||
// more similar than different and don't define any behaviour. I think this style of
|
||||
// "some hot" struct works better, at least for go.
|
||||
//
|
||||
// Type extensions are also represented by this same struct.
|
||||
type Definition struct {
|
||||
Kind DefinitionKind
|
||||
Description string
|
||||
Name string
|
||||
Directives DirectiveList
|
||||
Interfaces []string // object and input object
|
||||
Fields FieldList // object and input object
|
||||
Types []string // union
|
||||
EnumValues EnumValueList // enum
|
||||
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
BuiltIn bool `dump:"-"`
|
||||
}
|
||||
|
||||
func (d *Definition) IsLeafType() bool {
|
||||
return d.Kind == Enum || d.Kind == Scalar
|
||||
}
|
||||
|
||||
func (d *Definition) IsAbstractType() bool {
|
||||
return d.Kind == Interface || d.Kind == Union
|
||||
}
|
||||
|
||||
func (d *Definition) IsCompositeType() bool {
|
||||
return d.Kind == Object || d.Kind == Interface || d.Kind == Union
|
||||
}
|
||||
|
||||
func (d *Definition) IsInputType() bool {
|
||||
return d.Kind == Scalar || d.Kind == Enum || d.Kind == InputObject
|
||||
}
|
||||
|
||||
func (d *Definition) OneOf(types ...string) bool {
|
||||
for _, t := range types {
|
||||
if d.Name == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type FieldDefinition struct {
|
||||
Description string
|
||||
Name string
|
||||
Arguments ArgumentDefinitionList // only for objects
|
||||
DefaultValue *Value // only for input objects
|
||||
Type *Type
|
||||
Directives DirectiveList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type ArgumentDefinition struct {
|
||||
Description string
|
||||
Name string
|
||||
DefaultValue *Value
|
||||
Type *Type
|
||||
Directives DirectiveList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type EnumValueDefinition struct {
|
||||
Description string
|
||||
Name string
|
||||
Directives DirectiveList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type DirectiveDefinition struct {
|
||||
Description string
|
||||
Name string
|
||||
Arguments ArgumentDefinitionList
|
||||
Locations []DirectiveLocation
|
||||
IsRepeatable bool
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
package ast
|
||||
|
||||
type DirectiveLocation string
|
||||
|
||||
const (
|
||||
// Executable
|
||||
LocationQuery DirectiveLocation = `QUERY`
|
||||
LocationMutation DirectiveLocation = `MUTATION`
|
||||
LocationSubscription DirectiveLocation = `SUBSCRIPTION`
|
||||
LocationField DirectiveLocation = `FIELD`
|
||||
LocationFragmentDefinition DirectiveLocation = `FRAGMENT_DEFINITION`
|
||||
LocationFragmentSpread DirectiveLocation = `FRAGMENT_SPREAD`
|
||||
LocationInlineFragment DirectiveLocation = `INLINE_FRAGMENT`
|
||||
|
||||
// Type System
|
||||
LocationSchema DirectiveLocation = `SCHEMA`
|
||||
LocationScalar DirectiveLocation = `SCALAR`
|
||||
LocationObject DirectiveLocation = `OBJECT`
|
||||
LocationFieldDefinition DirectiveLocation = `FIELD_DEFINITION`
|
||||
LocationArgumentDefinition DirectiveLocation = `ARGUMENT_DEFINITION`
|
||||
LocationInterface DirectiveLocation = `INTERFACE`
|
||||
LocationUnion DirectiveLocation = `UNION`
|
||||
LocationEnum DirectiveLocation = `ENUM`
|
||||
LocationEnumValue DirectiveLocation = `ENUM_VALUE`
|
||||
LocationInputObject DirectiveLocation = `INPUT_OBJECT`
|
||||
LocationInputFieldDefinition DirectiveLocation = `INPUT_FIELD_DEFINITION`
|
||||
LocationVariableDefinition DirectiveLocation = `VARIABLE_DEFINITION`
|
||||
)
|
||||
|
||||
type Directive struct {
|
||||
Name string
|
||||
Arguments ArgumentList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
|
||||
// Requires validation
|
||||
ParentDefinition *Definition
|
||||
Definition *DirectiveDefinition
|
||||
Location DirectiveLocation
|
||||
}
|
||||
|
||||
func (d *Directive) ArgumentMap(vars map[string]interface{}) map[string]interface{} {
|
||||
return arg2map(d.Definition.Arguments, d.Arguments, vars)
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
package ast
|
||||
|
||||
type QueryDocument struct {
|
||||
Operations OperationList
|
||||
Fragments FragmentDefinitionList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type SchemaDocument struct {
|
||||
Schema SchemaDefinitionList
|
||||
SchemaExtension SchemaDefinitionList
|
||||
Directives DirectiveDefinitionList
|
||||
Definitions DefinitionList
|
||||
Extensions DefinitionList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (d *SchemaDocument) Merge(other *SchemaDocument) {
|
||||
d.Schema = append(d.Schema, other.Schema...)
|
||||
d.SchemaExtension = append(d.SchemaExtension, other.SchemaExtension...)
|
||||
d.Directives = append(d.Directives, other.Directives...)
|
||||
d.Definitions = append(d.Definitions, other.Definitions...)
|
||||
d.Extensions = append(d.Extensions, other.Extensions...)
|
||||
}
|
||||
|
||||
type Schema struct {
|
||||
Query *Definition
|
||||
Mutation *Definition
|
||||
Subscription *Definition
|
||||
|
||||
Types map[string]*Definition
|
||||
Directives map[string]*DirectiveDefinition
|
||||
|
||||
PossibleTypes map[string][]*Definition
|
||||
Implements map[string][]*Definition
|
||||
|
||||
Description string
|
||||
}
|
||||
|
||||
// AddTypes is the helper to add types definition to the schema
|
||||
func (s *Schema) AddTypes(defs ...*Definition) {
|
||||
if s.Types == nil {
|
||||
s.Types = make(map[string]*Definition)
|
||||
}
|
||||
for _, def := range defs {
|
||||
s.Types[def.Name] = def
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Schema) AddPossibleType(name string, def *Definition) {
|
||||
s.PossibleTypes[name] = append(s.PossibleTypes[name], def)
|
||||
}
|
||||
|
||||
// GetPossibleTypes will enumerate all the definitions for a given interface or union
|
||||
func (s *Schema) GetPossibleTypes(def *Definition) []*Definition {
|
||||
return s.PossibleTypes[def.Name]
|
||||
}
|
||||
|
||||
func (s *Schema) AddImplements(name string, iface *Definition) {
|
||||
s.Implements[name] = append(s.Implements[name], iface)
|
||||
}
|
||||
|
||||
// GetImplements returns all the interface and union definitions that the given definition satisfies
|
||||
func (s *Schema) GetImplements(def *Definition) []*Definition {
|
||||
return s.Implements[def.Name]
|
||||
}
|
||||
|
||||
type SchemaDefinition struct {
|
||||
Description string
|
||||
Directives DirectiveList
|
||||
OperationTypes OperationTypeDefinitionList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type OperationTypeDefinition struct {
|
||||
Operation Operation
|
||||
Type string
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Dump turns ast into a stable string format for assertions in tests
|
||||
func Dump(i interface{}) string {
|
||||
v := reflect.ValueOf(i)
|
||||
|
||||
d := dumper{Buffer: &bytes.Buffer{}}
|
||||
d.dump(v)
|
||||
|
||||
return d.String()
|
||||
}
|
||||
|
||||
type dumper struct {
|
||||
*bytes.Buffer
|
||||
indent int
|
||||
}
|
||||
|
||||
type Dumpable interface {
|
||||
Dump() string
|
||||
}
|
||||
|
||||
func (d *dumper) dump(v reflect.Value) {
|
||||
if dumpable, isDumpable := v.Interface().(Dumpable); isDumpable {
|
||||
d.WriteString(dumpable.Dump())
|
||||
return
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
d.WriteString("true")
|
||||
} else {
|
||||
d.WriteString("false")
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
d.WriteString(strconv.FormatInt(v.Int(), 10))
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
d.WriteString(strconv.FormatUint(v.Uint(), 10))
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
d.WriteString(fmt.Sprintf("%.2f", v.Float()))
|
||||
|
||||
case reflect.String:
|
||||
if v.Type().Name() != "string" {
|
||||
d.WriteString(v.Type().Name() + "(" + strconv.Quote(v.String()) + ")")
|
||||
} else {
|
||||
d.WriteString(strconv.Quote(v.String()))
|
||||
}
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
d.dumpArray(v)
|
||||
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
d.dumpPtr(v)
|
||||
|
||||
case reflect.Struct:
|
||||
d.dumpStruct(v)
|
||||
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported kind: %s\n buf: %s", v.Kind().String(), d.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dumper) writeIndent() {
|
||||
d.Buffer.WriteString(strings.Repeat(" ", d.indent))
|
||||
}
|
||||
|
||||
func (d *dumper) nl() {
|
||||
d.Buffer.WriteByte('\n')
|
||||
d.writeIndent()
|
||||
}
|
||||
|
||||
func typeName(t reflect.Type) string {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
return typeName(t.Elem())
|
||||
}
|
||||
return t.Name()
|
||||
}
|
||||
|
||||
func (d *dumper) dumpArray(v reflect.Value) {
|
||||
d.WriteString("[" + typeName(v.Type().Elem()) + "]")
|
||||
|
||||
for i := range v.Len() {
|
||||
d.nl()
|
||||
d.WriteString("- ")
|
||||
d.indent++
|
||||
d.dump(v.Index(i))
|
||||
d.indent--
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dumper) dumpStruct(v reflect.Value) {
|
||||
d.WriteString("<" + v.Type().Name() + ">")
|
||||
d.indent++
|
||||
|
||||
typ := v.Type()
|
||||
for i := range v.NumField() {
|
||||
f := v.Field(i)
|
||||
if typ.Field(i).Tag.Get("dump") == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if isZero(f) {
|
||||
continue
|
||||
}
|
||||
d.nl()
|
||||
d.WriteString(typ.Field(i).Name)
|
||||
d.WriteString(": ")
|
||||
d.dump(v.Field(i))
|
||||
}
|
||||
|
||||
d.indent--
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
return v.IsNil()
|
||||
case reflect.Func, reflect.Map:
|
||||
return v.IsNil()
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return true
|
||||
}
|
||||
z := true
|
||||
for i := range v.Len() {
|
||||
z = z && isZero(v.Index(i))
|
||||
}
|
||||
return z
|
||||
case reflect.Struct:
|
||||
z := true
|
||||
for i := range v.NumField() {
|
||||
z = z && isZero(v.Field(i))
|
||||
}
|
||||
return z
|
||||
case reflect.String:
|
||||
return v.String() == ""
|
||||
}
|
||||
|
||||
// Compare other types directly:
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()))
|
||||
}
|
||||
|
||||
func (d *dumper) dumpPtr(v reflect.Value) {
|
||||
if v.IsNil() {
|
||||
d.WriteString("nil")
|
||||
return
|
||||
}
|
||||
d.dump(v.Elem())
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package ast
|
||||
|
||||
type FragmentSpread struct {
|
||||
Name string
|
||||
Directives DirectiveList
|
||||
|
||||
// Require validation
|
||||
ObjectDefinition *Definition
|
||||
Definition *FragmentDefinition
|
||||
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type InlineFragment struct {
|
||||
TypeCondition string
|
||||
Directives DirectiveList
|
||||
SelectionSet SelectionSet
|
||||
|
||||
// Require validation
|
||||
ObjectDefinition *Definition
|
||||
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type FragmentDefinition struct {
|
||||
Name string
|
||||
// Note: fragment variable definitions are experimental and may be changed
|
||||
// or removed in the future.
|
||||
VariableDefinition VariableDefinitionList
|
||||
TypeCondition string
|
||||
Directives DirectiveList
|
||||
SelectionSet SelectionSet
|
||||
|
||||
// Require validation
|
||||
Definition *Definition
|
||||
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package ast
|
||||
|
||||
type Operation string
|
||||
|
||||
const (
|
||||
Query Operation = "query"
|
||||
Mutation Operation = "mutation"
|
||||
Subscription Operation = "subscription"
|
||||
)
|
||||
|
||||
type OperationDefinition struct {
|
||||
Operation Operation
|
||||
Name string
|
||||
VariableDefinitions VariableDefinitionList
|
||||
Directives DirectiveList
|
||||
SelectionSet SelectionSet
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
type VariableDefinition struct {
|
||||
Variable string
|
||||
Type *Type
|
||||
DefaultValue *Value
|
||||
Directives DirectiveList
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
|
||||
// Requires validation
|
||||
Definition *Definition
|
||||
Used bool `dump:"-"`
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var _ json.Unmarshaler = (*Path)(nil)
|
||||
|
||||
type Path []PathElement
|
||||
|
||||
type PathElement interface {
|
||||
isPathElement()
|
||||
}
|
||||
|
||||
var _ PathElement = PathIndex(0)
|
||||
var _ PathElement = PathName("")
|
||||
|
||||
func (path Path) String() string {
|
||||
var str bytes.Buffer
|
||||
for i, v := range path {
|
||||
switch v := v.(type) {
|
||||
case PathIndex:
|
||||
str.WriteString(fmt.Sprintf("[%d]", v))
|
||||
case PathName:
|
||||
if i != 0 {
|
||||
str.WriteByte('.')
|
||||
}
|
||||
str.WriteString(string(v))
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown type: %T", v))
|
||||
}
|
||||
}
|
||||
return str.String()
|
||||
}
|
||||
|
||||
func (path *Path) UnmarshalJSON(b []byte) error {
|
||||
var vs []interface{}
|
||||
err := json.Unmarshal(b, &vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*path = make([]PathElement, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
*path = append(*path, PathName(v))
|
||||
case int:
|
||||
*path = append(*path, PathIndex(v))
|
||||
case float64:
|
||||
*path = append(*path, PathIndex(int(v)))
|
||||
default:
|
||||
return fmt.Errorf("unknown path element type: %T", v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PathIndex int
|
||||
|
||||
func (PathIndex) isPathElement() {}
|
||||
|
||||
type PathName string
|
||||
|
||||
func (PathName) isPathElement() {}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package ast
|
||||
|
||||
type SelectionSet []Selection
|
||||
|
||||
type Selection interface {
|
||||
isSelection()
|
||||
GetPosition() *Position
|
||||
}
|
||||
|
||||
func (*Field) isSelection() {}
|
||||
func (*FragmentSpread) isSelection() {}
|
||||
func (*InlineFragment) isSelection() {}
|
||||
|
||||
func (s *Field) GetPosition() *Position { return s.Position }
|
||||
func (s *FragmentSpread) GetPosition() *Position { return s.Position }
|
||||
func (s *InlineFragment) GetPosition() *Position { return s.Position }
|
||||
|
||||
type Field struct {
|
||||
Alias string
|
||||
Name string
|
||||
Arguments ArgumentList
|
||||
Directives DirectiveList
|
||||
SelectionSet SelectionSet
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
|
||||
// Require validation
|
||||
Definition *FieldDefinition
|
||||
ObjectDefinition *Definition
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
Name string
|
||||
Value *Value
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (s *Field) ArgumentMap(vars map[string]interface{}) map[string]interface{} {
|
||||
return arg2map(s.Definition.Arguments, s.Arguments, vars)
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package ast
|
||||
|
||||
// Source covers a single *.graphql file
|
||||
type Source struct {
|
||||
// Name is the filename of the source
|
||||
Name string
|
||||
// Input is the actual contents of the source file
|
||||
Input string
|
||||
// BuiltIn indicate whether the source is a part of the specification
|
||||
BuiltIn bool
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
Start int // The starting position, in runes, of this token in the input.
|
||||
End int // The end position, in runes, of this token in the input.
|
||||
Line int // The line number at the start of this item.
|
||||
Column int // The column number at the start of this item.
|
||||
Src *Source // The source document this token belongs to
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package ast
|
||||
|
||||
func NonNullNamedType(named string, pos *Position) *Type {
|
||||
return &Type{NamedType: named, NonNull: true, Position: pos}
|
||||
}
|
||||
|
||||
func NamedType(named string, pos *Position) *Type {
|
||||
return &Type{NamedType: named, NonNull: false, Position: pos}
|
||||
}
|
||||
|
||||
func NonNullListType(elem *Type, pos *Position) *Type {
|
||||
return &Type{Elem: elem, NonNull: true, Position: pos}
|
||||
}
|
||||
|
||||
func ListType(elem *Type, pos *Position) *Type {
|
||||
return &Type{Elem: elem, NonNull: false, Position: pos}
|
||||
}
|
||||
|
||||
type Type struct {
|
||||
NamedType string
|
||||
Elem *Type
|
||||
NonNull bool
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (t *Type) Name() string {
|
||||
if t.NamedType != "" {
|
||||
return t.NamedType
|
||||
}
|
||||
|
||||
return t.Elem.Name()
|
||||
}
|
||||
|
||||
func (t *Type) String() string {
|
||||
nn := ""
|
||||
if t.NonNull {
|
||||
nn = "!"
|
||||
}
|
||||
if t.NamedType != "" {
|
||||
return t.NamedType + nn
|
||||
}
|
||||
|
||||
return "[" + t.Elem.String() + "]" + nn
|
||||
}
|
||||
|
||||
func (t *Type) IsCompatible(other *Type) bool {
|
||||
if t.NamedType != other.NamedType {
|
||||
return false
|
||||
}
|
||||
|
||||
if t.Elem != nil && other.Elem == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if t.Elem != nil && !t.Elem.IsCompatible(other.Elem) {
|
||||
return false
|
||||
}
|
||||
|
||||
if other.NonNull {
|
||||
return t.NonNull
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Type) Dump() string {
|
||||
return t.String()
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ValueKind int
|
||||
|
||||
const (
|
||||
Variable ValueKind = iota
|
||||
IntValue
|
||||
FloatValue
|
||||
StringValue
|
||||
BlockValue
|
||||
BooleanValue
|
||||
NullValue
|
||||
EnumValue
|
||||
ListValue
|
||||
ObjectValue
|
||||
)
|
||||
|
||||
type Value struct {
|
||||
Raw string
|
||||
Children ChildValueList
|
||||
Kind ValueKind
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
|
||||
// Require validation
|
||||
Definition *Definition
|
||||
VariableDefinition *VariableDefinition
|
||||
ExpectedType *Type
|
||||
}
|
||||
|
||||
type ChildValue struct {
|
||||
Name string
|
||||
Value *Value
|
||||
Position *Position `dump:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (v *Value) Value(vars map[string]interface{}) (interface{}, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch v.Kind {
|
||||
case Variable:
|
||||
if value, ok := vars[v.Raw]; ok {
|
||||
return value, nil
|
||||
}
|
||||
if v.VariableDefinition != nil && v.VariableDefinition.DefaultValue != nil {
|
||||
return v.VariableDefinition.DefaultValue.Value(vars)
|
||||
}
|
||||
return nil, nil
|
||||
case IntValue:
|
||||
return strconv.ParseInt(v.Raw, 10, 64)
|
||||
case FloatValue:
|
||||
return strconv.ParseFloat(v.Raw, 64)
|
||||
case StringValue, BlockValue, EnumValue:
|
||||
return v.Raw, nil
|
||||
case BooleanValue:
|
||||
return strconv.ParseBool(v.Raw)
|
||||
case NullValue:
|
||||
return nil, nil
|
||||
case ListValue:
|
||||
var val []interface{}
|
||||
for _, elem := range v.Children {
|
||||
elemVal, err := elem.Value.Value(vars)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
val = append(val, elemVal)
|
||||
}
|
||||
return val, nil
|
||||
case ObjectValue:
|
||||
val := map[string]interface{}{}
|
||||
for _, elem := range v.Children {
|
||||
elemVal, err := elem.Value.Value(vars)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
val[elem.Name] = elemVal
|
||||
}
|
||||
return val, nil
|
||||
default:
|
||||
panic(fmt.Errorf("unknown value kind %d", v.Kind))
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Value) String() string {
|
||||
if v == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
switch v.Kind {
|
||||
case Variable:
|
||||
return "$" + v.Raw
|
||||
case IntValue, FloatValue, EnumValue, BooleanValue, NullValue:
|
||||
return v.Raw
|
||||
case StringValue, BlockValue:
|
||||
return strconv.Quote(v.Raw)
|
||||
case ListValue:
|
||||
var val []string
|
||||
for _, elem := range v.Children {
|
||||
val = append(val, elem.Value.String())
|
||||
}
|
||||
return "[" + strings.Join(val, ",") + "]"
|
||||
case ObjectValue:
|
||||
var val []string
|
||||
for _, elem := range v.Children {
|
||||
val = append(val, elem.Name+":"+elem.Value.String())
|
||||
}
|
||||
return "{" + strings.Join(val, ",") + "}"
|
||||
default:
|
||||
panic(fmt.Errorf("unknown value kind %d", v.Kind))
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Value) Dump() string {
|
||||
return v.String()
|
||||
}
|
||||
-147
@@ -1,147 +0,0 @@
|
||||
package gqlerror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
)
|
||||
|
||||
// Error is the standard graphql error type described in https://facebook.github.io/graphql/draft/#sec-Errors
|
||||
type Error struct {
|
||||
err error `json:"-"`
|
||||
Message string `json:"message"`
|
||||
Path ast.Path `json:"path,omitempty"`
|
||||
Locations []Location `json:"locations,omitempty"`
|
||||
Extensions map[string]interface{} `json:"extensions,omitempty"`
|
||||
Rule string `json:"-"`
|
||||
}
|
||||
|
||||
func (err *Error) SetFile(file string) {
|
||||
if file == "" {
|
||||
return
|
||||
}
|
||||
if err.Extensions == nil {
|
||||
err.Extensions = map[string]interface{}{}
|
||||
}
|
||||
|
||||
err.Extensions["file"] = file
|
||||
}
|
||||
|
||||
type Location struct {
|
||||
Line int `json:"line,omitempty"`
|
||||
Column int `json:"column,omitempty"`
|
||||
}
|
||||
|
||||
type List []*Error
|
||||
|
||||
func (err *Error) Error() string {
|
||||
var res bytes.Buffer
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
filename, _ := err.Extensions["file"].(string)
|
||||
if filename == "" {
|
||||
filename = "input"
|
||||
}
|
||||
res.WriteString(filename)
|
||||
|
||||
if len(err.Locations) > 0 {
|
||||
res.WriteByte(':')
|
||||
res.WriteString(strconv.Itoa(err.Locations[0].Line))
|
||||
res.WriteByte(':')
|
||||
res.WriteString(strconv.Itoa(err.Locations[0].Column))
|
||||
}
|
||||
|
||||
res.WriteString(": ")
|
||||
if ps := err.pathString(); ps != "" {
|
||||
res.WriteString(ps)
|
||||
res.WriteByte(' ')
|
||||
}
|
||||
|
||||
res.WriteString(err.Message)
|
||||
|
||||
return res.String()
|
||||
}
|
||||
|
||||
func (err Error) pathString() string {
|
||||
return err.Path.String()
|
||||
}
|
||||
|
||||
func (err Error) Unwrap() error {
|
||||
return err.err
|
||||
}
|
||||
|
||||
func (errs List) Error() string {
|
||||
var buf bytes.Buffer
|
||||
for _, err := range errs {
|
||||
buf.WriteString(err.Error())
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (errs List) Is(target error) bool {
|
||||
for _, err := range errs {
|
||||
if errors.Is(err, target) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (errs List) As(target interface{}) bool {
|
||||
for _, err := range errs {
|
||||
if errors.As(err, target) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func WrapPath(path ast.Path, err error) *Error {
|
||||
return &Error{
|
||||
err: err,
|
||||
Message: err.Error(),
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func Errorf(message string, args ...interface{}) *Error {
|
||||
return &Error{
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorPathf(path ast.Path, message string, args ...interface{}) *Error {
|
||||
return &Error{
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorPosf(pos *ast.Position, message string, args ...interface{}) *Error {
|
||||
return ErrorLocf(
|
||||
pos.Src.Name,
|
||||
pos.Line,
|
||||
pos.Column,
|
||||
message,
|
||||
args...,
|
||||
)
|
||||
}
|
||||
|
||||
func ErrorLocf(file string, line int, col int, message string, args ...interface{}) *Error {
|
||||
var extensions map[string]interface{}
|
||||
if file != "" {
|
||||
extensions = map[string]interface{}{"file": file}
|
||||
}
|
||||
return &Error{
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
Extensions: extensions,
|
||||
Locations: []Location{
|
||||
{Line: line, Column: col},
|
||||
},
|
||||
}
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// blockStringValue produces the value of a block string from its parsed raw value, similar to
|
||||
// Coffeescript's block string, Python's docstring trim or Ruby's strip_heredoc.
|
||||
//
|
||||
// This implements the GraphQL spec's BlockStringValue() static algorithm.
|
||||
func blockStringValue(raw string) string {
|
||||
lines := strings.Split(raw, "\n")
|
||||
|
||||
commonIndent := math.MaxInt32
|
||||
for _, line := range lines {
|
||||
indent := leadingWhitespace(line)
|
||||
if indent < len(line) && indent < commonIndent {
|
||||
commonIndent = indent
|
||||
if commonIndent == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if commonIndent != math.MaxInt32 && len(lines) > 0 {
|
||||
for i := 1; i < len(lines); i++ {
|
||||
if len(lines[i]) < commonIndent {
|
||||
lines[i] = ""
|
||||
} else {
|
||||
lines[i] = lines[i][commonIndent:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start := 0
|
||||
end := len(lines)
|
||||
|
||||
for start < end && leadingWhitespace(lines[start]) == math.MaxInt32 {
|
||||
start++
|
||||
}
|
||||
|
||||
for start < end && leadingWhitespace(lines[end-1]) == math.MaxInt32 {
|
||||
end--
|
||||
}
|
||||
|
||||
return strings.Join(lines[start:end], "\n")
|
||||
}
|
||||
|
||||
func leadingWhitespace(str string) int {
|
||||
for i, r := range str {
|
||||
if r != ' ' && r != '\t' {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// this line is made up entirely of whitespace, its leading whitespace doesnt count.
|
||||
return math.MaxInt32
|
||||
}
|
||||
-517
@@ -1,517 +0,0 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/gqlerror"
|
||||
)
|
||||
|
||||
// Lexer turns graphql request and schema strings into tokens
|
||||
type Lexer struct {
|
||||
*ast.Source
|
||||
// An offset into the string in bytes
|
||||
start int
|
||||
// An offset into the string in runes
|
||||
startRunes int
|
||||
// An offset into the string in bytes
|
||||
end int
|
||||
// An offset into the string in runes
|
||||
endRunes int
|
||||
// the current line number
|
||||
line int
|
||||
// An offset into the string in rune
|
||||
lineStartRunes int
|
||||
}
|
||||
|
||||
func New(src *ast.Source) Lexer {
|
||||
return Lexer{
|
||||
Source: src,
|
||||
line: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// take one rune from input and advance end
|
||||
func (s *Lexer) peek() (rune, int) {
|
||||
return utf8.DecodeRuneInString(s.Input[s.end:])
|
||||
}
|
||||
|
||||
func (s *Lexer) makeToken(kind Type) (Token, error) {
|
||||
return s.makeValueToken(kind, s.Input[s.start:s.end])
|
||||
}
|
||||
|
||||
func (s *Lexer) makeValueToken(kind Type, value string) (Token, error) {
|
||||
return Token{
|
||||
Kind: kind,
|
||||
Value: value,
|
||||
Pos: ast.Position{
|
||||
Start: s.startRunes,
|
||||
End: s.endRunes,
|
||||
Line: s.line,
|
||||
Column: s.startRunes - s.lineStartRunes + 1,
|
||||
Src: s.Source,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Lexer) makeError(format string, args ...interface{}) (Token, error) {
|
||||
column := s.endRunes - s.lineStartRunes + 1
|
||||
return Token{
|
||||
Kind: Invalid,
|
||||
Pos: ast.Position{
|
||||
Start: s.startRunes,
|
||||
End: s.endRunes,
|
||||
Line: s.line,
|
||||
Column: column,
|
||||
Src: s.Source,
|
||||
},
|
||||
}, gqlerror.ErrorLocf(s.Source.Name, s.line, column, format, args...)
|
||||
}
|
||||
|
||||
// ReadToken gets the next token from the source starting at the given position.
|
||||
//
|
||||
// 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) {
|
||||
|
||||
s.ws()
|
||||
s.start = s.end
|
||||
s.startRunes = s.endRunes
|
||||
|
||||
if s.end >= len(s.Input) {
|
||||
return s.makeToken(EOF)
|
||||
}
|
||||
r := s.Input[s.start]
|
||||
s.end++
|
||||
s.endRunes++
|
||||
switch r {
|
||||
case '!':
|
||||
return s.makeValueToken(Bang, "")
|
||||
|
||||
case '$':
|
||||
return s.makeValueToken(Dollar, "")
|
||||
case '&':
|
||||
return s.makeValueToken(Amp, "")
|
||||
case '(':
|
||||
return s.makeValueToken(ParenL, "")
|
||||
case ')':
|
||||
return s.makeValueToken(ParenR, "")
|
||||
case '.':
|
||||
if len(s.Input) > s.start+2 && s.Input[s.start:s.start+3] == "..." {
|
||||
s.end += 2
|
||||
s.endRunes += 2
|
||||
return s.makeValueToken(Spread, "")
|
||||
}
|
||||
case ':':
|
||||
return s.makeValueToken(Colon, "")
|
||||
case '=':
|
||||
return s.makeValueToken(Equals, "")
|
||||
case '@':
|
||||
return s.makeValueToken(At, "")
|
||||
case '[':
|
||||
return s.makeValueToken(BracketL, "")
|
||||
case ']':
|
||||
return s.makeValueToken(BracketR, "")
|
||||
case '{':
|
||||
return s.makeValueToken(BraceL, "")
|
||||
case '}':
|
||||
return s.makeValueToken(BraceR, "")
|
||||
case '|':
|
||||
return s.makeValueToken(Pipe, "")
|
||||
case '#':
|
||||
if comment, err := s.readComment(); err != nil {
|
||||
return comment, err
|
||||
}
|
||||
return s.ReadToken()
|
||||
|
||||
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()
|
||||
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return s.readNumber()
|
||||
|
||||
case '"':
|
||||
if len(s.Input) > s.start+2 && s.Input[s.start:s.start+3] == `"""` {
|
||||
return s.readBlockString()
|
||||
}
|
||||
|
||||
return s.readString()
|
||||
}
|
||||
|
||||
s.end--
|
||||
s.endRunes--
|
||||
|
||||
if r < 0x0020 && r != 0x0009 && r != 0x000a && r != 0x000d {
|
||||
return s.makeError(`Cannot contain the invalid character "\u%04d"`, r)
|
||||
}
|
||||
|
||||
if r == '\'' {
|
||||
return s.makeError(`Unexpected single quote character ('), did you mean to use a double quote (")?`)
|
||||
}
|
||||
|
||||
return s.makeError(`Cannot parse the unexpected character "%s".`, string(r))
|
||||
}
|
||||
|
||||
// ws reads from body starting at startPosition until it finds a non-whitespace
|
||||
// or commented character, and updates the token end to include all whitespace
|
||||
func (s *Lexer) ws() {
|
||||
for s.end < len(s.Input) {
|
||||
switch s.Input[s.end] {
|
||||
case '\t', ' ', ',':
|
||||
s.end++
|
||||
s.endRunes++
|
||||
case '\n':
|
||||
s.end++
|
||||
s.endRunes++
|
||||
s.line++
|
||||
s.lineStartRunes = s.endRunes
|
||||
case '\r':
|
||||
s.end++
|
||||
s.endRunes++
|
||||
s.line++
|
||||
s.lineStartRunes = s.endRunes
|
||||
// skip the following newline if its there
|
||||
if s.end < len(s.Input) && s.Input[s.end] == '\n' {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
}
|
||||
// byte order mark, given ws is hot path we aren't relying on the unicode package here.
|
||||
case 0xef:
|
||||
if s.end+2 < len(s.Input) && s.Input[s.end+1] == 0xBB && s.Input[s.end+2] == 0xBF {
|
||||
s.end += 3
|
||||
s.endRunes++
|
||||
} else {
|
||||
return
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readComment from the input
|
||||
//
|
||||
// #[\u0009\u0020-\uFFFF]*
|
||||
func (s *Lexer) readComment() (Token, error) {
|
||||
for s.end < len(s.Input) {
|
||||
r, w := s.peek()
|
||||
|
||||
// SourceCharacter but not LineTerminator
|
||||
if r > 0x001f || r == '\t' {
|
||||
s.end += w
|
||||
s.endRunes++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return s.makeToken(Comment)
|
||||
}
|
||||
|
||||
// readNumber from the input, either a float
|
||||
// or an int depending on whether a decimal point appears.
|
||||
//
|
||||
// Int: -?(0|[1-9][0-9]*)
|
||||
// Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)?
|
||||
func (s *Lexer) readNumber() (Token, error) {
|
||||
float := false
|
||||
|
||||
// backup to the first digit
|
||||
s.end--
|
||||
s.endRunes--
|
||||
|
||||
s.acceptByte('-')
|
||||
|
||||
if s.acceptByte('0') {
|
||||
if consumed := s.acceptDigits(); consumed != 0 {
|
||||
s.end -= consumed
|
||||
s.endRunes -= consumed
|
||||
return s.makeError("Invalid number, unexpected digit after 0: %s.", s.describeNext())
|
||||
}
|
||||
} else {
|
||||
if consumed := s.acceptDigits(); consumed == 0 {
|
||||
return s.makeError("Invalid number, expected digit but got: %s.", s.describeNext())
|
||||
}
|
||||
}
|
||||
|
||||
if s.acceptByte('.') {
|
||||
float = true
|
||||
|
||||
if consumed := s.acceptDigits(); consumed == 0 {
|
||||
return s.makeError("Invalid number, expected digit but got: %s.", s.describeNext())
|
||||
}
|
||||
}
|
||||
|
||||
if s.acceptByte('e', 'E') {
|
||||
float = true
|
||||
|
||||
s.acceptByte('-', '+')
|
||||
|
||||
if consumed := s.acceptDigits(); consumed == 0 {
|
||||
return s.makeError("Invalid number, expected digit but got: %s.", s.describeNext())
|
||||
}
|
||||
}
|
||||
|
||||
if float {
|
||||
return s.makeToken(Float)
|
||||
}
|
||||
return s.makeToken(Int)
|
||||
|
||||
}
|
||||
|
||||
// acceptByte if it matches any of given bytes, returning true if it found anything
|
||||
func (s *Lexer) acceptByte(bytes ...uint8) bool {
|
||||
if s.end >= len(s.Input) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, accepted := range bytes {
|
||||
if s.Input[s.end] == accepted {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// acceptDigits from the input, returning the number of digits it found
|
||||
func (s *Lexer) acceptDigits() int {
|
||||
consumed := 0
|
||||
for s.end < len(s.Input) && s.Input[s.end] >= '0' && s.Input[s.end] <= '9' {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
consumed++
|
||||
}
|
||||
|
||||
return consumed
|
||||
}
|
||||
|
||||
// describeNext peeks at the input and returns a human readable string. This should will alloc
|
||||
// and should only be used in errors
|
||||
func (s *Lexer) describeNext() string {
|
||||
if s.end < len(s.Input) {
|
||||
return `"` + string(s.Input[s.end]) + `"`
|
||||
}
|
||||
return "<EOF>"
|
||||
}
|
||||
|
||||
// readString from the input
|
||||
//
|
||||
// "([^"\\\u000A\u000D]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*"
|
||||
func (s *Lexer) readString() (Token, error) {
|
||||
inputLen := len(s.Input)
|
||||
|
||||
// this buffer is lazily created only if there are escape characters.
|
||||
var buf *bytes.Buffer
|
||||
|
||||
// skip the opening quote
|
||||
s.start++
|
||||
s.startRunes++
|
||||
|
||||
for s.end < inputLen {
|
||||
r := s.Input[s.end]
|
||||
if r == '\n' || r == '\r' {
|
||||
break
|
||||
}
|
||||
if r < 0x0020 && r != '\t' {
|
||||
return s.makeError(`Invalid character within String: "\u%04d".`, r)
|
||||
}
|
||||
switch r {
|
||||
default:
|
||||
var char = rune(r)
|
||||
var w = 1
|
||||
|
||||
// skip unicode overhead if we are in the ascii range
|
||||
if r >= 127 {
|
||||
char, w = utf8.DecodeRuneInString(s.Input[s.end:])
|
||||
}
|
||||
s.end += w
|
||||
s.endRunes++
|
||||
|
||||
if buf != nil {
|
||||
buf.WriteRune(char)
|
||||
}
|
||||
|
||||
case '"':
|
||||
t, err := s.makeToken(String)
|
||||
// the token should not include the quotes in its value, but should cover them in its position
|
||||
t.Pos.Start--
|
||||
t.Pos.End++
|
||||
|
||||
if buf != nil {
|
||||
t.Value = buf.String()
|
||||
}
|
||||
|
||||
// skip the close quote
|
||||
s.end++
|
||||
s.endRunes++
|
||||
|
||||
return t, err
|
||||
|
||||
case '\\':
|
||||
if s.end+1 >= inputLen {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return s.makeError(`Invalid character escape sequence.`)
|
||||
}
|
||||
|
||||
if buf == nil {
|
||||
buf = bytes.NewBufferString(s.Input[s.start:s.end])
|
||||
}
|
||||
|
||||
escape := s.Input[s.end+1]
|
||||
|
||||
if escape == 'u' {
|
||||
if s.end+6 >= inputLen {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return s.makeError("Invalid character escape sequence: \\%s.", s.Input[s.end:])
|
||||
}
|
||||
|
||||
r, ok := unhex(s.Input[s.end+2 : s.end+6])
|
||||
if !ok {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return s.makeError("Invalid character escape sequence: \\%s.", s.Input[s.end:s.end+5])
|
||||
}
|
||||
buf.WriteRune(r)
|
||||
s.end += 6
|
||||
s.endRunes += 6
|
||||
} else {
|
||||
switch escape {
|
||||
case '"', '/', '\\':
|
||||
buf.WriteByte(escape)
|
||||
case 'b':
|
||||
buf.WriteByte('\b')
|
||||
case 'f':
|
||||
buf.WriteByte('\f')
|
||||
case 'n':
|
||||
buf.WriteByte('\n')
|
||||
case 'r':
|
||||
buf.WriteByte('\r')
|
||||
case 't':
|
||||
buf.WriteByte('\t')
|
||||
default:
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return s.makeError("Invalid character escape sequence: \\%s.", string(escape))
|
||||
}
|
||||
s.end += 2
|
||||
s.endRunes += 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s.makeError("Unterminated string.")
|
||||
}
|
||||
|
||||
// readBlockString from the input
|
||||
//
|
||||
// """("?"?(\\"""|\\(?!=""")|[^"\\]))*"""
|
||||
func (s *Lexer) readBlockString() (Token, error) {
|
||||
inputLen := len(s.Input)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
// skip the opening quote
|
||||
s.start += 3
|
||||
s.startRunes += 3
|
||||
s.end += 2
|
||||
s.endRunes += 2
|
||||
|
||||
for s.end < inputLen {
|
||||
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()))
|
||||
|
||||
// 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
|
||||
|
||||
// skip the close quote
|
||||
s.end += 3
|
||||
s.endRunes += 3
|
||||
return t, err
|
||||
}
|
||||
|
||||
// SourceCharacter
|
||||
if r < 0x0020 && r != '\t' && r != '\n' && r != '\r' {
|
||||
return s.makeError(`Invalid character within String: "\u%04d".`, r)
|
||||
}
|
||||
|
||||
if r == '\\' && s.end+4 <= inputLen && s.Input[s.end:s.end+4] == `\"""` {
|
||||
buf.WriteString(`"""`)
|
||||
s.end += 4
|
||||
s.endRunes += 4
|
||||
} else if r == '\r' {
|
||||
if s.end+1 < inputLen && s.Input[s.end+1] == '\n' {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
}
|
||||
|
||||
buf.WriteByte('\n')
|
||||
s.end++
|
||||
s.endRunes++
|
||||
s.line++
|
||||
s.lineStartRunes = s.endRunes
|
||||
} else {
|
||||
var char = rune(r)
|
||||
var w = 1
|
||||
|
||||
// skip unicode overhead if we are in the ascii range
|
||||
if r >= 127 {
|
||||
char, w = utf8.DecodeRuneInString(s.Input[s.end:])
|
||||
}
|
||||
s.end += w
|
||||
s.endRunes++
|
||||
buf.WriteRune(char)
|
||||
if r == '\n' {
|
||||
s.line++
|
||||
s.lineStartRunes = s.endRunes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s.makeError("Unterminated string.")
|
||||
}
|
||||
|
||||
func unhex(b string) (v rune, ok bool) {
|
||||
for _, c := range b {
|
||||
v <<= 4
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
v |= c - '0'
|
||||
case 'a' <= c && c <= 'f':
|
||||
v |= c - 'a' + 10
|
||||
case 'A' <= c && c <= 'F':
|
||||
v |= c - 'A' + 10
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
return v, true
|
||||
}
|
||||
|
||||
// readName from the input
|
||||
//
|
||||
// [_A-Za-z][_0-9A-Za-z]*
|
||||
func (s *Lexer) readName() (Token, error) {
|
||||
for s.end < len(s.Input) {
|
||||
r, w := s.peek()
|
||||
|
||||
if (r >= '0' && r <= '9') || (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' {
|
||||
s.end += w
|
||||
s.endRunes++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return s.makeToken(Name)
|
||||
}
|
||||
-692
@@ -1,692 +0,0 @@
|
||||
encoding:
|
||||
- name: disallows uncommon control characters
|
||||
input: "\u0007"
|
||||
error:
|
||||
message: 'Cannot contain the invalid character "\u0007"'
|
||||
locations: [{line: 1, column: 1}]
|
||||
|
||||
- name: accepts BOM header
|
||||
input: "\uFEFF foo"
|
||||
tokens:
|
||||
-
|
||||
kind: NAME
|
||||
start: 2
|
||||
end: 5
|
||||
value: 'foo'
|
||||
|
||||
simple tokens:
|
||||
- name: records line and column
|
||||
input: "\n \r\n \r foo\n"
|
||||
tokens:
|
||||
-
|
||||
kind: NAME
|
||||
start: 8
|
||||
end: 11
|
||||
line: 4
|
||||
column: 3
|
||||
value: 'foo'
|
||||
|
||||
- name: skips whitespace
|
||||
input: "\n\n foo\n\n\n"
|
||||
tokens:
|
||||
-
|
||||
kind: NAME
|
||||
start: 6
|
||||
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:
|
||||
-
|
||||
kind: NAME
|
||||
start: 3
|
||||
end: 6
|
||||
value: 'foo'
|
||||
|
||||
- name: errors respect whitespace
|
||||
input: "\n\n ?\n\n\n"
|
||||
error:
|
||||
message: 'Cannot parse the unexpected character "?".'
|
||||
locations: [{line: 3, column: 5}]
|
||||
string: |
|
||||
Syntax Error: Cannot parse the unexpected character "?".
|
||||
GraphQL request (3:5)
|
||||
2:
|
||||
3: ?
|
||||
^
|
||||
4:
|
||||
|
||||
- name: lex reports useful information for dashes in names
|
||||
input: "a-b"
|
||||
error:
|
||||
message: 'Invalid number, expected digit but got: "b".'
|
||||
locations: [{ line: 1, column: 3 }]
|
||||
tokens:
|
||||
-
|
||||
kind: Name
|
||||
start: 0
|
||||
end: 1
|
||||
value: a
|
||||
|
||||
lexes strings:
|
||||
- name: basic
|
||||
input: '"simple"'
|
||||
tokens:
|
||||
-
|
||||
kind: STRING
|
||||
start: 0
|
||||
end: 8
|
||||
value: 'simple'
|
||||
|
||||
- name: whitespace
|
||||
input: '" white space "'
|
||||
tokens:
|
||||
-
|
||||
kind: STRING
|
||||
start: 0
|
||||
end: 15
|
||||
value: ' white space '
|
||||
|
||||
- name: quote
|
||||
input: '"quote \""'
|
||||
tokens:
|
||||
-
|
||||
kind: STRING
|
||||
start: 0
|
||||
end: 10
|
||||
value: 'quote "'
|
||||
|
||||
- name: escaped
|
||||
input: '"escaped \n\r\b\t\f"'
|
||||
tokens:
|
||||
-
|
||||
kind: STRING
|
||||
start: 0
|
||||
end: 20
|
||||
value: "escaped \n\r\b\t\f"
|
||||
|
||||
- name: slashes
|
||||
input: '"slashes \\ \/"'
|
||||
tokens:
|
||||
-
|
||||
kind: STRING
|
||||
start: 0
|
||||
end: 15
|
||||
value: 'slashes \ /'
|
||||
|
||||
- name: unicode
|
||||
input: '"unicode \u1234\u5678\u90AB\uCDEF"'
|
||||
tokens:
|
||||
-
|
||||
kind: STRING
|
||||
start: 0
|
||||
end: 34
|
||||
value: "unicode \u1234\u5678\u90AB\uCDEF"
|
||||
|
||||
lex reports useful string errors:
|
||||
- name: unterminated
|
||||
input: '"'
|
||||
error:
|
||||
message: "Unterminated string."
|
||||
locations: [{ line: 1, column: 2 }]
|
||||
|
||||
- name: no end quote
|
||||
input: '"no end quote'
|
||||
error:
|
||||
message: 'Unterminated string.'
|
||||
locations: [{ line: 1, column: 14 }]
|
||||
|
||||
- name: single quotes
|
||||
input: "'single quotes'"
|
||||
error:
|
||||
message: "Unexpected single quote character ('), did you mean to use a double quote (\")?"
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
- name: control characters
|
||||
input: "\"contains unescaped \u0007 control char\""
|
||||
error:
|
||||
message: 'Invalid character within String: "\u0007".'
|
||||
locations: [{ line: 1, column: 21 }]
|
||||
|
||||
- name: null byte
|
||||
input: "\"null-byte is not \u0000 end of file\""
|
||||
error:
|
||||
message: 'Invalid character within String: "\u0000".'
|
||||
locations: [{ line: 1, column: 19 }]
|
||||
|
||||
- name: unterminated newline
|
||||
input: "\"multi\nline\""
|
||||
error:
|
||||
message: 'Unterminated string.'
|
||||
locations: [{line: 1, column: 7 }]
|
||||
|
||||
- name: unterminated carriage return
|
||||
input: "\"multi\rline\""
|
||||
error:
|
||||
message: 'Unterminated string.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
- name: bad escape character
|
||||
input: '"bad \z esc"'
|
||||
error:
|
||||
message: 'Invalid character escape sequence: \z.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
- name: hex escape sequence
|
||||
input: '"bad \x esc"'
|
||||
error:
|
||||
message: 'Invalid character escape sequence: \x.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
- name: short escape sequence
|
||||
input: '"bad \u1 esc"'
|
||||
error:
|
||||
message: 'Invalid character escape sequence: \u1 es.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
- name: invalid escape sequence 1
|
||||
input: '"bad \u0XX1 esc"'
|
||||
error:
|
||||
message: 'Invalid character escape sequence: \u0XX1.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
- name: invalid escape sequence 2
|
||||
input: '"bad \uXXXX esc"'
|
||||
error:
|
||||
message: 'Invalid character escape sequence: \uXXXX.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
- name: invalid escape sequence 3
|
||||
input: '"bad \uFXXX esc"'
|
||||
error:
|
||||
message: 'Invalid character escape sequence: \uFXXX.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
- name: invalid character escape sequence
|
||||
input: '"bad \uXXXF esc"'
|
||||
error:
|
||||
message: 'Invalid character escape sequence: \uXXXF.'
|
||||
locations: [{ line: 1, column: 7 }]
|
||||
|
||||
lexes block strings:
|
||||
- name: simple
|
||||
input: '"""simple"""'
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 12
|
||||
value: 'simple'
|
||||
|
||||
- name: white space
|
||||
input: '""" white space """'
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 19
|
||||
value: ' white space '
|
||||
|
||||
- name: contains quote
|
||||
input: '"""contains " quote"""'
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 22
|
||||
value: 'contains " quote'
|
||||
|
||||
- name: contains triplequote
|
||||
input: "\"\"\"contains \\\"\"\" triplequote\"\"\""
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 31
|
||||
value: 'contains """ triplequote'
|
||||
|
||||
- name: multi line
|
||||
input: "\"\"\"multi\nline\"\"\""
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 16
|
||||
value: "multi\nline"
|
||||
|
||||
- name: multi line normalized
|
||||
input: "\"\"\"multi\rline\r\nnormalized\"\"\""
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 28
|
||||
value: "multi\nline\nnormalized"
|
||||
|
||||
- name: unescaped
|
||||
input: '"""unescaped \n\r\b\t\f\u1234"""'
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 32
|
||||
value: 'unescaped \n\r\b\t\f\u1234'
|
||||
|
||||
- name: slashes
|
||||
input: '"""slashes \\ \/"""'
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 19
|
||||
value: 'slashes \\ \/'
|
||||
|
||||
- name: multiple lines
|
||||
input: |
|
||||
"""
|
||||
|
||||
spans
|
||||
multiple
|
||||
lines
|
||||
|
||||
"""
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
start: 0
|
||||
end: 36
|
||||
value: "spans\n multiple\n lines"
|
||||
|
||||
- name: records correct line and column after block string
|
||||
input: |
|
||||
"""
|
||||
|
||||
some
|
||||
description
|
||||
|
||||
""" foo
|
||||
tokens:
|
||||
-
|
||||
kind: BLOCK_STRING
|
||||
value: "some\ndescription"
|
||||
-
|
||||
kind: NAME
|
||||
start: 27
|
||||
end: 30
|
||||
line: 6
|
||||
column: 5
|
||||
value: 'foo'
|
||||
|
||||
lex reports useful block string errors:
|
||||
- name: unterminated string
|
||||
input: '"""'
|
||||
error:
|
||||
message: "Unterminated string."
|
||||
locations: [{ line: 1, column: 4 }]
|
||||
|
||||
- name: unescaped control characters
|
||||
input: "\"\"\"contains unescaped \u0007 control char\"\"\""
|
||||
error:
|
||||
message: 'Invalid character within String: "\u0007".'
|
||||
locations: [{ line: 1, column: 23 }]
|
||||
|
||||
- name: null byte
|
||||
input: "\"\"\"null-byte is not \u0000 end of file\"\"\""
|
||||
error:
|
||||
message: 'Invalid character within String: "\u0000".'
|
||||
locations: [{ line: 1, column: 21 }]
|
||||
|
||||
lexes numbers:
|
||||
- name: integer
|
||||
input: "4"
|
||||
tokens:
|
||||
-
|
||||
kind: INT
|
||||
start: 0
|
||||
end: 1
|
||||
value: '4'
|
||||
|
||||
- name: float
|
||||
input: "4.123"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 5
|
||||
value: '4.123'
|
||||
|
||||
- name: negative
|
||||
input: "-4"
|
||||
tokens:
|
||||
-
|
||||
kind: INT
|
||||
start: 0
|
||||
end: 2
|
||||
value: '-4'
|
||||
|
||||
- name: nine
|
||||
input: "9"
|
||||
tokens:
|
||||
-
|
||||
kind: INT
|
||||
start: 0
|
||||
end: 1
|
||||
value: '9'
|
||||
|
||||
- name: zero
|
||||
input: "0"
|
||||
tokens:
|
||||
-
|
||||
kind: INT
|
||||
start: 0
|
||||
end: 1
|
||||
value: '0'
|
||||
|
||||
- name: negative float
|
||||
input: "-4.123"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 6
|
||||
value: '-4.123'
|
||||
|
||||
- name: float leading zero
|
||||
input: "0.123"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 5
|
||||
value: '0.123'
|
||||
|
||||
- name: exponent whole
|
||||
input: "123e4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 5
|
||||
value: '123e4'
|
||||
|
||||
- name: exponent uppercase
|
||||
input: "123E4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 5
|
||||
value: '123E4'
|
||||
|
||||
- name: exponent negative power
|
||||
input: "123e-4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 6
|
||||
value: '123e-4'
|
||||
|
||||
- name: exponent positive power
|
||||
input: "123e+4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 6
|
||||
value: '123e+4'
|
||||
|
||||
- name: exponent negative base
|
||||
input: "-1.123e4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 8
|
||||
value: '-1.123e4'
|
||||
|
||||
- name: exponent negative base upper
|
||||
input: "-1.123E4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 8
|
||||
value: '-1.123E4'
|
||||
|
||||
- name: exponent negative base negative power
|
||||
input: "-1.123e-4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 9
|
||||
value: '-1.123e-4'
|
||||
|
||||
- name: exponent negative base positive power
|
||||
input: "-1.123e+4"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 9
|
||||
value: '-1.123e+4'
|
||||
|
||||
- name: exponent negative base large power
|
||||
input: "-1.123e4567"
|
||||
tokens:
|
||||
-
|
||||
kind: FLOAT
|
||||
start: 0
|
||||
end: 11
|
||||
value: '-1.123e4567'
|
||||
|
||||
lex reports useful number errors:
|
||||
- name: zero
|
||||
input: "00"
|
||||
error:
|
||||
message: 'Invalid number, unexpected digit after 0: "0".'
|
||||
locations: [{ line: 1, column: 2 }]
|
||||
|
||||
- name: positive
|
||||
input: "+1"
|
||||
error:
|
||||
message: 'Cannot parse the unexpected character "+".'
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
- name: trailing dot
|
||||
input: "1."
|
||||
error:
|
||||
message: 'Invalid number, expected digit but got: <EOF>.'
|
||||
locations: [{ line: 1, column: 3 }]
|
||||
|
||||
- name: traililng dot exponent
|
||||
input: "1.e1"
|
||||
error:
|
||||
message: 'Invalid number, expected digit but got: "e".'
|
||||
locations: [{ line: 1, column: 3 }]
|
||||
|
||||
- name: missing leading zero
|
||||
input: ".123"
|
||||
error:
|
||||
message: 'Cannot parse the unexpected character ".".'
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
- name: characters
|
||||
input: "1.A"
|
||||
error:
|
||||
message: 'Invalid number, expected digit but got: "A".'
|
||||
locations: [{ line: 1, column: 3 }]
|
||||
|
||||
- name: negative characters
|
||||
input: "-A"
|
||||
error:
|
||||
message: 'Invalid number, expected digit but got: "A".'
|
||||
locations: [{ line: 1, column: 2 }]
|
||||
|
||||
- name: missing exponent
|
||||
input: '1.0e'
|
||||
error:
|
||||
message: 'Invalid number, expected digit but got: <EOF>.'
|
||||
locations: [{ line: 1, column: 5 }]
|
||||
|
||||
- name: character exponent
|
||||
input: "1.0eA"
|
||||
error:
|
||||
message: 'Invalid number, expected digit but got: "A".'
|
||||
locations: [{ line: 1, column: 5 }]
|
||||
|
||||
lexes punctuation:
|
||||
- name: bang
|
||||
input: "!"
|
||||
tokens:
|
||||
-
|
||||
kind: BANG
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: dollar
|
||||
input: "$"
|
||||
tokens:
|
||||
-
|
||||
kind: DOLLAR
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: open paren
|
||||
input: "("
|
||||
tokens:
|
||||
-
|
||||
kind: PAREN_L
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: close paren
|
||||
input: ")"
|
||||
tokens:
|
||||
-
|
||||
kind: PAREN_R
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: spread
|
||||
input: "..."
|
||||
tokens:
|
||||
-
|
||||
kind: SPREAD
|
||||
start: 0
|
||||
end: 3
|
||||
value: undefined
|
||||
|
||||
- name: colon
|
||||
input: ":"
|
||||
tokens:
|
||||
-
|
||||
kind: COLON
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: equals
|
||||
input: "="
|
||||
tokens:
|
||||
-
|
||||
kind: EQUALS
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: at
|
||||
input: "@"
|
||||
tokens:
|
||||
-
|
||||
kind: AT
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: open bracket
|
||||
input: "["
|
||||
tokens:
|
||||
-
|
||||
kind: BRACKET_L
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: close bracket
|
||||
input: "]"
|
||||
tokens:
|
||||
-
|
||||
kind: BRACKET_R
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: open brace
|
||||
input: "{"
|
||||
tokens:
|
||||
-
|
||||
kind: BRACE_L
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: close brace
|
||||
input: "}"
|
||||
tokens:
|
||||
-
|
||||
kind: BRACE_R
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
- name: pipe
|
||||
input: "|"
|
||||
tokens:
|
||||
-
|
||||
kind: PIPE
|
||||
start: 0
|
||||
end: 1
|
||||
value: undefined
|
||||
|
||||
lex reports useful unknown character error:
|
||||
- name: not a spread
|
||||
input: ".."
|
||||
error:
|
||||
message: 'Cannot parse the unexpected character ".".'
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
- name: question mark
|
||||
input: "?"
|
||||
error:
|
||||
message: 'Cannot parse the unexpected character "?".'
|
||||
message: 'Cannot parse the unexpected character "?".'
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
- name: unicode 203
|
||||
input: "\u203B"
|
||||
error:
|
||||
message: 'Cannot parse the unexpected character "â".'
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
- name: unicode 200
|
||||
input: "\u200b"
|
||||
error:
|
||||
message: 'Cannot parse the unexpected character "â".'
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
)
|
||||
|
||||
const (
|
||||
Invalid Type = iota
|
||||
EOF
|
||||
Bang
|
||||
Dollar
|
||||
Amp
|
||||
ParenL
|
||||
ParenR
|
||||
Spread
|
||||
Colon
|
||||
Equals
|
||||
At
|
||||
BracketL
|
||||
BracketR
|
||||
BraceL
|
||||
BraceR
|
||||
Pipe
|
||||
Name
|
||||
Int
|
||||
Float
|
||||
String
|
||||
BlockString
|
||||
Comment
|
||||
)
|
||||
|
||||
func (t Type) Name() string {
|
||||
switch t {
|
||||
case Invalid:
|
||||
return "Invalid"
|
||||
case EOF:
|
||||
return "EOF"
|
||||
case Bang:
|
||||
return "Bang"
|
||||
case Dollar:
|
||||
return "Dollar"
|
||||
case Amp:
|
||||
return "Amp"
|
||||
case ParenL:
|
||||
return "ParenL"
|
||||
case ParenR:
|
||||
return "ParenR"
|
||||
case Spread:
|
||||
return "Spread"
|
||||
case Colon:
|
||||
return "Colon"
|
||||
case Equals:
|
||||
return "Equals"
|
||||
case At:
|
||||
return "At"
|
||||
case BracketL:
|
||||
return "BracketL"
|
||||
case BracketR:
|
||||
return "BracketR"
|
||||
case BraceL:
|
||||
return "BraceL"
|
||||
case BraceR:
|
||||
return "BraceR"
|
||||
case Pipe:
|
||||
return "Pipe"
|
||||
case Name:
|
||||
return "Name"
|
||||
case Int:
|
||||
return "Int"
|
||||
case Float:
|
||||
return "Float"
|
||||
case String:
|
||||
return "String"
|
||||
case BlockString:
|
||||
return "BlockString"
|
||||
case Comment:
|
||||
return "Comment"
|
||||
}
|
||||
return "Unknown " + strconv.Itoa(int(t))
|
||||
}
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case Invalid:
|
||||
return "<Invalid>"
|
||||
case EOF:
|
||||
return "<EOF>"
|
||||
case Bang:
|
||||
return "!"
|
||||
case Dollar:
|
||||
return "$"
|
||||
case Amp:
|
||||
return "&"
|
||||
case ParenL:
|
||||
return "("
|
||||
case ParenR:
|
||||
return ")"
|
||||
case Spread:
|
||||
return "..."
|
||||
case Colon:
|
||||
return ":"
|
||||
case Equals:
|
||||
return "="
|
||||
case At:
|
||||
return "@"
|
||||
case BracketL:
|
||||
return "["
|
||||
case BracketR:
|
||||
return "]"
|
||||
case BraceL:
|
||||
return "{"
|
||||
case BraceR:
|
||||
return "}"
|
||||
case Pipe:
|
||||
return "|"
|
||||
case Name:
|
||||
return "Name"
|
||||
case Int:
|
||||
return "Int"
|
||||
case Float:
|
||||
return "Float"
|
||||
case String:
|
||||
return "String"
|
||||
case BlockString:
|
||||
return "BlockString"
|
||||
case Comment:
|
||||
return "Comment"
|
||||
}
|
||||
return "Unknown " + strconv.Itoa(int(t))
|
||||
}
|
||||
|
||||
// Kind represents a type of token. The types are predefined as constants.
|
||||
type Type int
|
||||
|
||||
type Token struct {
|
||||
Kind Type // The token type.
|
||||
Value string // The literal value consumed.
|
||||
Pos ast.Position // The file and line this token was read from
|
||||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
if t.Value != "" {
|
||||
return t.Kind.String() + " " + strconv.Quote(t.Value)
|
||||
}
|
||||
return t.Kind.String()
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
lexer lexer.Lexer
|
||||
err error
|
||||
|
||||
peeked bool
|
||||
peekToken lexer.Token
|
||||
peekError error
|
||||
|
||||
prev lexer.Token
|
||||
}
|
||||
|
||||
func (p *parser) peekPos() *ast.Position {
|
||||
if p.err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
peek := p.peek()
|
||||
return &peek.Pos
|
||||
}
|
||||
|
||||
func (p *parser) peek() lexer.Token {
|
||||
if p.err != nil {
|
||||
return p.prev
|
||||
}
|
||||
|
||||
if !p.peeked {
|
||||
p.peekToken, p.peekError = p.lexer.ReadToken()
|
||||
p.peeked = true
|
||||
}
|
||||
|
||||
return p.peekToken
|
||||
}
|
||||
|
||||
func (p *parser) error(tok lexer.Token, format string, args ...interface{}) {
|
||||
if p.err != nil {
|
||||
return
|
||||
}
|
||||
p.err = gqlerror.ErrorLocf(tok.Pos.Src.Name, tok.Pos.Line, tok.Pos.Column, format, args...)
|
||||
}
|
||||
|
||||
func (p *parser) next() lexer.Token {
|
||||
if p.err != nil {
|
||||
return p.prev
|
||||
}
|
||||
if p.peeked {
|
||||
p.peeked = false
|
||||
p.prev, p.err = p.peekToken, p.peekError
|
||||
} else {
|
||||
p.prev, p.err = p.lexer.ReadToken()
|
||||
}
|
||||
return p.prev
|
||||
}
|
||||
|
||||
func (p *parser) expectKeyword(value string) lexer.Token {
|
||||
tok := p.peek()
|
||||
if tok.Kind == lexer.Name && tok.Value == value {
|
||||
return p.next()
|
||||
}
|
||||
|
||||
p.error(tok, "Expected %s, found %s", strconv.Quote(value), tok.String())
|
||||
return tok
|
||||
}
|
||||
|
||||
func (p *parser) expect(kind lexer.Type) lexer.Token {
|
||||
tok := p.peek()
|
||||
if tok.Kind == kind {
|
||||
return p.next()
|
||||
}
|
||||
|
||||
p.error(tok, "Expected %s, found %s", kind, tok.Kind.String())
|
||||
return tok
|
||||
}
|
||||
|
||||
func (p *parser) skip(kind lexer.Type) bool {
|
||||
if p.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
tok := p.peek()
|
||||
|
||||
if tok.Kind != kind {
|
||||
return false
|
||||
}
|
||||
p.next()
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *parser) unexpectedError() {
|
||||
p.unexpectedToken(p.peek())
|
||||
}
|
||||
|
||||
func (p *parser) unexpectedToken(tok lexer.Token) {
|
||||
p.error(tok, "Unexpected %s", tok.String())
|
||||
}
|
||||
|
||||
func (p *parser) many(start lexer.Type, end lexer.Type, cb func()) {
|
||||
hasDef := p.skip(start)
|
||||
if !hasDef {
|
||||
return
|
||||
}
|
||||
|
||||
for p.peek().Kind != end && p.err == nil {
|
||||
cb()
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
|
||||
func (p *parser) some(start lexer.Type, end lexer.Type, cb func()) {
|
||||
hasDef := p.skip(start)
|
||||
if !hasDef {
|
||||
return
|
||||
}
|
||||
|
||||
called := false
|
||||
for p.peek().Kind != end && p.err == nil {
|
||||
called = true
|
||||
cb()
|
||||
}
|
||||
|
||||
if !called {
|
||||
p.error(p.peek(), "expected at least one definition, found %s", p.peek().Kind.String())
|
||||
return
|
||||
}
|
||||
|
||||
p.next()
|
||||
}
|
||||
-349
@@ -1,349 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/lexer"
|
||||
|
||||
//nolint:revive
|
||||
. "github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
)
|
||||
|
||||
func ParseQuery(source *Source) (*QueryDocument, error) {
|
||||
p := parser{
|
||||
lexer: lexer.New(source),
|
||||
}
|
||||
return p.parseQueryDocument(), p.err
|
||||
}
|
||||
|
||||
func (p *parser) parseQueryDocument() *QueryDocument {
|
||||
var doc QueryDocument
|
||||
for p.peek().Kind != lexer.EOF {
|
||||
if p.err != nil {
|
||||
return &doc
|
||||
}
|
||||
doc.Position = p.peekPos()
|
||||
switch p.peek().Kind {
|
||||
case lexer.Name:
|
||||
switch p.peek().Value {
|
||||
case "query", "mutation", "subscription":
|
||||
doc.Operations = append(doc.Operations, p.parseOperationDefinition())
|
||||
case "fragment":
|
||||
doc.Fragments = append(doc.Fragments, p.parseFragmentDefinition())
|
||||
default:
|
||||
p.unexpectedError()
|
||||
}
|
||||
case lexer.BraceL:
|
||||
doc.Operations = append(doc.Operations, p.parseOperationDefinition())
|
||||
default:
|
||||
p.unexpectedError()
|
||||
}
|
||||
}
|
||||
|
||||
return &doc
|
||||
}
|
||||
|
||||
func (p *parser) parseOperationDefinition() *OperationDefinition {
|
||||
if p.peek().Kind == lexer.BraceL {
|
||||
return &OperationDefinition{
|
||||
Position: p.peekPos(),
|
||||
Operation: Query,
|
||||
SelectionSet: p.parseRequiredSelectionSet(),
|
||||
}
|
||||
}
|
||||
|
||||
var od OperationDefinition
|
||||
od.Position = p.peekPos()
|
||||
od.Operation = p.parseOperationType()
|
||||
|
||||
if p.peek().Kind == lexer.Name {
|
||||
od.Name = p.next().Value
|
||||
}
|
||||
|
||||
od.VariableDefinitions = p.parseVariableDefinitions()
|
||||
od.Directives = p.parseDirectives(false)
|
||||
od.SelectionSet = p.parseRequiredSelectionSet()
|
||||
|
||||
return &od
|
||||
}
|
||||
|
||||
func (p *parser) parseOperationType() Operation {
|
||||
tok := p.next()
|
||||
switch tok.Value {
|
||||
case "query":
|
||||
return Query
|
||||
case "mutation":
|
||||
return Mutation
|
||||
case "subscription":
|
||||
return Subscription
|
||||
}
|
||||
p.unexpectedToken(tok)
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *parser) parseVariableDefinitions() VariableDefinitionList {
|
||||
var defs []*VariableDefinition
|
||||
p.many(lexer.ParenL, lexer.ParenR, func() {
|
||||
defs = append(defs, p.parseVariableDefinition())
|
||||
})
|
||||
|
||||
return defs
|
||||
}
|
||||
|
||||
func (p *parser) parseVariableDefinition() *VariableDefinition {
|
||||
var def VariableDefinition
|
||||
def.Position = p.peekPos()
|
||||
def.Variable = p.parseVariable()
|
||||
|
||||
p.expect(lexer.Colon)
|
||||
|
||||
def.Type = p.parseTypeReference()
|
||||
|
||||
if p.skip(lexer.Equals) {
|
||||
def.DefaultValue = p.parseValueLiteral(true)
|
||||
}
|
||||
|
||||
def.Directives = p.parseDirectives(false)
|
||||
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseVariable() string {
|
||||
p.expect(lexer.Dollar)
|
||||
return p.parseName()
|
||||
}
|
||||
|
||||
func (p *parser) parseOptionalSelectionSet() SelectionSet {
|
||||
var selections []Selection
|
||||
p.some(lexer.BraceL, lexer.BraceR, func() {
|
||||
selections = append(selections, p.parseSelection())
|
||||
})
|
||||
|
||||
return SelectionSet(selections)
|
||||
}
|
||||
|
||||
func (p *parser) parseRequiredSelectionSet() SelectionSet {
|
||||
if p.peek().Kind != lexer.BraceL {
|
||||
p.error(p.peek(), "Expected %s, found %s", lexer.BraceL, p.peek().Kind.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
var selections []Selection
|
||||
p.some(lexer.BraceL, lexer.BraceR, func() {
|
||||
selections = append(selections, p.parseSelection())
|
||||
})
|
||||
|
||||
return SelectionSet(selections)
|
||||
}
|
||||
|
||||
func (p *parser) parseSelection() Selection {
|
||||
if p.peek().Kind == lexer.Spread {
|
||||
return p.parseFragment()
|
||||
}
|
||||
return p.parseField()
|
||||
}
|
||||
|
||||
func (p *parser) parseField() *Field {
|
||||
var field Field
|
||||
field.Position = p.peekPos()
|
||||
field.Alias = p.parseName()
|
||||
|
||||
if p.skip(lexer.Colon) {
|
||||
field.Name = p.parseName()
|
||||
} else {
|
||||
field.Name = field.Alias
|
||||
}
|
||||
|
||||
field.Arguments = p.parseArguments(false)
|
||||
field.Directives = p.parseDirectives(false)
|
||||
if p.peek().Kind == lexer.BraceL {
|
||||
field.SelectionSet = p.parseOptionalSelectionSet()
|
||||
}
|
||||
|
||||
return &field
|
||||
}
|
||||
|
||||
func (p *parser) parseArguments(isConst bool) ArgumentList {
|
||||
var arguments ArgumentList
|
||||
p.many(lexer.ParenL, lexer.ParenR, func() {
|
||||
arguments = append(arguments, p.parseArgument(isConst))
|
||||
})
|
||||
|
||||
return arguments
|
||||
}
|
||||
|
||||
func (p *parser) parseArgument(isConst bool) *Argument {
|
||||
arg := Argument{}
|
||||
arg.Position = p.peekPos()
|
||||
arg.Name = p.parseName()
|
||||
p.expect(lexer.Colon)
|
||||
|
||||
arg.Value = p.parseValueLiteral(isConst)
|
||||
return &arg
|
||||
}
|
||||
|
||||
func (p *parser) parseFragment() Selection {
|
||||
p.expect(lexer.Spread)
|
||||
|
||||
if peek := p.peek(); peek.Kind == lexer.Name && peek.Value != "on" {
|
||||
return &FragmentSpread{
|
||||
Position: p.peekPos(),
|
||||
Name: p.parseFragmentName(),
|
||||
Directives: p.parseDirectives(false),
|
||||
}
|
||||
}
|
||||
|
||||
var def InlineFragment
|
||||
def.Position = p.peekPos()
|
||||
if p.peek().Value == "on" {
|
||||
p.next() // "on"
|
||||
|
||||
def.TypeCondition = p.parseName()
|
||||
}
|
||||
|
||||
def.Directives = p.parseDirectives(false)
|
||||
def.SelectionSet = p.parseRequiredSelectionSet()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseFragmentDefinition() *FragmentDefinition {
|
||||
var def FragmentDefinition
|
||||
def.Position = p.peekPos()
|
||||
p.expectKeyword("fragment")
|
||||
|
||||
def.Name = p.parseFragmentName()
|
||||
def.VariableDefinition = p.parseVariableDefinitions()
|
||||
|
||||
p.expectKeyword("on")
|
||||
|
||||
def.TypeCondition = p.parseName()
|
||||
def.Directives = p.parseDirectives(false)
|
||||
def.SelectionSet = p.parseRequiredSelectionSet()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseFragmentName() string {
|
||||
if p.peek().Value == "on" {
|
||||
p.unexpectedError()
|
||||
return ""
|
||||
}
|
||||
|
||||
return p.parseName()
|
||||
}
|
||||
|
||||
func (p *parser) parseValueLiteral(isConst bool) *Value {
|
||||
token := p.peek()
|
||||
|
||||
var kind ValueKind
|
||||
switch token.Kind {
|
||||
case lexer.BracketL:
|
||||
return p.parseList(isConst)
|
||||
case lexer.BraceL:
|
||||
return p.parseObject(isConst)
|
||||
case lexer.Dollar:
|
||||
if isConst {
|
||||
p.unexpectedError()
|
||||
return nil
|
||||
}
|
||||
return &Value{Position: &token.Pos, Raw: p.parseVariable(), Kind: Variable}
|
||||
case lexer.Int:
|
||||
kind = IntValue
|
||||
case lexer.Float:
|
||||
kind = FloatValue
|
||||
case lexer.String:
|
||||
kind = StringValue
|
||||
case lexer.BlockString:
|
||||
kind = BlockValue
|
||||
case lexer.Name:
|
||||
switch token.Value {
|
||||
case "true", "false":
|
||||
kind = BooleanValue
|
||||
case "null":
|
||||
kind = NullValue
|
||||
default:
|
||||
kind = EnumValue
|
||||
}
|
||||
default:
|
||||
p.unexpectedError()
|
||||
return nil
|
||||
}
|
||||
|
||||
p.next()
|
||||
|
||||
return &Value{Position: &token.Pos, Raw: token.Value, Kind: kind}
|
||||
}
|
||||
|
||||
func (p *parser) parseList(isConst bool) *Value {
|
||||
var values ChildValueList
|
||||
pos := p.peekPos()
|
||||
p.many(lexer.BracketL, lexer.BracketR, func() {
|
||||
values = append(values, &ChildValue{Value: p.parseValueLiteral(isConst)})
|
||||
})
|
||||
|
||||
return &Value{Children: values, Kind: ListValue, Position: pos}
|
||||
}
|
||||
|
||||
func (p *parser) parseObject(isConst bool) *Value {
|
||||
var fields ChildValueList
|
||||
pos := p.peekPos()
|
||||
p.many(lexer.BraceL, lexer.BraceR, func() {
|
||||
fields = append(fields, p.parseObjectField(isConst))
|
||||
})
|
||||
|
||||
return &Value{Children: fields, Kind: ObjectValue, Position: pos}
|
||||
}
|
||||
|
||||
func (p *parser) parseObjectField(isConst bool) *ChildValue {
|
||||
field := ChildValue{}
|
||||
field.Position = p.peekPos()
|
||||
field.Name = p.parseName()
|
||||
|
||||
p.expect(lexer.Colon)
|
||||
|
||||
field.Value = p.parseValueLiteral(isConst)
|
||||
return &field
|
||||
}
|
||||
|
||||
func (p *parser) parseDirectives(isConst bool) []*Directive {
|
||||
var directives []*Directive
|
||||
|
||||
for p.peek().Kind == lexer.At {
|
||||
if p.err != nil {
|
||||
break
|
||||
}
|
||||
directives = append(directives, p.parseDirective(isConst))
|
||||
}
|
||||
return directives
|
||||
}
|
||||
|
||||
func (p *parser) parseDirective(isConst bool) *Directive {
|
||||
p.expect(lexer.At)
|
||||
|
||||
return &Directive{
|
||||
Position: p.peekPos(),
|
||||
Name: p.parseName(),
|
||||
Arguments: p.parseArguments(isConst),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseTypeReference() *Type {
|
||||
var typ Type
|
||||
|
||||
if p.skip(lexer.BracketL) {
|
||||
typ.Position = p.peekPos()
|
||||
typ.Elem = p.parseTypeReference()
|
||||
p.expect(lexer.BracketR)
|
||||
} else {
|
||||
typ.Position = p.peekPos()
|
||||
typ.NamedType = p.parseName()
|
||||
}
|
||||
|
||||
if p.skip(lexer.Bang) {
|
||||
typ.NonNull = true
|
||||
}
|
||||
return &typ
|
||||
}
|
||||
|
||||
func (p *parser) parseName() string {
|
||||
token := p.expect(lexer.Name)
|
||||
|
||||
return token.Value
|
||||
}
|
||||
-544
@@ -1,544 +0,0 @@
|
||||
parser provides useful errors:
|
||||
- name: unclosed paren
|
||||
input: '{'
|
||||
error:
|
||||
message: "Expected Name, found <EOF>"
|
||||
locations: [{line: 1, column: 2}]
|
||||
|
||||
- name: missing on in fragment
|
||||
input: |
|
||||
{ ...MissingOn }
|
||||
fragment MissingOn Type
|
||||
error:
|
||||
message: 'Expected "on", found Name "Type"'
|
||||
locations: [{ line: 2, column: 20 }]
|
||||
|
||||
- name: missing name after alias
|
||||
input: '{ field: {} }'
|
||||
error:
|
||||
message: "Expected Name, found {"
|
||||
locations: [{ line: 1, column: 10 }]
|
||||
|
||||
- name: not an operation
|
||||
input: 'notanoperation Foo { field }'
|
||||
error:
|
||||
message: 'Unexpected Name "notanoperation"'
|
||||
locations: [{ line: 1, column: 1 }]
|
||||
|
||||
- name: a wild splat appears
|
||||
input: '...'
|
||||
error:
|
||||
message: 'Unexpected ...'
|
||||
locations: [{ line: 1, column: 1}]
|
||||
|
||||
variables:
|
||||
- name: are allowed in args
|
||||
input: '{ field(complex: { a: { b: [ $var ] } }) }'
|
||||
|
||||
- name: are not allowed in default args
|
||||
input: 'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }'
|
||||
error:
|
||||
message: 'Unexpected $'
|
||||
locations: [{ line: 1, column: 37 }]
|
||||
|
||||
- name: can have directives
|
||||
input: 'query ($withDirective: String @first @second, $withoutDirective: String) { f }'
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
VariableDefinitions: [VariableDefinition]
|
||||
- <VariableDefinition>
|
||||
Variable: "withDirective"
|
||||
Type: String
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "first"
|
||||
- <Directive>
|
||||
Name: "second"
|
||||
- <VariableDefinition>
|
||||
Variable: "withoutDirective"
|
||||
Type: String
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "f"
|
||||
Name: "f"
|
||||
|
||||
fragments:
|
||||
- name: can not be named 'on'
|
||||
input: 'fragment on on on { on }'
|
||||
error:
|
||||
message: 'Unexpected Name "on"'
|
||||
locations: [{ line: 1, column: 10 }]
|
||||
|
||||
- name: can not spread fragments called 'on'
|
||||
input: '{ ...on }'
|
||||
error:
|
||||
message: 'Expected Name, found }'
|
||||
locations: [{ line: 1, column: 9 }]
|
||||
|
||||
encoding:
|
||||
- name: multibyte characters are supported
|
||||
input: |
|
||||
# This comment has a ਊ multi-byte character.
|
||||
{ field(arg: "Has a ਊ multi-byte character.") }
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "field"
|
||||
Name: "field"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "arg"
|
||||
Value: "Has a ਊ multi-byte character."
|
||||
|
||||
keywords are allowed anywhere a name is:
|
||||
- name: on
|
||||
input: |
|
||||
query on {
|
||||
... a
|
||||
... on on { field }
|
||||
}
|
||||
fragment a on Type {
|
||||
on(on: $on)
|
||||
@on(on: on)
|
||||
}
|
||||
|
||||
- name: subscription
|
||||
input: |
|
||||
query subscription {
|
||||
... subscription
|
||||
... on subscription { field }
|
||||
}
|
||||
fragment subscription on Type {
|
||||
subscription(subscription: $subscription)
|
||||
@subscription(subscription: subscription)
|
||||
}
|
||||
|
||||
- name: true
|
||||
input: |
|
||||
query true {
|
||||
... true
|
||||
... on true { field }
|
||||
}
|
||||
fragment true on Type {
|
||||
true(true: $true)
|
||||
@true(true: true)
|
||||
}
|
||||
|
||||
operations:
|
||||
- name: anonymous mutation
|
||||
input: 'mutation { mutationField }'
|
||||
|
||||
- name: named mutation
|
||||
input: 'mutation Foo { mutationField }'
|
||||
|
||||
- name: anonymous subscription
|
||||
input: 'subscription { subscriptionField }'
|
||||
|
||||
- name: named subscription
|
||||
input: 'subscription Foo { subscriptionField }'
|
||||
|
||||
|
||||
ast:
|
||||
- name: simple query
|
||||
input: |
|
||||
{
|
||||
node(id: 4) {
|
||||
id,
|
||||
name
|
||||
}
|
||||
}
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "node"
|
||||
Name: "node"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "id"
|
||||
Value: 4
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
- <Field>
|
||||
Alias: "name"
|
||||
Name: "name"
|
||||
|
||||
- name: nameless query with no variables
|
||||
input: |
|
||||
query {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "node"
|
||||
Name: "node"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
|
||||
- name: fragment defined variables
|
||||
input: 'fragment a($v: Boolean = false) on t { f(v: $v) }'
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Fragments: [FragmentDefinition]
|
||||
- <FragmentDefinition>
|
||||
Name: "a"
|
||||
VariableDefinition: [VariableDefinition]
|
||||
- <VariableDefinition>
|
||||
Variable: "v"
|
||||
Type: Boolean
|
||||
DefaultValue: false
|
||||
TypeCondition: "t"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "f"
|
||||
Name: "f"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "v"
|
||||
Value: $v
|
||||
|
||||
|
||||
values:
|
||||
- name: null
|
||||
input: '{ f(id: null) }'
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "f"
|
||||
Name: "f"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "id"
|
||||
Value: null
|
||||
|
||||
- name: strings
|
||||
input: '{ f(long: """long""", short: "short") } '
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "f"
|
||||
Name: "f"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "long"
|
||||
Value: "long"
|
||||
- <Argument>
|
||||
Name: "short"
|
||||
Value: "short"
|
||||
|
||||
- name: list
|
||||
input: '{ f(id: [1,2]) }'
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "f"
|
||||
Name: "f"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "id"
|
||||
Value: [1,2]
|
||||
|
||||
types:
|
||||
- name: common types
|
||||
input: 'query ($string: String, $int: Int, $arr: [Arr], $notnull: [Arr!]!) { f }'
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
VariableDefinitions: [VariableDefinition]
|
||||
- <VariableDefinition>
|
||||
Variable: "string"
|
||||
Type: String
|
||||
- <VariableDefinition>
|
||||
Variable: "int"
|
||||
Type: Int
|
||||
- <VariableDefinition>
|
||||
Variable: "arr"
|
||||
Type: [Arr]
|
||||
- <VariableDefinition>
|
||||
Variable: "notnull"
|
||||
Type: [Arr!]!
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "f"
|
||||
Name: "f"
|
||||
|
||||
large queries:
|
||||
- name: kitchen sink
|
||||
input: |
|
||||
# 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 ,
|
||||
... on User @defer {
|
||||
field2 {
|
||||
id ,
|
||||
alias: field1(first:10, after:$foo,) @include(if: $foo) {
|
||||
id,
|
||||
...frag
|
||||
}
|
||||
}
|
||||
}
|
||||
... @skip(unless: $foo) {
|
||||
id
|
||||
}
|
||||
... {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation likeStory {
|
||||
like(story: 123) @defer {
|
||||
story {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
|
||||
storyLikeSubscribe(input: $input) {
|
||||
story {
|
||||
likers {
|
||||
count
|
||||
}
|
||||
likeSentence {
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment frag on Friend {
|
||||
foo(size: $size, bar: $b, obj: {key: "value", block: """
|
||||
block string uses \"""
|
||||
"""})
|
||||
}
|
||||
|
||||
{
|
||||
unnamed(truthy: true, falsey: false, nullish: null),
|
||||
query
|
||||
}
|
||||
ast: |
|
||||
<QueryDocument>
|
||||
Operations: [OperationDefinition]
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
Name: "queryName"
|
||||
VariableDefinitions: [VariableDefinition]
|
||||
- <VariableDefinition>
|
||||
Variable: "foo"
|
||||
Type: ComplexType
|
||||
- <VariableDefinition>
|
||||
Variable: "site"
|
||||
Type: Site
|
||||
DefaultValue: MOBILE
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "whoever123is"
|
||||
Name: "node"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "id"
|
||||
Value: [123,456]
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
- <InlineFragment>
|
||||
TypeCondition: "User"
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "defer"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "field2"
|
||||
Name: "field2"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
- <Field>
|
||||
Alias: "alias"
|
||||
Name: "field1"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "first"
|
||||
Value: 10
|
||||
- <Argument>
|
||||
Name: "after"
|
||||
Value: $foo
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "include"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "if"
|
||||
Value: $foo
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
- <FragmentSpread>
|
||||
Name: "frag"
|
||||
- <InlineFragment>
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "skip"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "unless"
|
||||
Value: $foo
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
- <InlineFragment>
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("mutation")
|
||||
Name: "likeStory"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "like"
|
||||
Name: "like"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "story"
|
||||
Value: 123
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "defer"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "story"
|
||||
Name: "story"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "id"
|
||||
Name: "id"
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("subscription")
|
||||
Name: "StoryLikeSubscription"
|
||||
VariableDefinitions: [VariableDefinition]
|
||||
- <VariableDefinition>
|
||||
Variable: "input"
|
||||
Type: StoryLikeSubscribeInput
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "storyLikeSubscribe"
|
||||
Name: "storyLikeSubscribe"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "input"
|
||||
Value: $input
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "story"
|
||||
Name: "story"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "likers"
|
||||
Name: "likers"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "count"
|
||||
Name: "count"
|
||||
- <Field>
|
||||
Alias: "likeSentence"
|
||||
Name: "likeSentence"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "text"
|
||||
Name: "text"
|
||||
- <OperationDefinition>
|
||||
Operation: Operation("query")
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "unnamed"
|
||||
Name: "unnamed"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "truthy"
|
||||
Value: true
|
||||
- <Argument>
|
||||
Name: "falsey"
|
||||
Value: false
|
||||
- <Argument>
|
||||
Name: "nullish"
|
||||
Value: null
|
||||
- <Field>
|
||||
Alias: "query"
|
||||
Name: "query"
|
||||
Fragments: [FragmentDefinition]
|
||||
- <FragmentDefinition>
|
||||
Name: "frag"
|
||||
TypeCondition: "Friend"
|
||||
SelectionSet: [Selection]
|
||||
- <Field>
|
||||
Alias: "foo"
|
||||
Name: "foo"
|
||||
Arguments: [Argument]
|
||||
- <Argument>
|
||||
Name: "size"
|
||||
Value: $size
|
||||
- <Argument>
|
||||
Name: "bar"
|
||||
Value: $b
|
||||
- <Argument>
|
||||
Name: "obj"
|
||||
Value: {key:"value",block:"block string uses \"\"\""}
|
||||
|
||||
fuzzer:
|
||||
- name: 01
|
||||
input: '{__typename{...}}'
|
||||
error:
|
||||
message: 'Expected {, found }'
|
||||
locations: [{ line: 1, column: 16 }]
|
||||
|
||||
- name: 02
|
||||
input: '{...{__typename{...{}}}}'
|
||||
error:
|
||||
message: 'expected at least one definition, found }'
|
||||
locations: [{ line: 1, column: 21 }]
|
||||
-535
@@ -1,535 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
//nolint:revive
|
||||
. "github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/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{}
|
||||
for _, input := range inputs {
|
||||
inputAst, err := ParseSchema(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ast.Merge(inputAst)
|
||||
}
|
||||
return ast, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseSchemaDocument() *SchemaDocument {
|
||||
var doc SchemaDocument
|
||||
doc.Position = p.peekPos()
|
||||
for p.peek().Kind != lexer.EOF {
|
||||
if p.err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var description string
|
||||
if p.peek().Kind == lexer.BlockString || p.peek().Kind == lexer.String {
|
||||
description = p.parseDescription()
|
||||
}
|
||||
|
||||
if p.peek().Kind != lexer.Name {
|
||||
p.unexpectedError()
|
||||
break
|
||||
}
|
||||
|
||||
switch p.peek().Value {
|
||||
case "scalar", "type", "interface", "union", "enum", "input":
|
||||
doc.Definitions = append(doc.Definitions, p.parseTypeSystemDefinition(description))
|
||||
case "schema":
|
||||
doc.Schema = append(doc.Schema, p.parseSchemaDefinition(description))
|
||||
case "directive":
|
||||
doc.Directives = append(doc.Directives, p.parseDirectiveDefinition(description))
|
||||
case "extend":
|
||||
if description != "" {
|
||||
p.unexpectedToken(p.prev)
|
||||
}
|
||||
p.parseTypeSystemExtension(&doc)
|
||||
default:
|
||||
p.unexpectedError()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return &doc
|
||||
}
|
||||
|
||||
func (p *parser) parseDescription() string {
|
||||
token := p.peek()
|
||||
|
||||
if token.Kind != lexer.BlockString && token.Kind != lexer.String {
|
||||
return ""
|
||||
}
|
||||
|
||||
return p.next().Value
|
||||
}
|
||||
|
||||
func (p *parser) parseTypeSystemDefinition(description string) *Definition {
|
||||
tok := p.peek()
|
||||
if tok.Kind != lexer.Name {
|
||||
p.unexpectedError()
|
||||
return nil
|
||||
}
|
||||
|
||||
switch tok.Value {
|
||||
case "scalar":
|
||||
return p.parseScalarTypeDefinition(description)
|
||||
case "type":
|
||||
return p.parseObjectTypeDefinition(description)
|
||||
case "interface":
|
||||
return p.parseInterfaceTypeDefinition(description)
|
||||
case "union":
|
||||
return p.parseUnionTypeDefinition(description)
|
||||
case "enum":
|
||||
return p.parseEnumTypeDefinition(description)
|
||||
case "input":
|
||||
return p.parseInputObjectTypeDefinition(description)
|
||||
default:
|
||||
p.unexpectedError()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseSchemaDefinition(description string) *SchemaDefinition {
|
||||
p.expectKeyword("schema")
|
||||
|
||||
def := SchemaDefinition{Description: description}
|
||||
def.Position = p.peekPos()
|
||||
def.Description = description
|
||||
def.Directives = p.parseDirectives(true)
|
||||
|
||||
p.some(lexer.BraceL, lexer.BraceR, func() {
|
||||
def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
|
||||
})
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseOperationTypeDefinition() *OperationTypeDefinition {
|
||||
var op OperationTypeDefinition
|
||||
op.Position = p.peekPos()
|
||||
op.Operation = p.parseOperationType()
|
||||
p.expect(lexer.Colon)
|
||||
op.Type = p.parseName()
|
||||
return &op
|
||||
}
|
||||
|
||||
func (p *parser) parseScalarTypeDefinition(description string) *Definition {
|
||||
p.expectKeyword("scalar")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
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")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Object
|
||||
def.Description = description
|
||||
def.Name = p.parseName()
|
||||
def.Interfaces = p.parseImplementsInterfaces()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.Fields = p.parseFieldsDefinition()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseImplementsInterfaces() []string {
|
||||
var types []string
|
||||
if p.peek().Value == "implements" {
|
||||
p.next()
|
||||
// optional leading ampersand
|
||||
p.skip(lexer.Amp)
|
||||
|
||||
types = append(types, p.parseName())
|
||||
for p.skip(lexer.Amp) && p.err == nil {
|
||||
types = append(types, p.parseName())
|
||||
}
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func (p *parser) parseFieldsDefinition() FieldList {
|
||||
var defs FieldList
|
||||
p.some(lexer.BraceL, lexer.BraceR, func() {
|
||||
defs = append(defs, p.parseFieldDefinition())
|
||||
})
|
||||
return defs
|
||||
}
|
||||
|
||||
func (p *parser) parseFieldDefinition() *FieldDefinition {
|
||||
var def FieldDefinition
|
||||
def.Position = p.peekPos()
|
||||
def.Description = p.parseDescription()
|
||||
def.Name = p.parseName()
|
||||
def.Arguments = p.parseArgumentDefs()
|
||||
p.expect(lexer.Colon)
|
||||
def.Type = p.parseTypeReference()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseArgumentDefs() ArgumentDefinitionList {
|
||||
var args ArgumentDefinitionList
|
||||
p.some(lexer.ParenL, lexer.ParenR, func() {
|
||||
args = append(args, p.parseArgumentDef())
|
||||
})
|
||||
return args
|
||||
}
|
||||
|
||||
func (p *parser) parseArgumentDef() *ArgumentDefinition {
|
||||
var def ArgumentDefinition
|
||||
def.Position = p.peekPos()
|
||||
def.Description = p.parseDescription()
|
||||
def.Name = p.parseName()
|
||||
p.expect(lexer.Colon)
|
||||
def.Type = p.parseTypeReference()
|
||||
if p.skip(lexer.Equals) {
|
||||
def.DefaultValue = p.parseValueLiteral(true)
|
||||
}
|
||||
def.Directives = p.parseDirectives(true)
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseInputValueDef() *FieldDefinition {
|
||||
var def FieldDefinition
|
||||
def.Position = p.peekPos()
|
||||
def.Description = p.parseDescription()
|
||||
def.Name = p.parseName()
|
||||
p.expect(lexer.Colon)
|
||||
def.Type = p.parseTypeReference()
|
||||
if p.skip(lexer.Equals) {
|
||||
def.DefaultValue = p.parseValueLiteral(true)
|
||||
}
|
||||
def.Directives = p.parseDirectives(true)
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseInterfaceTypeDefinition(description string) *Definition {
|
||||
p.expectKeyword("interface")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Interface
|
||||
def.Description = description
|
||||
def.Name = p.parseName()
|
||||
def.Interfaces = p.parseImplementsInterfaces()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.Fields = p.parseFieldsDefinition()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseUnionTypeDefinition(description string) *Definition {
|
||||
p.expectKeyword("union")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Union
|
||||
def.Description = description
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.Types = p.parseUnionMemberTypes()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseUnionMemberTypes() []string {
|
||||
var types []string
|
||||
if p.skip(lexer.Equals) {
|
||||
// optional leading pipe
|
||||
p.skip(lexer.Pipe)
|
||||
|
||||
types = append(types, p.parseName())
|
||||
for p.skip(lexer.Pipe) && p.err == nil {
|
||||
types = append(types, p.parseName())
|
||||
}
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func (p *parser) parseEnumTypeDefinition(description string) *Definition {
|
||||
p.expectKeyword("enum")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Enum
|
||||
def.Description = description
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.EnumValues = p.parseEnumValuesDefinition()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseEnumValuesDefinition() EnumValueList {
|
||||
var values EnumValueList
|
||||
p.some(lexer.BraceL, lexer.BraceR, func() {
|
||||
values = append(values, p.parseEnumValueDefinition())
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
func (p *parser) parseEnumValueDefinition() *EnumValueDefinition {
|
||||
return &EnumValueDefinition{
|
||||
Position: p.peekPos(),
|
||||
Description: p.parseDescription(),
|
||||
Name: p.parseName(),
|
||||
Directives: p.parseDirectives(true),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseInputObjectTypeDefinition(description string) *Definition {
|
||||
p.expectKeyword("input")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = InputObject
|
||||
def.Description = description
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.Fields = p.parseInputFieldsDefinition()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseInputFieldsDefinition() FieldList {
|
||||
var values FieldList
|
||||
p.some(lexer.BraceL, lexer.BraceR, func() {
|
||||
values = append(values, p.parseInputValueDef())
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
func (p *parser) parseTypeSystemExtension(doc *SchemaDocument) {
|
||||
p.expectKeyword("extend")
|
||||
|
||||
switch p.peek().Value {
|
||||
case "schema":
|
||||
doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension())
|
||||
case "scalar":
|
||||
doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension())
|
||||
case "type":
|
||||
doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension())
|
||||
case "interface":
|
||||
doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension())
|
||||
case "union":
|
||||
doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension())
|
||||
case "enum":
|
||||
doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension())
|
||||
case "input":
|
||||
doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension())
|
||||
default:
|
||||
p.unexpectedError()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseSchemaExtension() *SchemaDefinition {
|
||||
p.expectKeyword("schema")
|
||||
|
||||
var def SchemaDefinition
|
||||
def.Position = p.peekPos()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
p.some(lexer.BraceL, lexer.BraceR, func() {
|
||||
def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
|
||||
})
|
||||
if len(def.Directives) == 0 && len(def.OperationTypes) == 0 {
|
||||
p.unexpectedError()
|
||||
}
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseScalarTypeExtension() *Definition {
|
||||
p.expectKeyword("scalar")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Scalar
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
if len(def.Directives) == 0 {
|
||||
p.unexpectedError()
|
||||
}
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseObjectTypeExtension() *Definition {
|
||||
p.expectKeyword("type")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Object
|
||||
def.Name = p.parseName()
|
||||
def.Interfaces = p.parseImplementsInterfaces()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.Fields = p.parseFieldsDefinition()
|
||||
if len(def.Interfaces) == 0 && len(def.Directives) == 0 && len(def.Fields) == 0 {
|
||||
p.unexpectedError()
|
||||
}
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseInterfaceTypeExtension() *Definition {
|
||||
p.expectKeyword("interface")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Interface
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.Fields = p.parseFieldsDefinition()
|
||||
if len(def.Directives) == 0 && len(def.Fields) == 0 {
|
||||
p.unexpectedError()
|
||||
}
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseUnionTypeExtension() *Definition {
|
||||
p.expectKeyword("union")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Union
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.Types = p.parseUnionMemberTypes()
|
||||
|
||||
if len(def.Directives) == 0 && len(def.Types) == 0 {
|
||||
p.unexpectedError()
|
||||
}
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseEnumTypeExtension() *Definition {
|
||||
p.expectKeyword("enum")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = Enum
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(true)
|
||||
def.EnumValues = p.parseEnumValuesDefinition()
|
||||
if len(def.Directives) == 0 && len(def.EnumValues) == 0 {
|
||||
p.unexpectedError()
|
||||
}
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseInputObjectTypeExtension() *Definition {
|
||||
p.expectKeyword("input")
|
||||
|
||||
var def Definition
|
||||
def.Position = p.peekPos()
|
||||
def.Kind = InputObject
|
||||
def.Name = p.parseName()
|
||||
def.Directives = p.parseDirectives(false)
|
||||
def.Fields = 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")
|
||||
p.expect(lexer.At)
|
||||
|
||||
var def DirectiveDefinition
|
||||
def.Position = p.peekPos()
|
||||
def.Description = description
|
||||
def.Name = p.parseName()
|
||||
def.Arguments = p.parseArgumentDefs()
|
||||
|
||||
if peek := p.peek(); peek.Kind == lexer.Name && peek.Value == "repeatable" {
|
||||
def.IsRepeatable = true
|
||||
p.skip(lexer.Name)
|
||||
}
|
||||
|
||||
p.expectKeyword("on")
|
||||
def.Locations = p.parseDirectiveLocations()
|
||||
return &def
|
||||
}
|
||||
|
||||
func (p *parser) parseDirectiveLocations() []DirectiveLocation {
|
||||
p.skip(lexer.Pipe)
|
||||
|
||||
locations := []DirectiveLocation{p.parseDirectiveLocation()}
|
||||
|
||||
for p.skip(lexer.Pipe) && p.err == nil {
|
||||
locations = append(locations, p.parseDirectiveLocation())
|
||||
}
|
||||
|
||||
return locations
|
||||
}
|
||||
|
||||
func (p *parser) parseDirectiveLocation() DirectiveLocation {
|
||||
name := p.expect(lexer.Name)
|
||||
|
||||
switch name.Value {
|
||||
case `QUERY`:
|
||||
return LocationQuery
|
||||
case `MUTATION`:
|
||||
return LocationMutation
|
||||
case `SUBSCRIPTION`:
|
||||
return LocationSubscription
|
||||
case `FIELD`:
|
||||
return LocationField
|
||||
case `FRAGMENT_DEFINITION`:
|
||||
return LocationFragmentDefinition
|
||||
case `FRAGMENT_SPREAD`:
|
||||
return LocationFragmentSpread
|
||||
case `INLINE_FRAGMENT`:
|
||||
return LocationInlineFragment
|
||||
case `VARIABLE_DEFINITION`:
|
||||
return LocationVariableDefinition
|
||||
case `SCHEMA`:
|
||||
return LocationSchema
|
||||
case `SCALAR`:
|
||||
return LocationScalar
|
||||
case `OBJECT`:
|
||||
return LocationObject
|
||||
case `FIELD_DEFINITION`:
|
||||
return LocationFieldDefinition
|
||||
case `ARGUMENT_DEFINITION`:
|
||||
return LocationArgumentDefinition
|
||||
case `INTERFACE`:
|
||||
return LocationInterface
|
||||
case `UNION`:
|
||||
return LocationUnion
|
||||
case `ENUM`:
|
||||
return LocationEnum
|
||||
case `ENUM_VALUE`:
|
||||
return LocationEnumValue
|
||||
case `INPUT_OBJECT`:
|
||||
return LocationInputObject
|
||||
case `INPUT_FIELD_DEFINITION`:
|
||||
return LocationInputFieldDefinition
|
||||
}
|
||||
|
||||
p.unexpectedToken(name)
|
||||
return ""
|
||||
}
|
||||
Generated
Vendored
-646
@@ -1,646 +0,0 @@
|
||||
object types:
|
||||
- name: simple
|
||||
input: |
|
||||
type Hello {
|
||||
world: String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Type: String
|
||||
|
||||
- name: with description
|
||||
input: |
|
||||
"Description"
|
||||
type Hello {
|
||||
world: String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Description: "Description"
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Type: String
|
||||
|
||||
- name: with block description
|
||||
input: |
|
||||
"""
|
||||
Description
|
||||
"""
|
||||
# Even with comments between them
|
||||
type Hello {
|
||||
world: String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Description: "Description"
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Type: String
|
||||
- name: with field arg
|
||||
input: |
|
||||
type Hello {
|
||||
world(flag: Boolean): String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Arguments: [ArgumentDefinition]
|
||||
- <ArgumentDefinition>
|
||||
Name: "flag"
|
||||
Type: Boolean
|
||||
Type: String
|
||||
|
||||
- name: with field arg and default value
|
||||
input: |
|
||||
type Hello {
|
||||
world(flag: Boolean = true): String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Arguments: [ArgumentDefinition]
|
||||
- <ArgumentDefinition>
|
||||
Name: "flag"
|
||||
DefaultValue: true
|
||||
Type: Boolean
|
||||
Type: String
|
||||
|
||||
- name: with field list arg
|
||||
input: |
|
||||
type Hello {
|
||||
world(things: [String]): String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Arguments: [ArgumentDefinition]
|
||||
- <ArgumentDefinition>
|
||||
Name: "things"
|
||||
Type: [String]
|
||||
Type: String
|
||||
|
||||
- name: with two args
|
||||
input: |
|
||||
type Hello {
|
||||
world(argOne: Boolean, argTwo: Int): String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Arguments: [ArgumentDefinition]
|
||||
- <ArgumentDefinition>
|
||||
Name: "argOne"
|
||||
Type: Boolean
|
||||
- <ArgumentDefinition>
|
||||
Name: "argTwo"
|
||||
Type: Int
|
||||
Type: String
|
||||
- name: must define one or more fields
|
||||
input: |
|
||||
type Hello {}
|
||||
error:
|
||||
message: "expected at least one definition, found }"
|
||||
locations: [{ line: 1, column: 13 }]
|
||||
|
||||
type extensions:
|
||||
- name: Object extension
|
||||
input: |
|
||||
extend type Hello {
|
||||
world: String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Extensions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Type: String
|
||||
|
||||
- name: without any fields
|
||||
input: "extend type Hello implements Greeting"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Extensions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Interfaces: [string]
|
||||
- "Greeting"
|
||||
|
||||
- name: without fields twice
|
||||
input: |
|
||||
extend type Hello implements Greeting
|
||||
extend type Hello implements SecondGreeting
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Extensions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Interfaces: [string]
|
||||
- "Greeting"
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Interfaces: [string]
|
||||
- "SecondGreeting"
|
||||
|
||||
- name: without anything errors
|
||||
input: "extend type Hello"
|
||||
error:
|
||||
message: "Unexpected <EOF>"
|
||||
locations: [{ line: 1, column: 18 }]
|
||||
|
||||
- name: can have descriptions # hmm, this might not be spec compliant...
|
||||
input: |
|
||||
"Description"
|
||||
extend type Hello {
|
||||
world: String
|
||||
}
|
||||
error:
|
||||
message: 'Unexpected String "Description"'
|
||||
locations: [{ line: 1, column: 2 }]
|
||||
|
||||
- name: can not have descriptions on types
|
||||
input: |
|
||||
extend "Description" type Hello {
|
||||
world: String
|
||||
}
|
||||
error:
|
||||
message: Unexpected String "Description"
|
||||
locations: [{ line: 1, column: 9 }]
|
||||
|
||||
- name: all can have directives
|
||||
input: |
|
||||
extend scalar Foo @deprecated
|
||||
extend type Foo @deprecated
|
||||
extend interface Foo @deprecated
|
||||
extend union Foo @deprecated
|
||||
extend enum Foo @deprecated
|
||||
extend input Foo @deprecated
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Extensions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("SCALAR")
|
||||
Name: "Foo"
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "deprecated"
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Foo"
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "deprecated"
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("INTERFACE")
|
||||
Name: "Foo"
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "deprecated"
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("UNION")
|
||||
Name: "Foo"
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "deprecated"
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("ENUM")
|
||||
Name: "Foo"
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "deprecated"
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("INPUT_OBJECT")
|
||||
Name: "Foo"
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "deprecated"
|
||||
|
||||
schema definition:
|
||||
- name: simple
|
||||
input: |
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Schema: [SchemaDefinition]
|
||||
- <SchemaDefinition>
|
||||
OperationTypes: [OperationTypeDefinition]
|
||||
- <OperationTypeDefinition>
|
||||
Operation: Operation("query")
|
||||
Type: "Query"
|
||||
|
||||
schema extensions:
|
||||
- name: simple
|
||||
input: |
|
||||
extend schema {
|
||||
mutation: Mutation
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
SchemaExtension: [SchemaDefinition]
|
||||
- <SchemaDefinition>
|
||||
OperationTypes: [OperationTypeDefinition]
|
||||
- <OperationTypeDefinition>
|
||||
Operation: Operation("mutation")
|
||||
Type: "Mutation"
|
||||
|
||||
- name: directive only
|
||||
input: "extend schema @directive"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
SchemaExtension: [SchemaDefinition]
|
||||
- <SchemaDefinition>
|
||||
Directives: [Directive]
|
||||
- <Directive>
|
||||
Name: "directive"
|
||||
|
||||
- name: without anything errors
|
||||
input: "extend schema"
|
||||
error:
|
||||
message: "Unexpected <EOF>"
|
||||
locations: [{ line: 1, column: 14}]
|
||||
|
||||
inheritance:
|
||||
- name: single
|
||||
input: "type Hello implements World { field: String }"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Interfaces: [string]
|
||||
- "World"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "field"
|
||||
Type: String
|
||||
|
||||
- name: multi
|
||||
input: "type Hello implements Wo & rld { field: String }"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Interfaces: [string]
|
||||
- "Wo"
|
||||
- "rld"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "field"
|
||||
Type: String
|
||||
|
||||
- name: multi with leading amp
|
||||
input: "type Hello implements & Wo & rld { field: String }"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "Hello"
|
||||
Interfaces: [string]
|
||||
- "Wo"
|
||||
- "rld"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "field"
|
||||
Type: String
|
||||
|
||||
enums:
|
||||
- name: single value
|
||||
input: "enum Hello { WORLD }"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("ENUM")
|
||||
Name: "Hello"
|
||||
EnumValues: [EnumValueDefinition]
|
||||
- <EnumValueDefinition>
|
||||
Name: "WORLD"
|
||||
|
||||
- name: double value
|
||||
input: "enum Hello { WO, RLD }"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("ENUM")
|
||||
Name: "Hello"
|
||||
EnumValues: [EnumValueDefinition]
|
||||
- <EnumValueDefinition>
|
||||
Name: "WO"
|
||||
- <EnumValueDefinition>
|
||||
Name: "RLD"
|
||||
- name: must define one or more unique enum values
|
||||
input: |
|
||||
enum Hello {}
|
||||
error:
|
||||
message: "expected at least one definition, found }"
|
||||
locations: [{ line: 1, column: 13 }]
|
||||
|
||||
interface:
|
||||
- name: simple
|
||||
input: |
|
||||
interface Hello {
|
||||
world: String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("INTERFACE")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Type: String
|
||||
- name: must define one or more fields
|
||||
input: |
|
||||
interface Hello {}
|
||||
error:
|
||||
message: "expected at least one definition, found }"
|
||||
locations: [{ line: 1, column: 18 }]
|
||||
|
||||
- name: may define intermediate interfaces
|
||||
input: |
|
||||
interface IA {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
interface IIA implements IA {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type A implements IIA {
|
||||
id: ID!
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("INTERFACE")
|
||||
Name: "IA"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "id"
|
||||
Type: ID!
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("INTERFACE")
|
||||
Name: "IIA"
|
||||
Interfaces: [string]
|
||||
- "IA"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "id"
|
||||
Type: ID!
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("OBJECT")
|
||||
Name: "A"
|
||||
Interfaces: [string]
|
||||
- "IIA"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "id"
|
||||
Type: ID!
|
||||
|
||||
unions:
|
||||
- name: simple
|
||||
input: "union Hello = World"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("UNION")
|
||||
Name: "Hello"
|
||||
Types: [string]
|
||||
- "World"
|
||||
|
||||
- name: with two types
|
||||
input: "union Hello = Wo | Rld"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("UNION")
|
||||
Name: "Hello"
|
||||
Types: [string]
|
||||
- "Wo"
|
||||
- "Rld"
|
||||
|
||||
- name: with leading pipe
|
||||
input: "union Hello = | Wo | Rld"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("UNION")
|
||||
Name: "Hello"
|
||||
Types: [string]
|
||||
- "Wo"
|
||||
- "Rld"
|
||||
|
||||
- name: cant be empty
|
||||
input: "union Hello = || Wo | Rld"
|
||||
error:
|
||||
message: "Expected Name, found |"
|
||||
locations: [{ line: 1, column: 16 }]
|
||||
|
||||
- name: cant double pipe
|
||||
input: "union Hello = Wo || Rld"
|
||||
error:
|
||||
message: "Expected Name, found |"
|
||||
locations: [{ line: 1, column: 19 }]
|
||||
|
||||
- name: cant have trailing pipe
|
||||
input: "union Hello = | Wo | Rld |"
|
||||
error:
|
||||
message: "Expected Name, found <EOF>"
|
||||
locations: [{ line: 1, column: 27 }]
|
||||
|
||||
scalar:
|
||||
- name: simple
|
||||
input: "scalar Hello"
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("SCALAR")
|
||||
Name: "Hello"
|
||||
|
||||
input object:
|
||||
- name: simple
|
||||
input: |
|
||||
input Hello {
|
||||
world: String
|
||||
}
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Definitions: [Definition]
|
||||
- <Definition>
|
||||
Kind: DefinitionKind("INPUT_OBJECT")
|
||||
Name: "Hello"
|
||||
Fields: [FieldDefinition]
|
||||
- <FieldDefinition>
|
||||
Name: "world"
|
||||
Type: String
|
||||
|
||||
- name: can not have args
|
||||
input: |
|
||||
input Hello {
|
||||
world(foo: Int): String
|
||||
}
|
||||
error:
|
||||
message: "Expected :, found ("
|
||||
locations: [{ line: 2, column: 8 }]
|
||||
- name: must define one or more input fields
|
||||
input: |
|
||||
input Hello {}
|
||||
error:
|
||||
message: "expected at least one definition, found }"
|
||||
locations: [{ line: 1, column: 14 }]
|
||||
|
||||
directives:
|
||||
- name: simple
|
||||
input: directive @foo on FIELD
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Directives: [DirectiveDefinition]
|
||||
- <DirectiveDefinition>
|
||||
Name: "foo"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("FIELD")
|
||||
IsRepeatable: false
|
||||
|
||||
- name: executable
|
||||
input: |
|
||||
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
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Directives: [DirectiveDefinition]
|
||||
- <DirectiveDefinition>
|
||||
Name: "onQuery"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("QUERY")
|
||||
IsRepeatable: false
|
||||
- <DirectiveDefinition>
|
||||
Name: "onMutation"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("MUTATION")
|
||||
IsRepeatable: false
|
||||
- <DirectiveDefinition>
|
||||
Name: "onSubscription"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("SUBSCRIPTION")
|
||||
IsRepeatable: false
|
||||
- <DirectiveDefinition>
|
||||
Name: "onField"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("FIELD")
|
||||
IsRepeatable: false
|
||||
- <DirectiveDefinition>
|
||||
Name: "onFragmentDefinition"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("FRAGMENT_DEFINITION")
|
||||
IsRepeatable: false
|
||||
- <DirectiveDefinition>
|
||||
Name: "onFragmentSpread"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("FRAGMENT_SPREAD")
|
||||
IsRepeatable: false
|
||||
- <DirectiveDefinition>
|
||||
Name: "onInlineFragment"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("INLINE_FRAGMENT")
|
||||
IsRepeatable: false
|
||||
- <DirectiveDefinition>
|
||||
Name: "onVariableDefinition"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("VARIABLE_DEFINITION")
|
||||
IsRepeatable: false
|
||||
|
||||
- name: repeatable
|
||||
input: directive @foo repeatable on FIELD
|
||||
ast: |
|
||||
<SchemaDocument>
|
||||
Directives: [DirectiveDefinition]
|
||||
- <DirectiveDefinition>
|
||||
Name: "foo"
|
||||
Locations: [DirectiveLocation]
|
||||
- DirectiveLocation("FIELD")
|
||||
IsRepeatable: true
|
||||
|
||||
- name: invalid location
|
||||
input: "directive @foo on FIELD | INCORRECT_LOCATION"
|
||||
error:
|
||||
message: 'Unexpected Name "INCORRECT_LOCATION"'
|
||||
locations: [{ line: 1, column: 27 }]
|
||||
|
||||
fuzzer:
|
||||
- name: 1
|
||||
input: "type o{d(g:["
|
||||
error:
|
||||
message: 'Expected Name, found <EOF>'
|
||||
locations: [{ line: 1, column: 13 }]
|
||||
- name: 2
|
||||
input: "\"\"\"\r"
|
||||
error:
|
||||
message: 'Unexpected <Invalid>'
|
||||
locations: [{ line: 2, column: 1 }]
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/gqlerror"
|
||||
)
|
||||
|
||||
type ErrorOption func(err *gqlerror.Error)
|
||||
|
||||
func Message(msg string, args ...interface{}) ErrorOption {
|
||||
return func(err *gqlerror.Error) {
|
||||
err.Message += fmt.Sprintf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func At(position *ast.Position) ErrorOption {
|
||||
return func(err *gqlerror.Error) {
|
||||
if position == nil {
|
||||
return
|
||||
}
|
||||
err.Locations = append(err.Locations, gqlerror.Location{
|
||||
Line: position.Line,
|
||||
Column: position.Column,
|
||||
})
|
||||
if position.Src.Name != "" {
|
||||
err.SetFile(position.Src.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SuggestListQuoted(prefix string, typed string, suggestions []string) ErrorOption {
|
||||
suggested := SuggestionList(typed, suggestions)
|
||||
return func(err *gqlerror.Error) {
|
||||
if len(suggested) > 0 {
|
||||
err.Message += " " + prefix + " " + QuotedOrList(suggested...) + "?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SuggestListUnquoted(prefix string, typed string, suggestions []string) ErrorOption {
|
||||
suggested := SuggestionList(typed, suggestions)
|
||||
return func(err *gqlerror.Error) {
|
||||
if len(suggested) > 0 {
|
||||
err.Message += " " + prefix + " " + OrList(suggested...) + "?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Suggestf(suggestion string, args ...interface{}) ErrorOption {
|
||||
return func(err *gqlerror.Error) {
|
||||
err.Message += " Did you mean " + fmt.Sprintf(suggestion, args...) + "?"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-39
@@ -1,39 +0,0 @@
|
||||
package validator
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Given [ A, B, C ] return '"A", "B", or "C"'.
|
||||
func QuotedOrList(items ...string) string {
|
||||
itemsQuoted := make([]string, len(items))
|
||||
for i, item := range items {
|
||||
itemsQuoted[i] = `"` + item + `"`
|
||||
}
|
||||
return OrList(itemsQuoted...)
|
||||
}
|
||||
|
||||
// Given [ A, B, C ] return 'A, B, or C'.
|
||||
func OrList(items ...string) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if len(items) > 5 {
|
||||
items = items[:5]
|
||||
}
|
||||
if len(items) == 2 {
|
||||
buf.WriteString(items[0])
|
||||
buf.WriteString(" or ")
|
||||
buf.WriteString(items[1])
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
for i, item := range items {
|
||||
if i != 0 {
|
||||
if i == len(items)-1 {
|
||||
buf.WriteString(", or ")
|
||||
} else {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteString(item)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
)
|
||||
|
||||
//go:embed prelude.graphql
|
||||
var preludeGraphql string
|
||||
|
||||
var Prelude = &ast.Source{
|
||||
Name: "prelude.graphql",
|
||||
Input: preludeGraphql,
|
||||
BuiltIn: true,
|
||||
}
|
||||
Generated
Vendored
-121
@@ -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
|
||||
}
|
||||
vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/fields_on_correct_type.go
Generated
Vendored
-97
@@ -1,97 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"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("FieldsOnCorrectType", func(observers *Events, addError AddErrFunc) {
|
||||
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)
|
||||
|
||||
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),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Go through all of 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.
|
||||
func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string) []string {
|
||||
if !parent.IsAbstractType() {
|
||||
return nil
|
||||
}
|
||||
|
||||
possibleTypes := walker.Schema.GetPossibleTypes(parent)
|
||||
var suggestedObjectTypes = make([]string, 0, len(possibleTypes))
|
||||
var suggestedInterfaceTypes []string
|
||||
interfaceUsageCount := map[string]int{}
|
||||
|
||||
for _, possibleType := range possibleTypes {
|
||||
field := possibleType.Fields.ForName(name)
|
||||
if field == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
suggestedObjectTypes = append(suggestedObjectTypes, possibleType.Name)
|
||||
|
||||
for _, possibleInterface := range possibleType.Interfaces {
|
||||
interfaceField := walker.Schema.Types[possibleInterface]
|
||||
if interfaceField != nil && interfaceField.Fields.ForName(name) != nil {
|
||||
if interfaceUsageCount[possibleInterface] == 0 {
|
||||
suggestedInterfaceTypes = append(suggestedInterfaceTypes, possibleInterface)
|
||||
}
|
||||
interfaceUsageCount[possibleInterface]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suggestedTypes := append(suggestedInterfaceTypes, suggestedObjectTypes...)
|
||||
|
||||
sort.SliceStable(suggestedTypes, func(i, j int) bool {
|
||||
typeA, typeB := suggestedTypes[i], suggestedTypes[j]
|
||||
diff := interfaceUsageCount[typeB] - interfaceUsageCount[typeA]
|
||||
if diff != 0 {
|
||||
return diff < 0
|
||||
}
|
||||
return strings.Compare(typeA, typeB) < 0
|
||||
})
|
||||
|
||||
return suggestedTypes
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if parent.Kind != ast.Object && parent.Kind != ast.Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
var possibleFieldNames = make([]string, 0, len(parent.Fields))
|
||||
for _, field := range parent.Fields {
|
||||
possibleFieldNames = append(possibleFieldNames, field.Name)
|
||||
}
|
||||
|
||||
return SuggestionList(name, possibleFieldNames)
|
||||
}
|
||||
Generated
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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("FragmentsOnCompositeTypes", func(observers *Events, addError AddErrFunc) {
|
||||
observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
|
||||
fragmentType := walker.Schema.Types[inlineFragment.TypeCondition]
|
||||
if fragmentType == nil || fragmentType.IsCompositeType() {
|
||||
return
|
||||
}
|
||||
|
||||
message := fmt.Sprintf(`Fragment cannot condition on non composite type "%s".`, inlineFragment.TypeCondition)
|
||||
|
||||
addError(
|
||||
Message(message), //nolint:govet
|
||||
At(inlineFragment.Position),
|
||||
)
|
||||
})
|
||||
|
||||
observers.OnFragment(func(_ *Walker, fragment *ast.FragmentDefinition) {
|
||||
if fragment.Definition == nil || fragment.TypeCondition == "" || fragment.Definition.IsCompositeType() {
|
||||
return
|
||||
}
|
||||
|
||||
message := fmt.Sprintf(`Fragment "%s" cannot condition on non composite type "%s".`, fragment.Name, fragment.TypeCondition)
|
||||
|
||||
addError(
|
||||
Message(message), //nolint:govet
|
||||
At(fragment.Position),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-59
@@ -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),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-49
@@ -1,49 +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("KnownDirectives", 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) {
|
||||
if directive.Definition == nil {
|
||||
addError(
|
||||
Message(`Unknown directive "@%s".`, directive.Name),
|
||||
At(directive.Position),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
for _, loc := range directive.Definition.Locations {
|
||||
if loc == directive.Location {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// position must be exists if directive.Definition != nil
|
||||
tmp := mayNotBeUsedDirective{
|
||||
Name: directive.Name,
|
||||
Line: directive.Position.Line,
|
||||
Column: directive.Position.Column,
|
||||
}
|
||||
|
||||
if !seen[tmp] {
|
||||
addError(
|
||||
Message(`Directive "@%s" may not be used on %s.`, directive.Name, directive.Location),
|
||||
At(directive.Position),
|
||||
)
|
||||
seen[tmp] = true
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-21
@@ -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),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-37
@@ -1,37 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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("KnownRootType", 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) {
|
||||
var def *ast.Definition
|
||||
switch operation.Operation {
|
||||
case ast.Query, "":
|
||||
def = walker.Schema.Query
|
||||
case ast.Mutation:
|
||||
def = walker.Schema.Mutation
|
||||
case ast.Subscription:
|
||||
def = walker.Schema.Subscription
|
||||
default:
|
||||
// This shouldn't even parse; if it did we probably need to
|
||||
// update this switch block to add the new operation type.
|
||||
panic(fmt.Sprintf(`got unknown operation type "%s"`, operation.Operation))
|
||||
}
|
||||
if def == nil {
|
||||
addError(
|
||||
Message(`Schema does not support operation type "%s"`, operation.Operation),
|
||||
At(operation.Position))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-61
@@ -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),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-21
@@ -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),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-95
@@ -1,95 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"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("NoFragmentCycles", func(observers *Events, addError AddErrFunc) {
|
||||
visitedFrags := make(map[string]bool)
|
||||
|
||||
observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
|
||||
var spreadPath []*ast.FragmentSpread
|
||||
spreadPathIndexByName := make(map[string]int)
|
||||
|
||||
var recursive func(fragment *ast.FragmentDefinition)
|
||||
recursive = func(fragment *ast.FragmentDefinition) {
|
||||
if visitedFrags[fragment.Name] {
|
||||
return
|
||||
}
|
||||
|
||||
visitedFrags[fragment.Name] = true
|
||||
|
||||
spreadNodes := getFragmentSpreads(fragment.SelectionSet)
|
||||
if len(spreadNodes) == 0 {
|
||||
return
|
||||
}
|
||||
spreadPathIndexByName[fragment.Name] = len(spreadPath)
|
||||
|
||||
for _, spreadNode := range spreadNodes {
|
||||
spreadName := spreadNode.Name
|
||||
|
||||
cycleIndex, ok := spreadPathIndexByName[spreadName]
|
||||
|
||||
spreadPath = append(spreadPath, spreadNode)
|
||||
if !ok {
|
||||
spreadFragment := walker.Document.Fragments.ForName(spreadName)
|
||||
if spreadFragment != nil {
|
||||
recursive(spreadFragment)
|
||||
}
|
||||
} else {
|
||||
cyclePath := spreadPath[cycleIndex : len(spreadPath)-1]
|
||||
var fragmentNames []string
|
||||
for _, fs := range cyclePath {
|
||||
fragmentNames = append(fragmentNames, fmt.Sprintf(`"%s"`, fs.Name))
|
||||
}
|
||||
var via string
|
||||
if len(fragmentNames) != 0 {
|
||||
via = " via " + strings.Join(fragmentNames, ", ")
|
||||
}
|
||||
addError(
|
||||
Message(`Cannot spread fragment "%s" within itself%s.`, spreadName, via),
|
||||
At(spreadNode.Position),
|
||||
)
|
||||
}
|
||||
|
||||
spreadPath = spreadPath[:len(spreadPath)-1]
|
||||
}
|
||||
|
||||
delete(spreadPathIndexByName, fragment.Name)
|
||||
}
|
||||
|
||||
recursive(fragment)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func getFragmentSpreads(node ast.SelectionSet) []*ast.FragmentSpread {
|
||||
var spreads []*ast.FragmentSpread
|
||||
|
||||
setsToVisit := []ast.SelectionSet{node}
|
||||
|
||||
for len(setsToVisit) != 0 {
|
||||
set := setsToVisit[len(setsToVisit)-1]
|
||||
setsToVisit = setsToVisit[:len(setsToVisit)-1]
|
||||
|
||||
for _, selection := range set {
|
||||
switch selection := selection.(type) {
|
||||
case *ast.FragmentSpread:
|
||||
spreads = append(spreads, selection)
|
||||
case *ast.Field:
|
||||
setsToVisit = append(setsToVisit, selection.SelectionSet)
|
||||
case *ast.InlineFragment:
|
||||
setsToVisit = append(setsToVisit, selection.SelectionSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spreads
|
||||
}
|
||||
vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_undefined_variables.go
Generated
Vendored
-30
@@ -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("NoUndefinedVariables", 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
|
||||
}
|
||||
|
||||
if walker.CurrentOperation.Name != "" {
|
||||
addError(
|
||||
Message(`Variable "%s" is not defined by operation "%s".`, value, walker.CurrentOperation.Name),
|
||||
At(value.Position),
|
||||
)
|
||||
} else {
|
||||
addError(
|
||||
Message(`Variable "%s" is not defined.`, value),
|
||||
At(value.Position),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-32
@@ -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),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-32
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-562
@@ -1,562 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"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("OverlappingFieldsCanBeMerged", func(observers *Events, addError AddErrFunc) {
|
||||
/**
|
||||
* Algorithm:
|
||||
*
|
||||
* Conflicts occur when two fields exist in a query which will produce the same
|
||||
* response name, but represent differing values, thus creating a conflict.
|
||||
* The algorithm below finds all conflicts via making a series of comparisons
|
||||
* between fields. In order to compare as few fields as possible, this makes
|
||||
* a series of comparisons "within" sets of fields and "between" sets of fields.
|
||||
*
|
||||
* Given any selection set, a collection produces both a set of fields by
|
||||
* also including all inline fragments, as well as a list of fragments
|
||||
* referenced by fragment spreads.
|
||||
*
|
||||
* A) Each selection set represented in the document first compares "within" its
|
||||
* collected set of fields, finding any conflicts between every pair of
|
||||
* overlapping fields.
|
||||
* Note: This is the *only time* that a the fields "within" a set are compared
|
||||
* to each other. After this only fields "between" sets are compared.
|
||||
*
|
||||
* B) Also, if any fragment is referenced in a selection set, then a
|
||||
* comparison is made "between" the original set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* C) Also, if multiple fragments are referenced, then comparisons
|
||||
* are made "between" each referenced fragment.
|
||||
*
|
||||
* D) When comparing "between" a set of fields and a referenced fragment, first
|
||||
* a comparison is made between each field in the original set of fields and
|
||||
* each field in the referenced set of fields.
|
||||
*
|
||||
* E) Also, if any fragment is referenced in the referenced selection set,
|
||||
* then a comparison is made "between" the original set of fields and the
|
||||
* referenced fragment (recursively referring to step D).
|
||||
*
|
||||
* F) When comparing "between" two fragments, first a comparison is made between
|
||||
* each field in the first referenced set of fields and each field in the the
|
||||
* second referenced set of fields.
|
||||
*
|
||||
* G) Also, any fragments referenced by the first must be compared to the
|
||||
* second, and any fragments referenced by the second must be compared to the
|
||||
* first (recursively referring to step F).
|
||||
*
|
||||
* H) When comparing two fields, if both have selection sets, then a comparison
|
||||
* is made "between" both selection sets, first comparing the set of fields in
|
||||
* the first selection set with the set of fields in the second.
|
||||
*
|
||||
* I) Also, if any fragment is referenced in either selection set, then a
|
||||
* comparison is made "between" the other set of fields and the
|
||||
* referenced fragment.
|
||||
*
|
||||
* J) Also, if two fragments are referenced in both selection sets, then a
|
||||
* comparison is made "between" the two fragments.
|
||||
*
|
||||
*/
|
||||
|
||||
m := &overlappingFieldsCanBeMergedManager{
|
||||
comparedFragmentPairs: pairSet{data: make(map[string]map[string]bool)},
|
||||
}
|
||||
|
||||
observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
|
||||
m.walker = walker
|
||||
conflicts := m.findConflictsWithinSelectionSet(operation.SelectionSet)
|
||||
for _, conflict := range conflicts {
|
||||
conflict.addFieldsConflictMessage(addError)
|
||||
}
|
||||
})
|
||||
observers.OnField(func(walker *Walker, field *ast.Field) {
|
||||
if walker.CurrentOperation == nil {
|
||||
// When checking both Operation and Fragment, errors are duplicated when processing FragmentDefinition referenced from Operation
|
||||
return
|
||||
}
|
||||
m.walker = walker
|
||||
conflicts := m.findConflictsWithinSelectionSet(field.SelectionSet)
|
||||
for _, conflict := range conflicts {
|
||||
conflict.addFieldsConflictMessage(addError)
|
||||
}
|
||||
})
|
||||
observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
|
||||
m.walker = walker
|
||||
conflicts := m.findConflictsWithinSelectionSet(inlineFragment.SelectionSet)
|
||||
for _, conflict := range conflicts {
|
||||
conflict.addFieldsConflictMessage(addError)
|
||||
}
|
||||
})
|
||||
observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
|
||||
m.walker = walker
|
||||
conflicts := m.findConflictsWithinSelectionSet(fragment.SelectionSet)
|
||||
for _, conflict := range conflicts {
|
||||
conflict.addFieldsConflictMessage(addError)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type pairSet struct {
|
||||
data map[string]map[string]bool
|
||||
}
|
||||
|
||||
func (pairSet *pairSet) Add(a *ast.FragmentSpread, b *ast.FragmentSpread, areMutuallyExclusive bool) {
|
||||
add := func(a *ast.FragmentSpread, b *ast.FragmentSpread) {
|
||||
m := pairSet.data[a.Name]
|
||||
if m == nil {
|
||||
m = make(map[string]bool)
|
||||
pairSet.data[a.Name] = m
|
||||
}
|
||||
m[b.Name] = areMutuallyExclusive
|
||||
}
|
||||
add(a, b)
|
||||
add(b, a)
|
||||
}
|
||||
|
||||
func (pairSet *pairSet) Has(a *ast.FragmentSpread, b *ast.FragmentSpread, areMutuallyExclusive bool) bool {
|
||||
am, ok := pairSet.data[a.Name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
result, ok := am[b.Name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// areMutuallyExclusive being false is a superset of being true,
|
||||
// hence if we want to know if this PairSet "has" these two with no
|
||||
// exclusivity, we have to ensure it was added as such.
|
||||
if !areMutuallyExclusive {
|
||||
return !result
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type sequentialFieldsMap struct {
|
||||
// We can't use map[string][]*ast.Field. because map is not stable...
|
||||
seq []string
|
||||
data map[string][]*ast.Field
|
||||
}
|
||||
|
||||
type fieldIterateEntry struct {
|
||||
ResponseName string
|
||||
Fields []*ast.Field
|
||||
}
|
||||
|
||||
func (m *sequentialFieldsMap) Push(responseName string, field *ast.Field) {
|
||||
fields, ok := m.data[responseName]
|
||||
if !ok {
|
||||
m.seq = append(m.seq, responseName)
|
||||
}
|
||||
fields = append(fields, field)
|
||||
m.data[responseName] = fields
|
||||
}
|
||||
|
||||
func (m *sequentialFieldsMap) Get(responseName string) ([]*ast.Field, bool) {
|
||||
fields, ok := m.data[responseName]
|
||||
return fields, ok
|
||||
}
|
||||
|
||||
func (m *sequentialFieldsMap) Iterator() [][]*ast.Field {
|
||||
fieldsList := make([][]*ast.Field, 0, len(m.seq))
|
||||
for _, responseName := range m.seq {
|
||||
fields := m.data[responseName]
|
||||
fieldsList = append(fieldsList, fields)
|
||||
}
|
||||
return fieldsList
|
||||
}
|
||||
|
||||
func (m *sequentialFieldsMap) KeyValueIterator() []*fieldIterateEntry {
|
||||
fieldEntriesList := make([]*fieldIterateEntry, 0, len(m.seq))
|
||||
for _, responseName := range m.seq {
|
||||
fields := m.data[responseName]
|
||||
fieldEntriesList = append(fieldEntriesList, &fieldIterateEntry{
|
||||
ResponseName: responseName,
|
||||
Fields: fields,
|
||||
})
|
||||
}
|
||||
return fieldEntriesList
|
||||
}
|
||||
|
||||
type conflictMessageContainer struct {
|
||||
Conflicts []*ConflictMessage
|
||||
}
|
||||
|
||||
type ConflictMessage struct {
|
||||
Message string
|
||||
ResponseName string
|
||||
Names []string
|
||||
SubMessage []*ConflictMessage
|
||||
Position *ast.Position
|
||||
}
|
||||
|
||||
func (m *ConflictMessage) String(buf *bytes.Buffer) {
|
||||
if len(m.SubMessage) == 0 {
|
||||
buf.WriteString(m.Message)
|
||||
return
|
||||
}
|
||||
|
||||
for idx, subMessage := range m.SubMessage {
|
||||
buf.WriteString(`subfields "`)
|
||||
buf.WriteString(subMessage.ResponseName)
|
||||
buf.WriteString(`" conflict because `)
|
||||
subMessage.String(buf)
|
||||
if idx != len(m.SubMessage)-1 {
|
||||
buf.WriteString(" and ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ConflictMessage) addFieldsConflictMessage(addError AddErrFunc) {
|
||||
var buf bytes.Buffer
|
||||
m.String(&buf)
|
||||
addError(
|
||||
Message(`Fields "%s" conflict because %s. Use different aliases on the fields to fetch both if this was intentional.`, m.ResponseName, buf.String()),
|
||||
At(m.Position),
|
||||
)
|
||||
}
|
||||
|
||||
type overlappingFieldsCanBeMergedManager struct {
|
||||
walker *Walker
|
||||
|
||||
// per walker
|
||||
comparedFragmentPairs pairSet
|
||||
// cachedFieldsAndFragmentNames interface{}
|
||||
|
||||
// per selectionSet
|
||||
comparedFragments map[string]bool
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(selectionSet ast.SelectionSet) []*ConflictMessage {
|
||||
if len(selectionSet) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fieldsMap, fragmentSpreads := getFieldsAndFragmentNames(selectionSet)
|
||||
|
||||
var conflicts conflictMessageContainer
|
||||
|
||||
// (A) Find find all conflicts "within" the fieldMap of this selection set.
|
||||
// Note: this is the *only place* `collectConflictsWithin` is called.
|
||||
m.collectConflictsWithin(&conflicts, fieldsMap)
|
||||
|
||||
m.comparedFragments = make(map[string]bool)
|
||||
for idx, fragmentSpreadA := range fragmentSpreads {
|
||||
// (B) Then collect conflicts between these fieldMap and those represented by
|
||||
// each spread fragment name found.
|
||||
m.collectConflictsBetweenFieldsAndFragment(&conflicts, false, fieldsMap, fragmentSpreadA)
|
||||
|
||||
for _, fragmentSpreadB := range fragmentSpreads[idx+1:] {
|
||||
// (C) Then compare this fragment with all other fragments found in this
|
||||
// selection set to collect conflicts between fragments spread together.
|
||||
// This compares each item in the list of fragment names to every other
|
||||
// item in that same list (except for itself).
|
||||
m.collectConflictsBetweenFragments(&conflicts, false, fragmentSpreadA, fragmentSpreadB)
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts.Conflicts
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFieldsAndFragment(conflicts *conflictMessageContainer, areMutuallyExclusive bool, fieldsMap *sequentialFieldsMap, fragmentSpread *ast.FragmentSpread) {
|
||||
if m.comparedFragments[fragmentSpread.Name] {
|
||||
return
|
||||
}
|
||||
m.comparedFragments[fragmentSpread.Name] = true
|
||||
|
||||
if fragmentSpread.Definition == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fieldsMapB, fragmentSpreads := getFieldsAndFragmentNames(fragmentSpread.Definition.SelectionSet)
|
||||
|
||||
// Do not compare a fragment's fieldMap to itself.
|
||||
if reflect.DeepEqual(fieldsMap, fieldsMapB) {
|
||||
return
|
||||
}
|
||||
|
||||
// (D) First collect any conflicts between the provided collection of fields
|
||||
// and the collection of fields represented by the given fragment.
|
||||
m.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsMap, fieldsMapB)
|
||||
|
||||
// (E) Then collect any conflicts between the provided collection of fields
|
||||
// and any fragment names found in the given fragment.
|
||||
baseFragmentSpread := fragmentSpread
|
||||
for _, fragmentSpread := range fragmentSpreads {
|
||||
if fragmentSpread.Name == baseFragmentSpread.Name {
|
||||
continue
|
||||
}
|
||||
m.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsMap, fragmentSpread)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if m.comparedFragmentPairs.Has(fragmentSpreadA, fragmentSpreadB, areMutuallyExclusive) {
|
||||
return
|
||||
}
|
||||
m.comparedFragmentPairs.Add(fragmentSpreadA, fragmentSpreadB, areMutuallyExclusive)
|
||||
|
||||
if fragmentSpreadA.Definition == nil {
|
||||
return
|
||||
}
|
||||
if fragmentSpreadB.Definition == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(fragmentSpreadA.Definition.SelectionSet)
|
||||
fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(fragmentSpreadB.Definition.SelectionSet)
|
||||
|
||||
// (F) First, collect all conflicts between these two collections of fields
|
||||
// (not including any nested fragments).
|
||||
m.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsMapA, fieldsMapB)
|
||||
|
||||
// (G) Then collect conflicts between the first fragment and any nested
|
||||
// fragments spread in the second fragment.
|
||||
for _, fragmentSpread := range fragmentSpreadsB {
|
||||
check(fragmentSpreadA, fragmentSpread)
|
||||
}
|
||||
// (G) Then collect conflicts between the second fragment and any nested
|
||||
// fragments spread in the first fragment.
|
||||
for _, fragmentSpread := range fragmentSpreadsA {
|
||||
check(fragmentSpread, fragmentSpreadB)
|
||||
}
|
||||
}
|
||||
|
||||
check(fragmentSpreadA, fragmentSpreadB)
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSets(areMutuallyExclusive bool, selectionSetA ast.SelectionSet, selectionSetB ast.SelectionSet) *conflictMessageContainer {
|
||||
var conflicts conflictMessageContainer
|
||||
|
||||
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(selectionSetA)
|
||||
fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(selectionSetB)
|
||||
|
||||
// (H) First, collect all conflicts between these two collections of field.
|
||||
m.collectConflictsBetween(&conflicts, areMutuallyExclusive, fieldsMapA, fieldsMapB)
|
||||
|
||||
// (I) Then collect conflicts between the first collection of fields and
|
||||
// those referenced by each fragment name associated with the second.
|
||||
for _, fragmentSpread := range fragmentSpreadsB {
|
||||
m.comparedFragments = make(map[string]bool)
|
||||
m.collectConflictsBetweenFieldsAndFragment(&conflicts, areMutuallyExclusive, fieldsMapA, fragmentSpread)
|
||||
}
|
||||
|
||||
// (I) Then collect conflicts between the second collection of fields and
|
||||
// those referenced by each fragment name associated with the first.
|
||||
for _, fragmentSpread := range fragmentSpreadsA {
|
||||
m.comparedFragments = make(map[string]bool)
|
||||
m.collectConflictsBetweenFieldsAndFragment(&conflicts, areMutuallyExclusive, fieldsMapB, fragmentSpread)
|
||||
}
|
||||
|
||||
// (J) Also collect conflicts between any fragment names by the first and
|
||||
// fragment names by the second. This compares each item in the first set of
|
||||
// names to each item in the second set of names.
|
||||
for _, fragmentSpreadA := range fragmentSpreadsA {
|
||||
for _, fragmentSpreadB := range fragmentSpreadsB {
|
||||
m.collectConflictsBetweenFragments(&conflicts, areMutuallyExclusive, fragmentSpreadA, fragmentSpreadB)
|
||||
}
|
||||
}
|
||||
|
||||
if len(conflicts.Conflicts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &conflicts
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsWithin(conflicts *conflictMessageContainer, fieldsMap *sequentialFieldsMap) {
|
||||
for _, fields := range fieldsMap.Iterator() {
|
||||
for idx, fieldA := range fields {
|
||||
for _, fieldB := range fields[idx+1:] {
|
||||
conflict := m.findConflict(false, fieldA, fieldB)
|
||||
if conflict != nil {
|
||||
conflicts.Conflicts = append(conflicts.Conflicts, conflict)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetween(conflicts *conflictMessageContainer, parentFieldsAreMutuallyExclusive bool, fieldsMapA *sequentialFieldsMap, fieldsMapB *sequentialFieldsMap) {
|
||||
for _, fieldsEntryA := range fieldsMapA.KeyValueIterator() {
|
||||
fieldsB, ok := fieldsMapB.Get(fieldsEntryA.ResponseName)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, fieldA := range fieldsEntryA.Fields {
|
||||
for _, fieldB := range fieldsB {
|
||||
conflict := m.findConflict(parentFieldsAreMutuallyExclusive, fieldA, fieldB)
|
||||
if conflict != nil {
|
||||
conflicts.Conflicts = append(conflicts.Conflicts, conflict)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflict(parentFieldsAreMutuallyExclusive bool, fieldA *ast.Field, fieldB *ast.Field) *ConflictMessage {
|
||||
if fieldA.ObjectDefinition == nil || fieldB.ObjectDefinition == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
areMutuallyExclusive := parentFieldsAreMutuallyExclusive
|
||||
if !areMutuallyExclusive {
|
||||
tmp := fieldA.ObjectDefinition.Name != fieldB.ObjectDefinition.Name
|
||||
tmp = tmp && fieldA.ObjectDefinition.Kind == ast.Object
|
||||
tmp = tmp && fieldB.ObjectDefinition.Kind == ast.Object
|
||||
tmp = tmp && fieldA.Definition != nil && fieldB.Definition != nil
|
||||
areMutuallyExclusive = tmp
|
||||
}
|
||||
|
||||
fieldNameA := fieldA.Name
|
||||
if fieldA.Alias != "" {
|
||||
fieldNameA = fieldA.Alias
|
||||
}
|
||||
|
||||
if !areMutuallyExclusive {
|
||||
// Two aliases must refer to the same field.
|
||||
if fieldA.Name != fieldB.Name {
|
||||
return &ConflictMessage{
|
||||
ResponseName: fieldNameA,
|
||||
Message: fmt.Sprintf(`"%s" and "%s" are different fields`, fieldA.Name, fieldB.Name),
|
||||
Position: fieldB.Position,
|
||||
}
|
||||
}
|
||||
|
||||
// Two field calls must have the same arguments.
|
||||
if !sameArguments(fieldA.Arguments, fieldB.Arguments) {
|
||||
return &ConflictMessage{
|
||||
ResponseName: fieldNameA,
|
||||
Message: "they have differing arguments",
|
||||
Position: fieldB.Position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fieldA.Definition != nil && fieldB.Definition != nil && doTypesConflict(m.walker, fieldA.Definition.Type, fieldB.Definition.Type) {
|
||||
return &ConflictMessage{
|
||||
ResponseName: fieldNameA,
|
||||
Message: fmt.Sprintf(`they return conflicting types "%s" and "%s"`, fieldA.Definition.Type.String(), fieldB.Definition.Type.String()),
|
||||
Position: fieldB.Position,
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and compare sub-fields. Use the same "visited fragment names" list
|
||||
// for both collections so fields in a fragment reference are never
|
||||
// compared to themselves.
|
||||
conflicts := m.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, fieldA.SelectionSet, fieldB.SelectionSet)
|
||||
if conflicts == nil {
|
||||
return nil
|
||||
}
|
||||
return &ConflictMessage{
|
||||
ResponseName: fieldNameA,
|
||||
SubMessage: conflicts.Conflicts,
|
||||
Position: fieldB.Position,
|
||||
}
|
||||
}
|
||||
|
||||
func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool {
|
||||
if len(args1) != len(args2) {
|
||||
return false
|
||||
}
|
||||
for _, arg1 := range args1 {
|
||||
var matched bool
|
||||
for _, arg2 := range args2 {
|
||||
if arg1.Name == arg2.Name && sameValue(arg1.Value, arg2.Value) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func sameValue(value1 *ast.Value, value2 *ast.Value) bool {
|
||||
if value1.Kind != value2.Kind {
|
||||
return false
|
||||
}
|
||||
if value1.Raw != value2.Raw {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func doTypesConflict(walker *Walker, type1 *ast.Type, type2 *ast.Type) bool {
|
||||
if type1.Elem != nil {
|
||||
if type2.Elem != nil {
|
||||
return doTypesConflict(walker, type1.Elem, type2.Elem)
|
||||
}
|
||||
return true
|
||||
}
|
||||
if type2.Elem != nil {
|
||||
return true
|
||||
}
|
||||
if type1.NonNull && !type2.NonNull {
|
||||
return true
|
||||
}
|
||||
if !type1.NonNull && type2.NonNull {
|
||||
return true
|
||||
}
|
||||
|
||||
t1 := walker.Schema.Types[type1.NamedType]
|
||||
t2 := walker.Schema.Types[type2.NamedType]
|
||||
if (t1.Kind == ast.Scalar || t1.Kind == ast.Enum) && (t2.Kind == ast.Scalar || t2.Kind == ast.Enum) {
|
||||
return t1.Name != t2.Name
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getFieldsAndFragmentNames(selectionSet ast.SelectionSet) (*sequentialFieldsMap, []*ast.FragmentSpread) {
|
||||
fieldsMap := sequentialFieldsMap{
|
||||
data: make(map[string][]*ast.Field),
|
||||
}
|
||||
var fragmentSpreads []*ast.FragmentSpread
|
||||
|
||||
var walk func(selectionSet ast.SelectionSet)
|
||||
walk = func(selectionSet ast.SelectionSet) {
|
||||
for _, selection := range selectionSet {
|
||||
switch selection := selection.(type) {
|
||||
case *ast.Field:
|
||||
responseName := selection.Name
|
||||
if selection.Alias != "" {
|
||||
responseName = selection.Alias
|
||||
}
|
||||
fieldsMap.Push(responseName, selection)
|
||||
|
||||
case *ast.InlineFragment:
|
||||
walk(selection.SelectionSet)
|
||||
|
||||
case *ast.FragmentSpread:
|
||||
fragmentSpreads = append(fragmentSpreads, selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(selectionSet)
|
||||
|
||||
return &fieldsMap, fragmentSpreads
|
||||
}
|
||||
Generated
Vendored
-70
@@ -1,70 +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("PossibleFragmentSpreads", func(observers *Events, addError AddErrFunc) {
|
||||
|
||||
validate := func(walker *Walker, parentDef *ast.Definition, fragmentName string, emitError func()) {
|
||||
if parentDef == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var parentDefs []*ast.Definition
|
||||
switch parentDef.Kind {
|
||||
case ast.Object:
|
||||
parentDefs = []*ast.Definition{parentDef}
|
||||
case ast.Interface, ast.Union:
|
||||
parentDefs = walker.Schema.GetPossibleTypes(parentDef)
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
fragmentDefType := walker.Schema.Types[fragmentName]
|
||||
if fragmentDefType == nil {
|
||||
return
|
||||
}
|
||||
if !fragmentDefType.IsCompositeType() {
|
||||
// checked by FragmentsOnCompositeTypes
|
||||
return
|
||||
}
|
||||
fragmentDefs := walker.Schema.GetPossibleTypes(fragmentDefType)
|
||||
|
||||
for _, fragmentDef := range fragmentDefs {
|
||||
for _, parentDef := range parentDefs {
|
||||
if parentDef.Name == fragmentDef.Name {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitError()
|
||||
}
|
||||
|
||||
observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
|
||||
validate(walker, inlineFragment.ObjectDefinition, inlineFragment.TypeCondition, func() {
|
||||
addError(
|
||||
Message(`Fragment cannot be spread here as objects of type "%s" can never be of type "%s".`, inlineFragment.ObjectDefinition.Name, inlineFragment.TypeCondition),
|
||||
At(inlineFragment.Position),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) {
|
||||
if fragmentSpread.Definition == nil {
|
||||
return
|
||||
}
|
||||
validate(walker, fragmentSpread.ObjectDefinition, fragmentSpread.Definition.TypeCondition, func() {
|
||||
addError(
|
||||
Message(`Fragment "%s" cannot be spread here as objects of type "%s" can never be of type "%s".`, fragmentSpread.Name, fragmentSpread.ObjectDefinition.Name, fragmentSpread.Definition.TypeCondition),
|
||||
At(fragmentSpread.Position),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-64
@@ -1,64 +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("ProvidedRequiredArguments", func(observers *Events, addError AddErrFunc) {
|
||||
observers.OnField(func(_ *Walker, field *ast.Field) {
|
||||
if field.Definition == nil {
|
||||
return
|
||||
}
|
||||
|
||||
argDef:
|
||||
for _, argDef := range field.Definition.Arguments {
|
||||
if !argDef.Type.NonNull {
|
||||
continue
|
||||
}
|
||||
if argDef.DefaultValue != nil {
|
||||
continue
|
||||
}
|
||||
for _, arg := range field.Arguments {
|
||||
if arg.Name == argDef.Name {
|
||||
continue argDef
|
||||
}
|
||||
}
|
||||
|
||||
addError(
|
||||
Message(`Field "%s" argument "%s" of type "%s" is required, but it was not provided.`, field.Name, argDef.Name, argDef.Type.String()),
|
||||
At(field.Position),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
observers.OnDirective(func(_ *Walker, directive *ast.Directive) {
|
||||
if directive.Definition == nil {
|
||||
return
|
||||
}
|
||||
|
||||
argDef:
|
||||
for _, argDef := range directive.Definition.Arguments {
|
||||
if !argDef.Type.NonNull {
|
||||
continue
|
||||
}
|
||||
if argDef.DefaultValue != nil {
|
||||
continue
|
||||
}
|
||||
for _, arg := range directive.Arguments {
|
||||
if arg.Name == argDef.Name {
|
||||
continue argDef
|
||||
}
|
||||
}
|
||||
|
||||
addError(
|
||||
Message(`Directive "@%s" argument "%s" of type "%s" is required, but it was not provided.`, directive.Definition.Name, argDef.Name, argDef.Type.String()),
|
||||
At(directive.Position),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-38
@@ -1,38 +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("ScalarLeafs", func(observers *Events, addError AddErrFunc) {
|
||||
observers.OnField(func(walker *Walker, field *ast.Field) {
|
||||
if field.Definition == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fieldType := walker.Schema.Types[field.Definition.Type.Name()]
|
||||
if fieldType == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if fieldType.IsLeafType() && len(field.SelectionSet) > 0 {
|
||||
addError(
|
||||
Message(`Field "%s" must not have a selection since type "%s" has no subfields.`, field.Name, fieldType.Name),
|
||||
At(field.Position),
|
||||
)
|
||||
}
|
||||
|
||||
if !fieldType.IsLeafType() && len(field.SelectionSet) == 0 {
|
||||
addError(
|
||||
Message(`Field "%s" of type "%s" must have a selection of subfields.`, field.Name, field.Definition.Type.String()),
|
||||
Suggestf(`"%s { ... }"`, field.Name),
|
||||
At(field.Position),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-88
@@ -1,88 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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("SingleFieldSubscriptions", func(observers *Events, addError AddErrFunc) {
|
||||
observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
|
||||
if walker.Schema.Subscription == nil || operation.Operation != ast.Subscription {
|
||||
return
|
||||
}
|
||||
|
||||
fields := retrieveTopFieldNames(operation.SelectionSet)
|
||||
|
||||
name := "Anonymous Subscription"
|
||||
if operation.Name != "" {
|
||||
name = `Subscription ` + strconv.Quote(operation.Name)
|
||||
}
|
||||
|
||||
if len(fields) > 1 {
|
||||
addError(
|
||||
Message(`%s must select only one top level field.`, name),
|
||||
At(fields[1].position),
|
||||
)
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
if strings.HasPrefix(field.name, "__") {
|
||||
addError(
|
||||
Message(`%s must not select an introspection top level field.`, name),
|
||||
At(field.position),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type topField struct {
|
||||
name string
|
||||
position *ast.Position
|
||||
}
|
||||
|
||||
func retrieveTopFieldNames(selectionSet ast.SelectionSet) []*topField {
|
||||
fields := []*topField{}
|
||||
inFragmentRecursive := map[string]bool{}
|
||||
var walk func(selectionSet ast.SelectionSet)
|
||||
walk = func(selectionSet ast.SelectionSet) {
|
||||
for _, selection := range selectionSet {
|
||||
switch selection := selection.(type) {
|
||||
case *ast.Field:
|
||||
fields = append(fields, &topField{
|
||||
name: selection.Name,
|
||||
position: selection.GetPosition(),
|
||||
})
|
||||
case *ast.InlineFragment:
|
||||
walk(selection.SelectionSet)
|
||||
case *ast.FragmentSpread:
|
||||
if selection.Definition == nil {
|
||||
return
|
||||
}
|
||||
fragment := selection.Definition.Name
|
||||
if !inFragmentRecursive[fragment] {
|
||||
inFragmentRecursive[fragment] = true
|
||||
walk(selection.Definition.SelectionSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(selectionSet)
|
||||
|
||||
seen := make(map[string]bool, len(fields))
|
||||
uniquedFields := make([]*topField, 0, len(fields))
|
||||
for _, field := range fields {
|
||||
if !seen[field.name] {
|
||||
uniquedFields = append(uniquedFields, field)
|
||||
}
|
||||
seen[field.name] = true
|
||||
}
|
||||
return uniquedFields
|
||||
}
|
||||
Generated
Vendored
-35
@@ -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]++
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-26
@@ -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
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-24
@@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-29
@@ -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
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_operation_names.go
Generated
Vendored
-24
@@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-26
@@ -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]++
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/values_of_correct_type.go
Generated
Vendored
-168
@@ -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())
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-30
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Generated
Vendored
-40
@@ -1,40 +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("VariablesInAllowedPosition", 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
|
||||
}
|
||||
|
||||
tmp := *value.ExpectedType
|
||||
|
||||
// todo: move me into walk
|
||||
// If there is a default non nullable types can be null
|
||||
if value.VariableDefinition.DefaultValue != nil && value.VariableDefinition.DefaultValue.Kind != ast.NullValue {
|
||||
if value.ExpectedType.NonNull {
|
||||
tmp.NonNull = false
|
||||
}
|
||||
}
|
||||
|
||||
if !value.VariableDefinition.Type.IsCompatible(&tmp) {
|
||||
addError(
|
||||
Message(
|
||||
`Variable "%s" of type "%s" used in position expecting type "%s".`,
|
||||
value,
|
||||
value.VariableDefinition.Type.String(),
|
||||
value.ExpectedType.String(),
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
-513
@@ -1,513 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"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"
|
||||
)
|
||||
|
||||
func LoadSchema(inputs ...*Source) (*Schema, error) {
|
||||
ast, err := parser.ParseSchemas(inputs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ValidateSchemaDocument(ast)
|
||||
}
|
||||
|
||||
func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) {
|
||||
schema := Schema{
|
||||
Types: map[string]*Definition{},
|
||||
Directives: map[string]*DirectiveDefinition{},
|
||||
PossibleTypes: map[string][]*Definition{},
|
||||
Implements: map[string][]*Definition{},
|
||||
}
|
||||
|
||||
for i, def := range ast.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]
|
||||
}
|
||||
|
||||
defs := append(DefinitionList{}, ast.Definitions...)
|
||||
|
||||
for _, ext := range ast.Extensions {
|
||||
def := schema.Types[ext.Name]
|
||||
if def == nil {
|
||||
schema.Types[ext.Name] = &Definition{
|
||||
Kind: ext.Kind,
|
||||
Name: ext.Name,
|
||||
Position: ext.Position,
|
||||
}
|
||||
def = schema.Types[ext.Name]
|
||||
defs = append(defs, def)
|
||||
}
|
||||
|
||||
if def.Kind != ext.Kind {
|
||||
return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because the base type is a %s, not %s.", ext.Name, def.Kind, ext.Kind)
|
||||
}
|
||||
|
||||
def.Directives = append(def.Directives, ext.Directives...)
|
||||
def.Interfaces = append(def.Interfaces, ext.Interfaces...)
|
||||
def.Fields = append(def.Fields, ext.Fields...)
|
||||
def.Types = append(def.Types, ext.Types...)
|
||||
def.EnumValues = append(def.EnumValues, ext.EnumValues...)
|
||||
}
|
||||
|
||||
for _, def := range defs {
|
||||
switch def.Kind {
|
||||
case Union:
|
||||
for _, t := range def.Types {
|
||||
schema.AddPossibleType(def.Name, schema.Types[t])
|
||||
schema.AddImplements(t, def)
|
||||
}
|
||||
case InputObject, Object:
|
||||
for _, intf := range def.Interfaces {
|
||||
schema.AddPossibleType(intf, def)
|
||||
schema.AddImplements(def.Name, schema.Types[intf])
|
||||
}
|
||||
schema.AddPossibleType(def.Name, def)
|
||||
case Interface:
|
||||
for _, intf := range def.Interfaces {
|
||||
schema.AddPossibleType(intf, def)
|
||||
schema.AddImplements(def.Name, schema.Types[intf])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, dir := range ast.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
|
||||
// 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
|
||||
// is a lot of work.) So we just keep the first one we saw.
|
||||
// That's an arbitrary choice, but in theory the only way it
|
||||
// fails is if the server is using features newer than this
|
||||
// version of gqlparser, in which case they're in trouble
|
||||
// anyway.
|
||||
default:
|
||||
return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name)
|
||||
}
|
||||
}
|
||||
schema.Directives[dir.Name] = ast.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(ast.Schema) == 1 {
|
||||
schema.Description = ast.Schema[0].Description
|
||||
for _, entrypoint := range ast.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)
|
||||
}
|
||||
switch entrypoint.Operation {
|
||||
case Query:
|
||||
schema.Query = def
|
||||
case Mutation:
|
||||
schema.Mutation = def
|
||||
case Subscription:
|
||||
schema.Subscription = def
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ext := range ast.SchemaExtension {
|
||||
for _, entrypoint := range ext.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)
|
||||
}
|
||||
switch entrypoint.Operation {
|
||||
case Query:
|
||||
schema.Query = def
|
||||
case Mutation:
|
||||
schema.Mutation = def
|
||||
case Subscription:
|
||||
schema.Subscription = def
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateTypeDefinitions(&schema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := validateDirectiveDefinitions(&schema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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 schema.Query == nil && schema.Types["Query"] != nil {
|
||||
schema.Query = schema.Types["Query"]
|
||||
}
|
||||
|
||||
if schema.Mutation == nil && schema.Types["Mutation"] != nil {
|
||||
schema.Mutation = schema.Types["Mutation"]
|
||||
}
|
||||
|
||||
if schema.Subscription == nil && schema.Types["Subscription"] != nil {
|
||||
schema.Subscription = schema.Types["Subscription"]
|
||||
}
|
||||
}
|
||||
|
||||
if schema.Query != nil {
|
||||
schema.Query.Fields = append(
|
||||
schema.Query.Fields,
|
||||
&FieldDefinition{
|
||||
Name: "__schema",
|
||||
Type: NonNullNamedType("__Schema", nil),
|
||||
},
|
||||
&FieldDefinition{
|
||||
Name: "__type",
|
||||
Type: NamedType("__Type", nil),
|
||||
Arguments: ArgumentDefinitionList{
|
||||
{Name: "name", Type: NonNullNamedType("String", nil)},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return &schema, nil
|
||||
}
|
||||
|
||||
func validateTypeDefinitions(schema *Schema) *gqlerror.Error {
|
||||
types := make([]string, 0, len(schema.Types))
|
||||
for typ := range schema.Types {
|
||||
types = append(types, typ)
|
||||
}
|
||||
sort.Strings(types)
|
||||
for _, typ := range types {
|
||||
err := validateDefinition(schema, schema.Types[typ])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDirectiveDefinitions(schema *Schema) *gqlerror.Error {
|
||||
directives := make([]string, 0, len(schema.Directives))
|
||||
for directive := range schema.Directives {
|
||||
directives = append(directives, directive)
|
||||
}
|
||||
sort.Strings(directives)
|
||||
for _, directive := range directives {
|
||||
err := validateDirective(schema, schema.Directives[directive])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDirective(schema *Schema, def *DirectiveDefinition) *gqlerror.Error {
|
||||
if err := validateName(def.Position, def.Name); err != nil {
|
||||
// now, GraphQL spec doesn't have reserved directive name
|
||||
return err
|
||||
}
|
||||
|
||||
return validateArgs(schema, def.Arguments, def)
|
||||
}
|
||||
|
||||
func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
|
||||
for _, field := range def.Fields {
|
||||
if err := validateName(field.Position, field.Name); err != nil {
|
||||
// now, GraphQL spec doesn't have reserved field name
|
||||
return err
|
||||
}
|
||||
if err := validateTypeRef(schema, field.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateArgs(schema, field.Arguments, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
wantDirLocation := LocationFieldDefinition
|
||||
if def.Kind == InputObject {
|
||||
wantDirLocation = LocationInputFieldDefinition
|
||||
}
|
||||
if err := validateDirectives(schema, field.Directives, wantDirLocation, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, typ := range def.Types {
|
||||
typDef := schema.Types[typ]
|
||||
if typDef == nil {
|
||||
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(typ))
|
||||
}
|
||||
if !isValidKind(typDef.Kind, Object) {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s type %s must be %s.", def.Kind, strconv.Quote(typ), kindList(Object))
|
||||
}
|
||||
}
|
||||
|
||||
for _, intf := range def.Interfaces {
|
||||
if err := validateImplements(schema, def, intf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch def.Kind {
|
||||
case Object, Interface:
|
||||
if len(def.Fields) == 0 {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more fields.", def.Kind, def.Name)
|
||||
}
|
||||
for _, field := range def.Fields {
|
||||
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
||||
if !isValidKind(typ.Kind, Scalar, Object, Interface, Union, Enum) {
|
||||
return gqlerror.ErrorPosf(field.Position, "%s %s: field must be one of %s.", def.Kind, def.Name, kindList(Scalar, Object, Interface, Union, Enum))
|
||||
}
|
||||
}
|
||||
}
|
||||
case Enum:
|
||||
if len(def.EnumValues) == 0 {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more unique enum values.", def.Kind, def.Name)
|
||||
}
|
||||
for _, value := range def.EnumValues {
|
||||
for _, nonEnum := range [3]string{"true", "false", "null"} {
|
||||
if value.Name == nonEnum {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: non-enum value %s.", def.Kind, def.Name, value.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
case InputObject:
|
||||
if len(def.Fields) == 0 {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more input fields.", def.Kind, def.Name)
|
||||
}
|
||||
for _, field := range def.Fields {
|
||||
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
||||
if !isValidKind(typ.Kind, Scalar, Enum, InputObject) {
|
||||
return gqlerror.ErrorPosf(field.Position, "%s %s: field must be one of %s.", typ.Kind, field.Name, kindList(Scalar, Enum, InputObject))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for idx, field1 := range def.Fields {
|
||||
for _, field2 := range def.Fields[idx+1:] {
|
||||
if field1.Name == field2.Name {
|
||||
return gqlerror.ErrorPosf(field2.Position, "Field %s.%s can only be defined once.", def.Name, field2.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !def.BuiltIn {
|
||||
// GraphQL spec has reserved type names a lot!
|
||||
err := validateName(def.Position, def.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return validateDirectives(schema, def.Directives, DirectiveLocation(def.Kind), nil)
|
||||
}
|
||||
|
||||
func validateTypeRef(schema *Schema, typ *Type) *gqlerror.Error {
|
||||
if schema.Types[typ.Name()] == nil {
|
||||
return gqlerror.ErrorPosf(typ.Position, "Undefined type %s.", typ.Name())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective *DirectiveDefinition) *gqlerror.Error {
|
||||
for _, arg := range args {
|
||||
if err := validateName(arg.Position, arg.Name); err != nil {
|
||||
// now, GraphQL spec doesn't have reserved argument name
|
||||
return err
|
||||
}
|
||||
if err := validateTypeRef(schema, arg.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
def := schema.Types[arg.Type.Name()]
|
||||
if !def.IsInputType() {
|
||||
return gqlerror.ErrorPosf(
|
||||
arg.Position,
|
||||
"cannot use %s as argument %s because %s is not a valid input type",
|
||||
arg.Type.String(),
|
||||
arg.Name,
|
||||
def.Kind,
|
||||
)
|
||||
}
|
||||
if err := validateDirectives(schema, arg.Directives, LocationArgumentDefinition, currentDirective); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDirectives(schema *Schema, dirs DirectiveList, location DirectiveLocation, currentDirective *DirectiveDefinition) *gqlerror.Error {
|
||||
for _, dir := range dirs {
|
||||
if err := validateName(dir.Position, dir.Name); err != nil {
|
||||
// now, GraphQL spec doesn't have reserved directive name
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
|
||||
}
|
||||
validKind := false
|
||||
for _, dirLocation := range schema.Directives[dir.Name].Locations {
|
||||
if dirLocation == location {
|
||||
validKind = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validKind {
|
||||
return gqlerror.ErrorPosf(dir.Position, "Directive %s is not applicable on %s.", dir.Name, location)
|
||||
}
|
||||
dir.Definition = schema.Directives[dir.Name]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
intf := schema.Types[intfName]
|
||||
if intf == nil {
|
||||
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
|
||||
}
|
||||
if intf.Kind != Interface {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s is a non interface type %s.", strconv.Quote(intfName), intf.Kind)
|
||||
}
|
||||
for _, requiredField := range intf.Fields {
|
||||
foundField := def.Fields.ForName(requiredField.Name)
|
||||
if foundField == nil {
|
||||
return gqlerror.ErrorPosf(def.Position,
|
||||
`For %s to implement %s it must have a field called %s.`,
|
||||
def.Name, intf.Name, requiredField.Name,
|
||||
)
|
||||
}
|
||||
|
||||
if !isCovariant(schema, requiredField.Type, foundField.Type) {
|
||||
return gqlerror.ErrorPosf(foundField.Position,
|
||||
`For %s to implement %s the field %s must have type %s.`,
|
||||
def.Name, intf.Name, requiredField.Name, requiredField.Type.String(),
|
||||
)
|
||||
}
|
||||
|
||||
for _, requiredArg := range requiredField.Arguments {
|
||||
foundArg := foundField.Arguments.ForName(requiredArg.Name)
|
||||
if foundArg == nil {
|
||||
return gqlerror.ErrorPosf(foundField.Position,
|
||||
`For %s to implement %s the field %s must have the same arguments but it is missing %s.`,
|
||||
def.Name, intf.Name, requiredField.Name, requiredArg.Name,
|
||||
)
|
||||
}
|
||||
|
||||
if !requiredArg.Type.IsCompatible(foundArg.Type) {
|
||||
return gqlerror.ErrorPosf(foundArg.Position,
|
||||
`For %s to implement %s the field %s must have the same arguments but %s has the wrong type.`,
|
||||
def.Name, intf.Name, requiredField.Name, requiredArg.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
for _, foundArgs := range foundField.Arguments {
|
||||
if requiredField.Arguments.ForName(foundArgs.Name) == nil && foundArgs.Type.NonNull && foundArgs.DefaultValue == nil {
|
||||
return gqlerror.ErrorPosf(foundArgs.Position,
|
||||
`For %s to implement %s any additional arguments on %s must be optional or have a default value but %s is required.`,
|
||||
def.Name, intf.Name, foundField.Name, foundArgs.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return validateTypeImplementsAncestors(schema, def, intfName)
|
||||
}
|
||||
|
||||
// validateTypeImplementsAncestors
|
||||
// https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/type/validate.ts#L428
|
||||
func validateTypeImplementsAncestors(schema *Schema, def *Definition, intfName string) *gqlerror.Error {
|
||||
intf := schema.Types[intfName]
|
||||
if intf == nil {
|
||||
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
|
||||
}
|
||||
for _, transitive := range intf.Interfaces {
|
||||
if !containsString(def.Interfaces, transitive) {
|
||||
if transitive == def.Name {
|
||||
return gqlerror.ErrorPosf(def.Position,
|
||||
`Type %s cannot implement %s because it would create a circular reference.`,
|
||||
def.Name, intfName,
|
||||
)
|
||||
}
|
||||
return gqlerror.ErrorPosf(def.Position,
|
||||
`Type %s must implement %s because it is implemented by %s.`,
|
||||
def.Name, transitive, intfName,
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func containsString(slice []string, want string) bool {
|
||||
for _, str := range slice {
|
||||
if want == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isCovariant(schema *Schema, required *Type, actual *Type) bool {
|
||||
if required.NonNull && !actual.NonNull {
|
||||
return false
|
||||
}
|
||||
|
||||
if required.NamedType != "" {
|
||||
if required.NamedType == actual.NamedType {
|
||||
return true
|
||||
}
|
||||
for _, pt := range schema.PossibleTypes[required.NamedType] {
|
||||
if pt.Name == actual.NamedType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if required.Elem != nil && actual.Elem == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return isCovariant(schema, required.Elem, actual.Elem)
|
||||
}
|
||||
|
||||
func validateName(pos *Position, name string) *gqlerror.Error {
|
||||
if strings.HasPrefix(name, "__") {
|
||||
return gqlerror.ErrorPosf(pos, `Name "%s" must not begin with "__", which is reserved by GraphQL introspection.`, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidKind(kind DefinitionKind, valid ...DefinitionKind) bool {
|
||||
for _, k := range valid {
|
||||
if kind == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func kindList(kinds ...DefinitionKind) string {
|
||||
s := make([]string, len(kinds))
|
||||
for i, k := range kinds {
|
||||
s[i] = string(k)
|
||||
}
|
||||
return strings.Join(s, ", ")
|
||||
}
|
||||
Generated
Vendored
-678
@@ -1,678 +0,0 @@
|
||||
types:
|
||||
- name: cannot be redeclared
|
||||
input: |
|
||||
type A {
|
||||
name: String
|
||||
}
|
||||
type A {
|
||||
name: String
|
||||
}
|
||||
error:
|
||||
message: "Cannot redeclare type A."
|
||||
locations: [{line: 4, column: 6}]
|
||||
- name: cannot be duplicated field at same definition 1
|
||||
input: |
|
||||
type A {
|
||||
name: String
|
||||
name: String
|
||||
}
|
||||
error:
|
||||
message: "Field A.name can only be defined once."
|
||||
locations: [{line: 3, column: 3}]
|
||||
- name: cannot be duplicated field at same definition 2
|
||||
input: |
|
||||
type A {
|
||||
name: String
|
||||
}
|
||||
extend type A {
|
||||
name: String
|
||||
}
|
||||
error:
|
||||
message: "Field A.name can only be defined once."
|
||||
locations: [{line: 5, column: 3}]
|
||||
- name: cannot be duplicated field at same definition 3
|
||||
input: |
|
||||
type A {
|
||||
name: String
|
||||
}
|
||||
extend type A {
|
||||
age: Int
|
||||
age: Int
|
||||
}
|
||||
error:
|
||||
message: "Field A.age can only be defined once."
|
||||
locations: [{line: 6, column: 3}]
|
||||
|
||||
object types:
|
||||
- name: must define one or more fields
|
||||
input: |
|
||||
directive @D on OBJECT
|
||||
|
||||
# This pattern rejected by parser
|
||||
# type InvalidObject1 {}
|
||||
|
||||
type InvalidObject2 @D
|
||||
|
||||
type ValidObject {
|
||||
id: ID
|
||||
}
|
||||
extend type ValidObject @D
|
||||
extend type ValidObject {
|
||||
b: Int
|
||||
}
|
||||
error:
|
||||
message: 'OBJECT InvalidObject2: must define one or more fields.'
|
||||
locations: [{line: 6, column: 6}]
|
||||
- name: check reserved names on type name
|
||||
input: |
|
||||
type __FooBar {
|
||||
id: ID
|
||||
}
|
||||
error:
|
||||
message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
|
||||
locations: [{line: 1, column: 6}]
|
||||
- name: check reserved names on type field
|
||||
input: |
|
||||
type FooBar {
|
||||
__id: ID
|
||||
}
|
||||
error:
|
||||
message: 'Name "__id" must not begin with "__", which is reserved by GraphQL introspection.'
|
||||
locations: [{line: 2, column: 3}]
|
||||
|
||||
- name: check reserved names on type field argument
|
||||
input: |
|
||||
type FooBar {
|
||||
foo(__bar: ID): ID
|
||||
}
|
||||
error:
|
||||
message: 'Name "__bar" must not begin with "__", which is reserved by GraphQL introspection.'
|
||||
locations: [{line: 2, column: 7}]
|
||||
|
||||
- name: must not allow input object as field type
|
||||
input: |
|
||||
input Input {
|
||||
id: ID
|
||||
}
|
||||
type Query {
|
||||
input: Input!
|
||||
}
|
||||
error:
|
||||
message: 'OBJECT Query: field must be one of SCALAR, OBJECT, INTERFACE, UNION, ENUM.'
|
||||
locations: [{line: 5, column: 3}]
|
||||
|
||||
interfaces:
|
||||
- name: must exist
|
||||
input: |
|
||||
type Thing implements Object {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type Query {
|
||||
Things: [Thing!]!
|
||||
}
|
||||
error:
|
||||
message: 'Undefined type "Object".'
|
||||
locations: [{line: 1, column: 6}]
|
||||
|
||||
- name: must be an interface
|
||||
input: |
|
||||
type Thing implements Object {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type Query {
|
||||
Things: [Thing!]!
|
||||
}
|
||||
|
||||
type Object {
|
||||
name: String
|
||||
}
|
||||
error:
|
||||
message: '"Object" is a non interface type OBJECT.'
|
||||
locations: [{line: 1, column: 6}]
|
||||
|
||||
- name: must define one or more fields
|
||||
input: |
|
||||
directive @D on INTERFACE
|
||||
|
||||
# This pattern rejected by parser
|
||||
# interface InvalidInterface1 {}
|
||||
|
||||
interface InvalidInterface2 @D
|
||||
|
||||
interface ValidInterface {
|
||||
id: ID
|
||||
}
|
||||
extend interface ValidInterface @D
|
||||
extend interface ValidInterface {
|
||||
b: Int
|
||||
}
|
||||
error:
|
||||
message: 'INTERFACE InvalidInterface2: must define one or more fields.'
|
||||
locations: [{line: 6, column: 11}]
|
||||
|
||||
- name: check reserved names on type name
|
||||
input: |
|
||||
interface __FooBar {
|
||||
id: ID
|
||||
}
|
||||
error:
|
||||
message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
|
||||
locations: [{line: 1, column: 11}]
|
||||
|
||||
- name: must not allow input object as field type
|
||||
input: |
|
||||
input Input {
|
||||
id: ID
|
||||
}
|
||||
type Query {
|
||||
foo: Foo!
|
||||
}
|
||||
interface Foo {
|
||||
input: Input!
|
||||
}
|
||||
error:
|
||||
message: 'INTERFACE Foo: field must be one of SCALAR, OBJECT, INTERFACE, UNION, ENUM.'
|
||||
locations: [{line: 8, column: 3}]
|
||||
|
||||
- name: must have all fields from interface
|
||||
input: |
|
||||
type Bar implements BarInterface {
|
||||
someField: Int!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
id: ID!
|
||||
}
|
||||
error:
|
||||
message: 'For Bar to implement BarInterface it must have a field called id.'
|
||||
locations: [{line: 1, column: 6}]
|
||||
|
||||
- name: must have same type of fields
|
||||
input: |
|
||||
type Bar implements BarInterface {
|
||||
id: Int!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
id: ID!
|
||||
}
|
||||
error:
|
||||
message: 'For Bar to implement BarInterface the field id must have type ID!.'
|
||||
locations: [{line: 2, column: 5}]
|
||||
|
||||
- name: must have all required arguments
|
||||
input: |
|
||||
type Bar implements BarInterface {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
id(ff: Int!): ID!
|
||||
}
|
||||
error:
|
||||
message: 'For Bar to implement BarInterface the field id must have the same arguments but it is missing ff.'
|
||||
locations: [{line: 2, column: 5}]
|
||||
|
||||
- name: must have same argument types
|
||||
input: |
|
||||
type Bar implements BarInterface {
|
||||
id(ff: ID!): ID!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
id(ff: Int!): ID!
|
||||
}
|
||||
error:
|
||||
message: 'For Bar to implement BarInterface the field id must have the same arguments but ff has the wrong type.'
|
||||
locations: [{line: 2, column: 8}]
|
||||
|
||||
- name: may defined additional nullable arguments
|
||||
input: |
|
||||
type Bar implements BarInterface {
|
||||
id(opt: Int): ID!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
- name: may defined additional required arguments with defaults
|
||||
input: |
|
||||
type Bar implements BarInterface {
|
||||
id(opt: Int! = 1): ID!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
- name: must not define additional required arguments without defaults
|
||||
input: |
|
||||
type Bar implements BarInterface {
|
||||
id(opt: Int!): ID!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
id: ID!
|
||||
}
|
||||
error:
|
||||
message: 'For Bar to implement BarInterface any additional arguments on id must be optional or have a default value but opt is required.'
|
||||
locations: [{line: 2, column: 8}]
|
||||
|
||||
- name: can have covariant argument types
|
||||
input: |
|
||||
union U = A|B
|
||||
|
||||
type A { name: String }
|
||||
type B { name: String }
|
||||
|
||||
type Bar implements BarInterface {
|
||||
f: A!
|
||||
}
|
||||
|
||||
interface BarInterface {
|
||||
f: U!
|
||||
}
|
||||
|
||||
- name: may define intermediate interfaces
|
||||
input: |
|
||||
interface IA {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
interface IIA implements IA {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type A implements IIA & IA {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
- name: Type Foo must implement Baz because it is implemented by Bar
|
||||
input: |
|
||||
interface Baz {
|
||||
baz: String
|
||||
}
|
||||
|
||||
interface Bar implements Baz {
|
||||
bar: String
|
||||
baz: String
|
||||
}
|
||||
|
||||
type Foo implements Bar {
|
||||
foo: String
|
||||
bar: String
|
||||
baz: String
|
||||
}
|
||||
error:
|
||||
message: 'Type Foo must implement Baz because it is implemented by Bar.'
|
||||
locations: [{line: 10, column: 6}]
|
||||
|
||||
- name: circular reference error
|
||||
input: |
|
||||
interface Circular1 implements Circular2 {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
interface Circular2 implements Circular1 {
|
||||
id: ID!
|
||||
}
|
||||
error:
|
||||
message: 'Type Circular1 cannot implement Circular2 because it would create a circular reference.'
|
||||
locations: [{line: 1, column: 11}]
|
||||
|
||||
inputs:
|
||||
- name: must define one or more input fields
|
||||
input: |
|
||||
directive @D on INPUT_OBJECT
|
||||
|
||||
# This pattern rejected by parser
|
||||
# input InvalidInput1 {}
|
||||
|
||||
input InvalidInput2 @D
|
||||
|
||||
input ValidInput {
|
||||
id: ID
|
||||
}
|
||||
extend input ValidInput @D
|
||||
extend input ValidInput {
|
||||
b: Int
|
||||
}
|
||||
error:
|
||||
message: 'INPUT_OBJECT InvalidInput2: must define one or more input fields.'
|
||||
locations: [{line: 6, column: 7}]
|
||||
- name: check reserved names on type name
|
||||
input: |
|
||||
input __FooBar {
|
||||
id: ID
|
||||
}
|
||||
error:
|
||||
message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
|
||||
locations: [{line: 1, column: 7}]
|
||||
|
||||
- name: fields cannot be Objects
|
||||
input: |
|
||||
type Object { id: ID }
|
||||
input Foo { a: Object! }
|
||||
error:
|
||||
message: 'OBJECT a: field must be one of SCALAR, ENUM, INPUT_OBJECT.'
|
||||
locations: [{line: 2, column: 13}]
|
||||
|
||||
- name: fields cannot be Interfaces
|
||||
input: |
|
||||
interface Interface { id: ID! }
|
||||
input Foo { a: Interface! }
|
||||
error:
|
||||
message: 'INTERFACE a: field must be one of SCALAR, ENUM, INPUT_OBJECT.'
|
||||
locations: [{line: 2, column: 13}]
|
||||
|
||||
- name: fields cannot be Unions
|
||||
input: |
|
||||
type Object { id: ID }
|
||||
union Union = Object
|
||||
input Foo { a: Union! }
|
||||
error:
|
||||
message: 'UNION a: field must be one of SCALAR, ENUM, INPUT_OBJECT.'
|
||||
locations: [{line: 3, column: 13}]
|
||||
|
||||
args:
|
||||
- name: Valid arg types
|
||||
input: |
|
||||
input Input { id: ID }
|
||||
enum Enum { A }
|
||||
scalar Scalar
|
||||
|
||||
type Query {
|
||||
f(a: Input, b: Scalar, c: Enum): Boolean!
|
||||
}
|
||||
|
||||
- name: Objects not allowed
|
||||
input: |
|
||||
type Object { id: ID }
|
||||
type Query { f(a: Object): Boolean! }
|
||||
|
||||
error:
|
||||
message: 'cannot use Object as argument a because OBJECT is not a valid input type'
|
||||
locations: [{line: 2, column: 16}]
|
||||
|
||||
- name: Union not allowed
|
||||
input: |
|
||||
type Object { id: ID }
|
||||
union Union = Object
|
||||
type Query { f(a: Union): Boolean! }
|
||||
|
||||
error:
|
||||
message: 'cannot use Union as argument a because UNION is not a valid input type'
|
||||
locations: [{line: 3, column: 16}]
|
||||
|
||||
- name: Interface not allowed
|
||||
input: |
|
||||
interface Interface { id: ID }
|
||||
type Query { f(a: Interface): Boolean! }
|
||||
|
||||
error:
|
||||
message: 'cannot use Interface as argument a because INTERFACE is not a valid input type'
|
||||
locations: [{line: 2, column: 16}]
|
||||
|
||||
enums:
|
||||
- name: must define one or more unique enum values
|
||||
input: |
|
||||
directive @D on ENUM
|
||||
|
||||
# This pattern rejected by parser
|
||||
# enum InvalidEmum1 {}
|
||||
|
||||
enum InvalidEnum2 @D
|
||||
|
||||
enum ValidEnum {
|
||||
FOO
|
||||
}
|
||||
extend enum ValidEnum @D
|
||||
extend enum ValidEnum {
|
||||
BAR
|
||||
}
|
||||
error:
|
||||
message: 'ENUM InvalidEnum2: must define one or more unique enum values.'
|
||||
locations: [{line: 6, column: 6}]
|
||||
- name: check reserved names on type name
|
||||
input: |
|
||||
enum __FooBar {
|
||||
A
|
||||
B
|
||||
}
|
||||
error:
|
||||
message: 'Name "__FooBar" must not begin with "__", which is reserved by GraphQL introspection.'
|
||||
locations: [{line: 1, column: 6}]
|
||||
|
||||
unions:
|
||||
- name: union types must be defined
|
||||
input: |
|
||||
union Foo = Bar | Baz
|
||||
type Bar {
|
||||
id: ID
|
||||
}
|
||||
error:
|
||||
message: "Undefined type \"Baz\"."
|
||||
locations: [{line: 1, column: 7}]
|
||||
- name: union types must be objects
|
||||
input: |
|
||||
union Foo = Baz
|
||||
interface Baz {
|
||||
id: ID
|
||||
}
|
||||
error:
|
||||
message: "UNION type \"Baz\" must be OBJECT."
|
||||
locations: [{line: 1, column: 7}]
|
||||
|
||||
- name: unions of pure type extensions are valid
|
||||
input: |
|
||||
|
||||
type Review {
|
||||
body: String!
|
||||
author: User! @provides(fields: "username")
|
||||
product: Product!
|
||||
}
|
||||
|
||||
extend type User @key(fields: "id") {
|
||||
id: ID! @external
|
||||
reviews: [Review]
|
||||
}
|
||||
|
||||
extend type Product @key(fields: "upc") {
|
||||
upc: String! @external
|
||||
reviews: [Review]
|
||||
}
|
||||
|
||||
union Foo = User | Product
|
||||
scalar _Any
|
||||
scalar _FieldSet
|
||||
directive @external on FIELD_DEFINITION
|
||||
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
|
||||
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
|
||||
directive @key(fields: _FieldSet!) on OBJECT | INTERFACE
|
||||
directive @extends on OBJECT
|
||||
|
||||
|
||||
|
||||
type extensions:
|
||||
- name: can extend non existant types
|
||||
input: |
|
||||
extend type A {
|
||||
name: String
|
||||
}
|
||||
|
||||
|
||||
- name: cannot extend incorret type existant types
|
||||
input: |
|
||||
scalar A
|
||||
extend type A {
|
||||
name: String
|
||||
}
|
||||
error:
|
||||
message: "Cannot extend type A because the base type is a SCALAR, not OBJECT."
|
||||
locations: [{line: 2, column: 13}]
|
||||
|
||||
directives:
|
||||
- name: cannot redeclare directives
|
||||
input: |
|
||||
directive @A on FIELD_DEFINITION
|
||||
directive @A on FIELD_DEFINITION
|
||||
error:
|
||||
message: "Cannot redeclare directive A."
|
||||
locations: [{line: 2, column: 12}]
|
||||
|
||||
- name: can redeclare builtin directives
|
||||
input: |
|
||||
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||
|
||||
- name: must be declared
|
||||
input: |
|
||||
type User {
|
||||
name: String @foo
|
||||
}
|
||||
error:
|
||||
message: "Undefined directive foo."
|
||||
locations: [{line: 2, column: 17}]
|
||||
|
||||
- name: cannot be self-referential
|
||||
input: |
|
||||
directive @A(foo: Int! @A) on FIELD_DEFINITION
|
||||
error:
|
||||
message: "Directive A cannot refer to itself."
|
||||
locations: [{line: 1, column: 25}]
|
||||
- name: check reserved names on type name
|
||||
input: |
|
||||
directive @__A on FIELD_DEFINITION
|
||||
error:
|
||||
message: 'Name "__A" must not begin with "__", which is reserved by GraphQL introspection.'
|
||||
locations: [{line: 1, column: 12}]
|
||||
|
||||
- name: Valid arg types
|
||||
input: |
|
||||
input Input { id: ID }
|
||||
enum Enum { A }
|
||||
scalar Scalar
|
||||
|
||||
directive @A(a: Input, b: Scalar, c: Enum) on FIELD_DEFINITION
|
||||
|
||||
- name: Objects not allowed
|
||||
input: |
|
||||
type Object { id: ID }
|
||||
directive @A(a: Object) on FIELD_DEFINITION
|
||||
|
||||
error:
|
||||
message: 'cannot use Object as argument a because OBJECT is not a valid input type'
|
||||
locations: [{line: 2, column: 14}]
|
||||
|
||||
- name: Union not allowed
|
||||
input: |
|
||||
type Object { id: ID }
|
||||
union Union = Object
|
||||
directive @A(a: Union) on FIELD_DEFINITION
|
||||
|
||||
error:
|
||||
message: 'cannot use Union as argument a because UNION is not a valid input type'
|
||||
locations: [{line: 3, column: 14}]
|
||||
|
||||
- name: Interface not allowed
|
||||
input: |
|
||||
interface Interface { id: ID }
|
||||
directive @A(a: Interface) on FIELD_DEFINITION
|
||||
|
||||
error:
|
||||
message: 'cannot use Interface as argument a because INTERFACE is not a valid input type'
|
||||
locations: [{line: 2, column: 14}]
|
||||
|
||||
- name: Invalid location usage not allowed
|
||||
input: |
|
||||
directive @test on FIELD_DEFINITION
|
||||
input I1 @test { f: String }
|
||||
|
||||
error:
|
||||
message: 'Directive test is not applicable on INPUT_OBJECT.'
|
||||
locations: [{line: 2, column: 11}]
|
||||
|
||||
- name: Valid location usage
|
||||
input: |
|
||||
directive @testInputField on INPUT_FIELD_DEFINITION
|
||||
directive @testField on FIELD_DEFINITION
|
||||
directive @inp on INPUT_OBJECT
|
||||
input I1 @inp { f: String @testInputField }
|
||||
type P { name: String @testField }
|
||||
interface I { id: ID @testField }
|
||||
|
||||
|
||||
entry points:
|
||||
- name: multiple schema entry points
|
||||
input: |
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
scalar Query
|
||||
error:
|
||||
message: "Cannot have multiple schema entry points, consider schema extensions instead."
|
||||
locations: [{line: 4, column: 8}]
|
||||
|
||||
- name: Undefined schema entrypoint
|
||||
input: |
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
error:
|
||||
message: "Schema root query refers to a type Query that does not exist."
|
||||
locations: [{line: 2, column: 3}]
|
||||
|
||||
entry point extensions:
|
||||
- name: Undefined schema entrypoint
|
||||
input: |
|
||||
schema {
|
||||
query: Query
|
||||
}
|
||||
scalar Query
|
||||
extend schema {
|
||||
mutation: Mutation
|
||||
}
|
||||
error:
|
||||
message: "Schema root mutation refers to a type Mutation that does not exist."
|
||||
locations: [{line: 6, column: 3}]
|
||||
|
||||
type references:
|
||||
- name: Field types
|
||||
input: |
|
||||
type User {
|
||||
posts: Post
|
||||
}
|
||||
error:
|
||||
message: "Undefined type Post."
|
||||
locations: [{line: 2, column: 10}]
|
||||
|
||||
- name: Arg types
|
||||
input: |
|
||||
type User {
|
||||
posts(foo: FooBar): String
|
||||
}
|
||||
error:
|
||||
message: "Undefined type FooBar."
|
||||
locations: [{line: 2, column: 14}]
|
||||
|
||||
- name: Directive arg types
|
||||
input: |
|
||||
directive @Foo(foo: FooBar) on FIELD_DEFINITION
|
||||
|
||||
error:
|
||||
message: "Undefined type FooBar."
|
||||
locations: [{line: 1, column: 21}]
|
||||
|
||||
- name: Invalid enum value
|
||||
input: |
|
||||
enum Enum { true }
|
||||
|
||||
error:
|
||||
message: "ENUM Enum: non-enum value true."
|
||||
locations: [{line: 1, column: 6}]
|
||||
Generated
Vendored
-69
@@ -1,69 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/agnivade/levenshtein"
|
||||
)
|
||||
|
||||
// Given an invalid input string and a list of valid options, returns a filtered
|
||||
// list of valid options sorted based on their similarity with the input.
|
||||
func SuggestionList(input string, options []string) []string {
|
||||
var results []string
|
||||
optionsByDistance := map[string]int{}
|
||||
|
||||
for _, option := range options {
|
||||
distance := lexicalDistance(input, option)
|
||||
threshold := calcThreshold(input)
|
||||
if distance <= threshold {
|
||||
results = append(results, option)
|
||||
optionsByDistance[option] = distance
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return optionsByDistance[results[i]] < optionsByDistance[results[j]]
|
||||
})
|
||||
return results
|
||||
}
|
||||
|
||||
func calcThreshold(a string) (threshold int) {
|
||||
// the logic is copied from here
|
||||
// https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/jsutils/suggestionList.ts#L14
|
||||
threshold = int(math.Floor(float64(len(a))*0.4) + 1)
|
||||
|
||||
if threshold < 1 {
|
||||
threshold = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Computes the lexical distance between strings A and B.
|
||||
//
|
||||
// The "distance" between two strings is given by counting the minimum number
|
||||
// of edits needed to transform string A into string B. An edit can be an
|
||||
// insertion, deletion, or substitution of a single character, or a swap of two
|
||||
// adjacent characters.
|
||||
//
|
||||
// Includes a custom alteration from Damerau-Levenshtein to treat case changes
|
||||
// as a single edit which helps identify mis-cased values with an edit distance
|
||||
// of 1.
|
||||
//
|
||||
// This distance can be useful for detecting typos in input or sorting
|
||||
func lexicalDistance(a, b string) int {
|
||||
if a == b {
|
||||
return 0
|
||||
}
|
||||
|
||||
a = strings.ToLower(a)
|
||||
b = strings.ToLower(b)
|
||||
|
||||
// Any case change counts as a single edit
|
||||
if a == b {
|
||||
return 1
|
||||
}
|
||||
|
||||
return levenshtein.ComputeDistance(a, b)
|
||||
}
|
||||
Generated
Vendored
-45
@@ -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
|
||||
}
|
||||
-259
@@ -1,259 +0,0 @@
|
||||
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"
|
||||
)
|
||||
|
||||
var ErrUnexpectedType = errors.New("Unexpected Type")
|
||||
|
||||
// VariableValues coerces and validates variable values
|
||||
func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, error) {
|
||||
coercedVars := map[string]interface{}{}
|
||||
|
||||
validator := varValidator{
|
||||
path: ast.Path{ast.PathName("variable")},
|
||||
schema: schema,
|
||||
}
|
||||
|
||||
for _, v := range op.VariableDefinitions {
|
||||
validator.path = append(validator.path, ast.PathName(v.Variable))
|
||||
|
||||
if !v.Definition.IsInputType() {
|
||||
return nil, gqlerror.ErrorPathf(validator.path, "must an input type")
|
||||
}
|
||||
|
||||
val, hasValue := variables[v.Variable]
|
||||
|
||||
if !hasValue {
|
||||
if v.DefaultValue != nil {
|
||||
var err error
|
||||
val, err = v.DefaultValue.Value(nil)
|
||||
if err != nil {
|
||||
return nil, gqlerror.WrapPath(validator.path, err)
|
||||
}
|
||||
hasValue = true
|
||||
} else if v.Type.NonNull {
|
||||
return nil, gqlerror.ErrorPathf(validator.path, "must be defined")
|
||||
}
|
||||
}
|
||||
|
||||
if hasValue {
|
||||
if val == nil {
|
||||
if v.Type.NonNull {
|
||||
return nil, gqlerror.ErrorPathf(validator.path, "cannot be null")
|
||||
}
|
||||
coercedVars[v.Variable] = nil
|
||||
} else {
|
||||
rv := reflect.ValueOf(val)
|
||||
|
||||
jsonNumber, isJSONNumber := val.(json.Number)
|
||||
if isJSONNumber {
|
||||
if v.Type.NamedType == "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" {
|
||||
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 {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
|
||||
rval, err := validator.validateVarType(v.Type, rv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coercedVars[v.Variable] = rval.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
validator.path = validator.path[0 : len(validator.path)-1]
|
||||
}
|
||||
return coercedVars, nil
|
||||
}
|
||||
|
||||
type varValidator struct {
|
||||
path ast.Path
|
||||
schema *ast.Schema
|
||||
}
|
||||
|
||||
func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflect.Value, *gqlerror.Error) {
|
||||
currentPath := v.path
|
||||
resetPath := func() {
|
||||
v.path = currentPath
|
||||
}
|
||||
defer resetPath()
|
||||
if typ.Elem != nil {
|
||||
if val.Kind() != reflect.Slice {
|
||||
// GraphQL spec says that non-null values should be coerced to an array when possible.
|
||||
// Hence if the value is not a slice, we create a slice and add val to it.
|
||||
slc := reflect.MakeSlice(reflect.SliceOf(val.Type()), 0, 0)
|
||||
slc = reflect.Append(slc, val)
|
||||
val = slc
|
||||
}
|
||||
for i := range val.Len() {
|
||||
resetPath()
|
||||
v.path = append(v.path, ast.PathIndex(i))
|
||||
field := val.Index(i)
|
||||
if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
|
||||
if typ.Elem.NonNull && field.IsNil() {
|
||||
return val, gqlerror.ErrorPathf(v.path, "cannot be null")
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
_, err := v.validateVarType(typ.Elem, field)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
def := v.schema.Types[typ.NamedType]
|
||||
if def == nil {
|
||||
panic(fmt.Errorf("missing def for %s", typ.NamedType))
|
||||
}
|
||||
|
||||
if !typ.NonNull && !val.IsValid() {
|
||||
// If the type is not null and we got a invalid value namely null/nil, then it's valid
|
||||
return val, nil
|
||||
}
|
||||
|
||||
switch def.Kind {
|
||||
case ast.Enum:
|
||||
kind := val.Type().Kind()
|
||||
if kind != reflect.Int && kind != reflect.Int32 && kind != reflect.Int64 && kind != reflect.String {
|
||||
return val, gqlerror.ErrorPathf(v.path, "enums must be ints or strings")
|
||||
}
|
||||
isValidEnum := false
|
||||
for _, enumVal := range def.EnumValues {
|
||||
if strings.EqualFold(val.String(), enumVal.Name) {
|
||||
isValidEnum = true
|
||||
}
|
||||
}
|
||||
if !isValidEnum {
|
||||
return val, gqlerror.ErrorPathf(v.path, "%s is not a valid %s", val.String(), def.Name)
|
||||
}
|
||||
return val, nil
|
||||
case ast.Scalar:
|
||||
kind := val.Type().Kind()
|
||||
switch typ.NamedType {
|
||||
case "Int":
|
||||
if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.Float32 || kind == reflect.Float64 || IsValidIntString(val, kind) {
|
||||
return val, nil
|
||||
}
|
||||
case "Float":
|
||||
if kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || IsValidFloatString(val, kind) {
|
||||
return val, nil
|
||||
}
|
||||
case "String":
|
||||
if kind == reflect.String {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
case "Boolean":
|
||||
if kind == reflect.Bool {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
case "ID":
|
||||
if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
|
||||
return val, nil
|
||||
}
|
||||
default:
|
||||
// assume custom scalars are ok
|
||||
return val, nil
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// check for unknown fields
|
||||
for _, name := range val.MapKeys() {
|
||||
val.MapIndex(name)
|
||||
fieldDef := def.Fields.ForName(name.String())
|
||||
resetPath()
|
||||
v.path = append(v.path, ast.PathName(name.String()))
|
||||
|
||||
switch {
|
||||
case name.String() == "__typename":
|
||||
continue
|
||||
case fieldDef == nil:
|
||||
return val, gqlerror.ErrorPathf(v.path, "unknown field")
|
||||
}
|
||||
}
|
||||
|
||||
for _, fieldDef := range def.Fields {
|
||||
resetPath()
|
||||
v.path = append(v.path, ast.PathName(fieldDef.Name))
|
||||
|
||||
field := val.MapIndex(reflect.ValueOf(fieldDef.Name))
|
||||
if !field.IsValid() {
|
||||
if fieldDef.Type.NonNull {
|
||||
if fieldDef.DefaultValue != nil {
|
||||
var err error
|
||||
_, err = fieldDef.DefaultValue.Value(nil)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return val, gqlerror.ErrorPathf(v.path, "must be defined")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
|
||||
if fieldDef.Type.NonNull && field.IsNil() {
|
||||
return val, gqlerror.ErrorPathf(v.path, "cannot be null")
|
||||
}
|
||||
// allow null object field and skip it
|
||||
if !fieldDef.Type.NonNull && field.IsNil() {
|
||||
continue
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
cval, err := v.validateVarType(fieldDef.Type, field)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
val.SetMapIndex(reflect.ValueOf(fieldDef.Name), cval)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported type %s", def.Kind))
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func IsValidIntString(val reflect.Value, kind reflect.Kind) bool {
|
||||
if kind != reflect.String {
|
||||
return false
|
||||
}
|
||||
_, e := strconv.ParseInt(fmt.Sprintf("%v", val.Interface()), 10, 64)
|
||||
|
||||
return e == nil
|
||||
}
|
||||
|
||||
func IsValidFloatString(val reflect.Value, kind reflect.Kind) bool {
|
||||
if kind != reflect.String {
|
||||
return false
|
||||
}
|
||||
_, e := strconv.ParseFloat(fmt.Sprintf("%v", val.Interface()), 64)
|
||||
return e == nil
|
||||
}
|
||||
-292
@@ -1,292 +0,0 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/gqlparser/ast"
|
||||
)
|
||||
|
||||
type Events struct {
|
||||
operationVisitor []func(walker *Walker, operation *ast.OperationDefinition)
|
||||
field []func(walker *Walker, field *ast.Field)
|
||||
fragment []func(walker *Walker, fragment *ast.FragmentDefinition)
|
||||
inlineFragment []func(walker *Walker, inlineFragment *ast.InlineFragment)
|
||||
fragmentSpread []func(walker *Walker, fragmentSpread *ast.FragmentSpread)
|
||||
directive []func(walker *Walker, directive *ast.Directive)
|
||||
directiveList []func(walker *Walker, directives []*ast.Directive)
|
||||
value []func(walker *Walker, value *ast.Value)
|
||||
variable []func(walker *Walker, variable *ast.VariableDefinition)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func Walk(schema *ast.Schema, document *ast.QueryDocument, observers *Events) {
|
||||
w := Walker{
|
||||
Observers: observers,
|
||||
Schema: schema,
|
||||
Document: document,
|
||||
}
|
||||
|
||||
w.walk()
|
||||
}
|
||||
|
||||
type Walker struct {
|
||||
Context context.Context
|
||||
Observers *Events
|
||||
Schema *ast.Schema
|
||||
Document *ast.QueryDocument
|
||||
|
||||
validatedFragmentSpreads map[string]bool
|
||||
CurrentOperation *ast.OperationDefinition
|
||||
}
|
||||
|
||||
func (w *Walker) walk() {
|
||||
for _, child := range w.Document.Operations {
|
||||
w.validatedFragmentSpreads = make(map[string]bool)
|
||||
w.walkOperation(child)
|
||||
}
|
||||
for _, child := range w.Document.Fragments {
|
||||
w.validatedFragmentSpreads = make(map[string]bool)
|
||||
w.walkFragment(child)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Walker) walkOperation(operation *ast.OperationDefinition) {
|
||||
w.CurrentOperation = operation
|
||||
for _, varDef := range operation.VariableDefinitions {
|
||||
varDef.Definition = w.Schema.Types[varDef.Type.Name()]
|
||||
for _, v := range w.Observers.variable {
|
||||
v(w, varDef)
|
||||
}
|
||||
if varDef.DefaultValue != nil {
|
||||
varDef.DefaultValue.ExpectedType = varDef.Type
|
||||
varDef.DefaultValue.Definition = w.Schema.Types[varDef.Type.Name()]
|
||||
}
|
||||
}
|
||||
|
||||
var def *ast.Definition
|
||||
var loc ast.DirectiveLocation
|
||||
switch operation.Operation {
|
||||
case ast.Query, "":
|
||||
def = w.Schema.Query
|
||||
loc = ast.LocationQuery
|
||||
case ast.Mutation:
|
||||
def = w.Schema.Mutation
|
||||
loc = ast.LocationMutation
|
||||
case ast.Subscription:
|
||||
def = w.Schema.Subscription
|
||||
loc = ast.LocationSubscription
|
||||
}
|
||||
|
||||
for _, varDef := range operation.VariableDefinitions {
|
||||
if varDef.DefaultValue != nil {
|
||||
w.walkValue(varDef.DefaultValue)
|
||||
}
|
||||
w.walkDirectives(varDef.Definition, varDef.Directives, ast.LocationVariableDefinition)
|
||||
}
|
||||
|
||||
w.walkDirectives(def, operation.Directives, loc)
|
||||
w.walkSelectionSet(def, operation.SelectionSet)
|
||||
|
||||
for _, v := range w.Observers.operationVisitor {
|
||||
v(w, operation)
|
||||
}
|
||||
w.CurrentOperation = nil
|
||||
}
|
||||
|
||||
func (w *Walker) walkFragment(it *ast.FragmentDefinition) {
|
||||
def := w.Schema.Types[it.TypeCondition]
|
||||
|
||||
it.Definition = def
|
||||
|
||||
w.walkDirectives(def, it.Directives, ast.LocationFragmentDefinition)
|
||||
w.walkSelectionSet(def, it.SelectionSet)
|
||||
|
||||
for _, v := range w.Observers.fragment {
|
||||
v(w, it)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Walker) walkDirectives(parentDef *ast.Definition, directives []*ast.Directive, location ast.DirectiveLocation) {
|
||||
for _, dir := range directives {
|
||||
def := w.Schema.Directives[dir.Name]
|
||||
dir.Definition = def
|
||||
dir.ParentDefinition = parentDef
|
||||
dir.Location = location
|
||||
|
||||
for _, arg := range dir.Arguments {
|
||||
var argDef *ast.ArgumentDefinition
|
||||
if def != nil {
|
||||
argDef = def.Arguments.ForName(arg.Name)
|
||||
}
|
||||
|
||||
w.walkArgument(argDef, arg)
|
||||
}
|
||||
|
||||
for _, v := range w.Observers.directive {
|
||||
v(w, dir)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range w.Observers.directiveList {
|
||||
v(w, directives)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Walker) walkValue(value *ast.Value) {
|
||||
if value.Kind == ast.Variable && w.CurrentOperation != nil {
|
||||
value.VariableDefinition = w.CurrentOperation.VariableDefinitions.ForName(value.Raw)
|
||||
if value.VariableDefinition != nil {
|
||||
value.VariableDefinition.Used = true
|
||||
}
|
||||
}
|
||||
|
||||
if value.Kind == ast.ObjectValue {
|
||||
for _, child := range value.Children {
|
||||
if value.Definition != nil {
|
||||
fieldDef := value.Definition.Fields.ForName(child.Name)
|
||||
if fieldDef != nil {
|
||||
child.Value.ExpectedType = fieldDef.Type
|
||||
child.Value.Definition = w.Schema.Types[fieldDef.Type.Name()]
|
||||
}
|
||||
}
|
||||
w.walkValue(child.Value)
|
||||
}
|
||||
}
|
||||
|
||||
if value.Kind == ast.ListValue {
|
||||
for _, child := range value.Children {
|
||||
if value.ExpectedType != nil && value.ExpectedType.Elem != nil {
|
||||
child.Value.ExpectedType = value.ExpectedType.Elem
|
||||
child.Value.Definition = value.Definition
|
||||
}
|
||||
|
||||
w.walkValue(child.Value)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range w.Observers.value {
|
||||
v(w, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Walker) walkArgument(argDef *ast.ArgumentDefinition, arg *ast.Argument) {
|
||||
if argDef != nil {
|
||||
arg.Value.ExpectedType = argDef.Type
|
||||
arg.Value.Definition = w.Schema.Types[argDef.Type.Name()]
|
||||
}
|
||||
|
||||
w.walkValue(arg.Value)
|
||||
}
|
||||
|
||||
func (w *Walker) walkSelectionSet(parentDef *ast.Definition, it ast.SelectionSet) {
|
||||
for _, child := range it {
|
||||
w.walkSelection(parentDef, child)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Walker) walkSelection(parentDef *ast.Definition, it ast.Selection) {
|
||||
switch it := it.(type) {
|
||||
case *ast.Field:
|
||||
var def *ast.FieldDefinition
|
||||
if it.Name == "__typename" {
|
||||
def = &ast.FieldDefinition{
|
||||
Name: "__typename",
|
||||
Type: ast.NamedType("String", nil),
|
||||
}
|
||||
} else if parentDef != nil {
|
||||
def = parentDef.Fields.ForName(it.Name)
|
||||
}
|
||||
|
||||
it.Definition = def
|
||||
it.ObjectDefinition = parentDef
|
||||
|
||||
var nextParentDef *ast.Definition
|
||||
if def != nil {
|
||||
nextParentDef = w.Schema.Types[def.Type.Name()]
|
||||
}
|
||||
|
||||
for _, arg := range it.Arguments {
|
||||
var argDef *ast.ArgumentDefinition
|
||||
if def != nil {
|
||||
argDef = def.Arguments.ForName(arg.Name)
|
||||
}
|
||||
|
||||
w.walkArgument(argDef, arg)
|
||||
}
|
||||
|
||||
w.walkDirectives(nextParentDef, it.Directives, ast.LocationField)
|
||||
w.walkSelectionSet(nextParentDef, it.SelectionSet)
|
||||
|
||||
for _, v := range w.Observers.field {
|
||||
v(w, it)
|
||||
}
|
||||
|
||||
case *ast.InlineFragment:
|
||||
it.ObjectDefinition = parentDef
|
||||
|
||||
nextParentDef := parentDef
|
||||
if it.TypeCondition != "" {
|
||||
nextParentDef = w.Schema.Types[it.TypeCondition]
|
||||
}
|
||||
|
||||
w.walkDirectives(nextParentDef, it.Directives, ast.LocationInlineFragment)
|
||||
w.walkSelectionSet(nextParentDef, it.SelectionSet)
|
||||
|
||||
for _, v := range w.Observers.inlineFragment {
|
||||
v(w, it)
|
||||
}
|
||||
|
||||
case *ast.FragmentSpread:
|
||||
def := w.Document.Fragments.ForName(it.Name)
|
||||
it.Definition = def
|
||||
it.ObjectDefinition = parentDef
|
||||
|
||||
var nextParentDef *ast.Definition
|
||||
if def != nil {
|
||||
nextParentDef = w.Schema.Types[def.TypeCondition]
|
||||
}
|
||||
|
||||
w.walkDirectives(nextParentDef, it.Directives, ast.LocationFragmentSpread)
|
||||
|
||||
if def != nil && !w.validatedFragmentSpreads[def.Name] {
|
||||
// prevent inifinite recursion
|
||||
w.validatedFragmentSpreads[def.Name] = true
|
||||
w.walkSelectionSet(nextParentDef, def.SelectionSet)
|
||||
}
|
||||
|
||||
for _, v := range w.Observers.fragmentSpread {
|
||||
v(w, it)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported %T", it))
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ const (
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (keyType *KeyType) Accept(value interface{}) error {
|
||||
func (keyType *KeyType) Accept(value any) error {
|
||||
var tmp KeyType
|
||||
switch x := value.(type) {
|
||||
case string:
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ const (
|
||||
|
||||
// Accept is used when conversion from values given by
|
||||
// outside sources (such as JSON payloads) is required
|
||||
func (signature *SignatureAlgorithm) Accept(value interface{}) error {
|
||||
func (signature *SignatureAlgorithm) Accept(value any) error {
|
||||
var tmp SignatureAlgorithm
|
||||
switch x := value.(type) {
|
||||
case string:
|
||||
|
||||
+2
-2
@@ -39,12 +39,12 @@ func newECDSAPrivateKey(key *ecdsa.PrivateKey) (*ECDSAPrivateKey, error) {
|
||||
}
|
||||
|
||||
// Materialize returns the EC-DSA public key represented by this JWK
|
||||
func (k ECDSAPublicKey) Materialize() (interface{}, error) {
|
||||
func (k ECDSAPublicKey) Materialize() (any, error) {
|
||||
return k.key, nil
|
||||
}
|
||||
|
||||
// Materialize returns the EC-DSA private key represented by this JWK
|
||||
func (k ECDSAPrivateKey) Materialize() (interface{}, error) {
|
||||
func (k ECDSAPrivateKey) Materialize() (any, error) {
|
||||
return k.key, nil
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -18,15 +18,15 @@ const (
|
||||
|
||||
// Headers provides a common interface to all future possible headers
|
||||
type Headers interface {
|
||||
Get(string) (interface{}, bool)
|
||||
Set(string, interface{}) error
|
||||
Walk(func(string, interface{}) error) error
|
||||
Get(string) (any, bool)
|
||||
Set(string, any) error
|
||||
Walk(func(string, any) error) error
|
||||
GetAlgorithm() jwa.SignatureAlgorithm
|
||||
GetKeyID() string
|
||||
GetKeyOps() KeyOperationList
|
||||
GetKeyType() jwa.KeyType
|
||||
GetKeyUsage() string
|
||||
GetPrivateParams() map[string]interface{}
|
||||
GetPrivateParams() map[string]any
|
||||
}
|
||||
|
||||
// StandardHeaders stores the common JWK parameters
|
||||
@@ -36,7 +36,7 @@ type StandardHeaders struct {
|
||||
KeyOps KeyOperationList `json:"key_ops,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.3
|
||||
KeyType jwa.KeyType `json:"kty,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.1
|
||||
KeyUsage string `json:"use,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.2
|
||||
PrivateParams map[string]interface{} `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4
|
||||
PrivateParams map[string]any `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4
|
||||
}
|
||||
|
||||
// GetAlgorithm is a convenience function to retrieve the corresponding value stored in the StandardHeaders
|
||||
@@ -68,12 +68,12 @@ func (h *StandardHeaders) GetKeyUsage() string {
|
||||
}
|
||||
|
||||
// GetPrivateParams is a convenience function to retrieve the corresponding value stored in the StandardHeaders
|
||||
func (h *StandardHeaders) GetPrivateParams() map[string]interface{} {
|
||||
func (h *StandardHeaders) GetPrivateParams() map[string]any {
|
||||
return h.PrivateParams
|
||||
}
|
||||
|
||||
// Get is a general getter function for JWK StandardHeaders structure
|
||||
func (h *StandardHeaders) Get(name string) (interface{}, bool) {
|
||||
func (h *StandardHeaders) Get(name string) (any, bool) {
|
||||
switch name {
|
||||
case AlgorithmKey:
|
||||
alg := h.GetAlgorithm()
|
||||
@@ -117,7 +117,7 @@ func (h *StandardHeaders) Get(name string) (interface{}, bool) {
|
||||
}
|
||||
|
||||
// Set is a general getter function for JWK StandardHeaders structure
|
||||
func (h *StandardHeaders) Set(name string, value interface{}) error {
|
||||
func (h *StandardHeaders) Set(name string, value any) error {
|
||||
switch name {
|
||||
case AlgorithmKey:
|
||||
var acceptor jwa.SignatureAlgorithm
|
||||
@@ -149,7 +149,7 @@ func (h *StandardHeaders) Set(name string, value interface{}) error {
|
||||
}
|
||||
return fmt.Errorf("invalid value for %s key: %T", KeyUsageKey, value)
|
||||
case PrivateParamsKey:
|
||||
if v, ok := value.(map[string]interface{}); ok {
|
||||
if v, ok := value.(map[string]any); ok {
|
||||
h.PrivateParams = v
|
||||
return nil
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func (h *StandardHeaders) Set(name string, value interface{}) error {
|
||||
}
|
||||
|
||||
// Walk iterates over all JWK standard headers fields while applying a function to its value.
|
||||
func (h StandardHeaders) Walk(f func(string, interface{}) error) error {
|
||||
func (h StandardHeaders) Walk(f func(string, any) error) error {
|
||||
for _, key := range []string{AlgorithmKey, KeyIDKey, KeyOpsKey, KeyTypeKey, KeyUsageKey, PrivateParamsKey} {
|
||||
if v, ok := h.Get(key); ok {
|
||||
if err := f(key, v); err != nil {
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ type Key interface {
|
||||
// RSA types would create *rsa.PublicKey or *rsa.PrivateKey,
|
||||
// EC types would create *ecdsa.PublicKey or *ecdsa.PrivateKey,
|
||||
// and OctetSeq types create a []byte key.
|
||||
Materialize() (interface{}, error)
|
||||
Materialize() (any, error)
|
||||
GenerateKey(*RawKeyJSON) error
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@ import (
|
||||
// For rsa key types *rsa.PublicKey is returned; for ecdsa key types *ecdsa.PublicKey;
|
||||
// for byte slice (raw) keys, the key itself is returned. If the corresponding
|
||||
// public key cannot be deduced, an error is returned
|
||||
func GetPublicKey(key interface{}) (interface{}, error) {
|
||||
func GetPublicKey(key any) (any, error) {
|
||||
if key == nil {
|
||||
return nil, errors.New("jwk.New requires a non-nil key")
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func GetPublicKey(key interface{}) (interface{}, error) {
|
||||
switch v := key.(type) {
|
||||
// Mental note: although Public() is defined in both types,
|
||||
// you can not coalesce the clauses for rsa.PrivateKey and
|
||||
// ecdsa.PrivateKey, as then `v` becomes interface{}
|
||||
// ecdsa.PrivateKey, as then `v` becomes any
|
||||
// b/c the compiler cannot deduce the exact type.
|
||||
case *rsa.PrivateKey:
|
||||
return v.Public(), nil
|
||||
@@ -37,7 +37,7 @@ func GetPublicKey(key interface{}) (interface{}, error) {
|
||||
}
|
||||
|
||||
// GetKeyTypeFromKey creates a jwk.Key from the given key.
|
||||
func GetKeyTypeFromKey(key interface{}) jwa.KeyType {
|
||||
func GetKeyTypeFromKey(key any) jwa.KeyType {
|
||||
|
||||
switch key.(type) {
|
||||
case *rsa.PrivateKey, *rsa.PublicKey:
|
||||
@@ -52,7 +52,7 @@ func GetKeyTypeFromKey(key interface{}) jwa.KeyType {
|
||||
}
|
||||
|
||||
// New creates a jwk.Key from the given key.
|
||||
func New(key interface{}) (Key, error) {
|
||||
func New(key any) (Key, error) {
|
||||
if key == nil {
|
||||
return nil, errors.New("jwk.New requires a non-nil key")
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ const (
|
||||
)
|
||||
|
||||
// Accept determines if Key Operation is valid
|
||||
func (keyOperationList *KeyOperationList) Accept(v interface{}) error {
|
||||
func (keyOperationList *KeyOperationList) Accept(v any) error {
|
||||
switch x := v.(type) {
|
||||
case KeyOperationList:
|
||||
*keyOperationList = x
|
||||
|
||||
+2
-2
@@ -65,7 +65,7 @@ func newRSAPrivateKey(key *rsa.PrivateKey) (*RSAPrivateKey, error) {
|
||||
}
|
||||
|
||||
// Materialize returns the standard RSA Public Key representation stored in the internal representation
|
||||
func (k *RSAPublicKey) Materialize() (interface{}, error) {
|
||||
func (k *RSAPublicKey) Materialize() (any, error) {
|
||||
if k.key == nil {
|
||||
return nil, errors.New("key has no rsa.PublicKey associated with it")
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func (k *RSAPublicKey) Materialize() (interface{}, error) {
|
||||
}
|
||||
|
||||
// Materialize returns the standard RSA Private Key representation stored in the internal representation
|
||||
func (k *RSAPrivateKey) Materialize() (interface{}, error) {
|
||||
func (k *RSAPrivateKey) Materialize() (any, error) {
|
||||
if k.key == nil {
|
||||
return nil, errors.New("key has no rsa.PrivateKey associated with it")
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ func newSymmetricKey(key []byte) (*SymmetricKey, error) {
|
||||
|
||||
// Materialize returns the octets for this symmetric key.
|
||||
// Since this is a symmetric key, this just calls Octets
|
||||
func (s SymmetricKey) Materialize() (interface{}, error) {
|
||||
func (s SymmetricKey) Materialize() (any, error) {
|
||||
return s.Octets(), nil
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -20,8 +20,8 @@ const (
|
||||
|
||||
// Headers provides a common interface for common header parameters
|
||||
type Headers interface {
|
||||
Get(string) (interface{}, bool)
|
||||
Set(string, interface{}) error
|
||||
Get(string) (any, bool)
|
||||
Set(string, any) error
|
||||
GetAlgorithm() jwa.SignatureAlgorithm
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ type StandardHeaders struct {
|
||||
JWK string `json:"jwk,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.3
|
||||
JWKSetURL string `json:"jku,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.2
|
||||
KeyID string `json:"kid,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4
|
||||
PrivateParams map[string]interface{} `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.9
|
||||
PrivateParams map[string]any `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.9
|
||||
Type string `json:"typ,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.9
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func (h *StandardHeaders) GetAlgorithm() jwa.SignatureAlgorithm {
|
||||
}
|
||||
|
||||
// Get is a general getter function for StandardHeaders structure
|
||||
func (h *StandardHeaders) Get(name string) (interface{}, bool) {
|
||||
func (h *StandardHeaders) Get(name string) (any, bool) {
|
||||
switch name {
|
||||
case AlgorithmKey:
|
||||
v := h.Algorithm
|
||||
@@ -99,7 +99,7 @@ func (h *StandardHeaders) Get(name string) (interface{}, bool) {
|
||||
}
|
||||
|
||||
// Set is a general setter function for StandardHeaders structure
|
||||
func (h *StandardHeaders) Set(name string, value interface{}) error {
|
||||
func (h *StandardHeaders) Set(name string, value any) error {
|
||||
switch name {
|
||||
case AlgorithmKey:
|
||||
if err := h.Algorithm.Accept(value); err != nil {
|
||||
@@ -137,7 +137,7 @@ func (h *StandardHeaders) Set(name string, value interface{}) error {
|
||||
}
|
||||
return fmt.Errorf("invalid value for %s key: %T", KeyIDKey, value)
|
||||
case PrivateParamsKey:
|
||||
if v, ok := value.(map[string]interface{}); ok {
|
||||
if v, ok := value.(map[string]any); ok {
|
||||
h.PrivateParams = v
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user