A port of the parser from graphql-js into golang

gqlparser CircleCI Go Report Card Coverage Status

This is a parser for graphql, written to mirror the graphql-js reference implementation as closely while remaining idiomatic and easy to use.

spec target: June 2018 (Schema definition language, block strings as descriptions, error paths & extension)

This parser is used by gqlgen, and it should be reasonablly stable.

Guiding principles:

  • maintainability: It should be easy to stay up to date with the spec
  • well tested: It shouldnt need a graphql server to validate itself. Changes to this repo should be self contained.
  • server agnostic: It should be usable by any of the graphql server implementations, and any graphql client tooling.
  • idiomatic & stable api: It should follow go best practices, especially around forwards compatibility.
  • fast: Where it doesnt impact on the above it should be fast. Avoid unnecessary allocs in hot paths.
  • close to reference: Where it doesnt impact on the above, it should stay close to the graphql/graphql-js reference implementation.
Comments
  • update Node.js harness

    update Node.js harness

    Rebased #178 after merging #169 as it adds to gqlparser support for e.g. interface Canine implements Mammal ... @Code-Hex @vvakame @wilhelmeek If you have ideas as to why the tests continue to fail, I would appreciate a fix.

  • What is __DirectiveResolver?

    What is __DirectiveResolver?

    Hello,

    Just by updating from v2.1.0 to v2.2.0 I noticed in my generated code I have now a new resolver:

    __DirectiveResolver

    I guess it's linked to: https://github.com/graphql/graphql-spec/pull/510

    I don't now if it's a bug, or if I have to implement something :)

    type ResolverRoot interface {
           // ...
    	__Directive() __DirectiveResolver
    }
    
    
    type __DirectiveResolver interface {
    	IsRepeatable(ctx context.Context, obj *introspection.Directive) (bool, error)
    }
    
  • Fix wrong validation on input field with default value

    Fix wrong validation on input field with default value

    Avoid must be defined error on an input field having a default value defined in the schema:

    type Query {
        structArg(i: InputType!): Boolean!
    }
    input InputType {
        name: String!
        defaultName: String! = "defaultFoo"
    }
    

    With variables:

    {
      "var": {
         "name": "foo"
      }
    }
    

    Before: Error received: input: variable.defaultName must be defined

    After: No more errors received, this is the expected behavior because a default value is defined in the schema.

  • gqlparser accepts a schema with no query type and validates any query against it

    gqlparser accepts a schema with no query type and validates any query against it

    Consider the following schema:

    type T { f: String }
    

    Note the lack of a Query type (or a schema declaration pointing to an alternative entrypoint); you can't make any queries against it. However:

    1. gqlparser considers this schema valid; this is sort of debatable but the spec says "The query root operation type must be provided and must be an Object type." and here the query root operation type defaults to Query which is not a valid object type.
    2. Worse, gqlparser will validate any (otherwise syntactically correct) query against such a schema, e.g. the query { f { g } } will validate; this is clearly wrong. Indeed the same thing happens with an entirely empty schema.

    Here's a test case for your convenience (I just shoved it in a random file since I wasn't sure how the tests are organized):

    diff --git validator/imported/spec/schemas.yml validator/imported/spec/schemas.yml
    index e7db1f3..5eeafed 100644
    --- validator/imported/spec/schemas.yml
    +++ validator/imported/spec/schemas.yml
    @@ -487,3 +487,4 @@
       }
     
       scalar Any
    +- ""
    diff --git validator/spec/FragmentsOnCompositeTypes.spec.yml validator/spec/FragmentsOnCompositeTypes.spec.yml
    index ec8ad0b..012c154 100644
    --- validator/spec/FragmentsOnCompositeTypes.spec.yml
    +++ validator/spec/FragmentsOnCompositeTypes.spec.yml
    @@ -7,3 +7,10 @@
       errors:
         - message: Fragment "c" cannot condition on non composite type "Float".
         - message: Cannot spread fragment "c" within itself.
    +
    +- name: Empty schema
    +  schema: 20
    +  query: |
    +    { f { g } }
    +  errors:
    +    - message: Something should fail!
    
  • Fix arguments comparison logic for overlapping fields

    Fix arguments comparison logic for overlapping fields

    The current argument comparison seems different from graphql-js which is resulting in unexpected behavior like this. Instead of comparing all the arguments to each other, we just need to compare the corresponding ones (with the same argument.Name)

  • AST String method

    AST String method

    Would like a method to generate the string representation of an AST object that could be directly used in a query. For example, if had a Field that was generated from

    object(id:$id){ id name value subobject(index:9){ id name }}
    

    would like to be able to call something like

    ast.String(field)
    

    to return an equivalent string.

    I wrote a utility function to do this in my own project, but it is specific to ast.Field and doubt it covers all possible definitions:

    func fieldQueryString(field *ast.Field) string {
    	query := field.Name
    	if len(field.Arguments) > 0 {
    		query += "("
    		for _, arg := range field.Arguments {
    			query += arg.Name + ":"
    			query += field.Arguments.ForName(arg.Name).Value.String()
    		}
    		query += ")"
    	}
    	if len(field.SelectionSet) > 0 {
    		query += "{"
    		for _, f := range field.SelectionSet {
    			field := f.(*ast.Field)
    			query += fieldQueryString(field) + ","
    		}
    		query += "}"
    	}
    
    	return query
    }
    

    I think this would be generally useful for logging/debugging.

    Apologies if this already exists, please point to where it is found if exists.

  • Parser works but there are some syntax errors

    Parser works but there are some syntax errors

    Does the parser work as validator? In my case, parser works even if some syntax errors. I'm using Rego language, which use your parser. And having such case on the screen from Rego playground, where I use your parser give me OK, but actually it's wrong. The question is does you have a validate function separatly? Or don't have at all? Снимок экрана 2022-09-13 в 10 24 30 Снимок экрана 2022-09-13 в 10 29 15

  • Memory leak at parser.parseField

    Memory leak at parser.parseField

    Hello! I have a memory issue while using gqlgen websocket transport. I measured memory using pprof and it seems there's a memory leak in gqlparser's code. Here's a dump https://gist.github.com/fletcherist/94f0bb3bc7b09ec5177fccd6cdf4d2d5

    Also attaching some screenshots which points to gqlparser

    Screenshot 2022-05-03 at 11 53 38 AM Screenshot 2022-05-03 at 11 53 15 AM
  • Distribute prelude string over multiple lines

    Distribute prelude string over multiple lines

    Having one big long string literal in validator/prelude.go is a bit problematic. Foremostly it makes it hard to see what changes have been made in git history or blame. (I was trying to check if/when description: String field has been added to type __Schema.)

    Is there any reason not to have it extend over multiple lines? (String literals joined with the + operator are combined into one literal at compile time, I believe.)

    Perhaps even better would be to embed it (//go:embed), since go.mod is already at Go 1.16. (File embedding was added in 1.16.)

  • Fix for update by reference of default variable values

    Fix for update by reference of default variable values

    So Field "..." argument "..." of type "...!" is required but not provided. validation would always work, despite the order of the calls.

    Context of the issue

    # Note: there is default enum value in variables
    query SomeOperation ($locale: Locale! = DE) {
    	myAction(myEnum: $locale) {
    		id
    	}
    }
    
    query SomeOperation {
    	# Note: Not providing mandatory parameter: (myEnum: Locale!)
    	myAction {
    		id
    	}
    }
    
    • Before fix: Missing field not caught
    • After fix: Field "myAction" argument "myEnum" of type "Locale!" is required but not provided.

    Cause

    VariablesInAllowedPosition rule updates value.ExpectedType.NonNull = false, resulting in ProvidedRequiredArguments rule to have argDef.Type.NonNull changed on the next call.

  • make reproducable importer

    make reproducable importer

    current importer can't reproducable. We can't know what version of graphql-js does we use?.

    Current implementations use e6c36e0725ea0aabad1a19c68e54180fb7092e3a.

    latest commit of values_of_correct_type.go is Jul 19, 2018. https://github.com/vektah/gqlparser/commits/master/validator/rules/values_of_correct_type.go

    I picked up it from https://github.com/graphql/graphql-js/commits/master/src/validation/rules/ValuesOfCorrectType.js . e6c36e is made at Jul 18, 2018.


    So, I tried to update latest one. But I can't solve flowtype issue... 😿

    SyntaxError: .../gqlparser/validator/imported/graphql-js/src/error/GraphQLError.js: Identifier 'GraphQLError' has already been declared (86:16)
    
      84 | }
      85 |
    > 86 | export function GraphQLError( // eslint-disable-line no-redeclare
         |                 ^
      87 |   message: string,
      88 |   nodes?: $ReadOnlyArray<ASTNode> | ASTNode | void,
      89 |   source?: ?Source,
    

    https://github.com/vvakame/gqlparser/commits/improve-importer-r3 https://github.com/vvakame/gqlparser/commit/895df5cd7eaca64ea88786d9cb0eeadd46a26278

    Do you advanced to babel & flowtype ...?

  • Add `Is(err error) bool` to gqlerror to work with `errors.Is`

    Add `Is(err error) bool` to gqlerror to work with `errors.Is`

    What happened?

    Trying to compare two gqlerrors using either errors.Is or something like https://github.com/google/go-cmp with cmpopts.EquateErrors fails when the two errors have all the same fields, but are different structs.

    What did you expect?

    The comparison to succeed. Because gqlerror does not implement an Is method, it fails even though they are for all intents and purposes equal.

    versions

    • go list -m github.com/vektah/gqlparser/v2? v2.5.1
    • go version? v1.19.4
  • Unexpected Name

    Unexpected Name "directive"

    What happened?

    I got error while applying http://github.com/Yamashou/gqlgenc to my schemas. I discovered similar issue at that repo first (https://github.com/Yamashou/gqlgenc/issues/88) but traced error back to this parser. I debugged code generation and found that my query and schema values in plugin config referenced same directory, and in this folder there is a directive file (it is first file in the directory, so it failed pretty fast), containing directive @access(num: Int) on FIELD_DEFINITION I use gqlgen for generating my models, and it handles such structure nicely. However, query doc parser here is unable to handle directive token.

    What did you expect?

    I expect query parser not to fail, and parser to parse my directive correctly. I might be wrong putting schema and query under same folder, but it worked for me with gqlgen server generation, and now I decided to start generating client as well. ❓ So, is it a hard requirement to keep directives out of query folder? ❓ Looks like you can only put query, mutation, subscription and fragment there.

    Minimal graphql.schema and models to reproduce

    put following into file in under query folder: directive @access(num: Int) on FIELD_DEFINITION and run query parser on that document.

    versions

    • go list -m github.com/vektah/gqlparser/v2? took latest master aed070fd commit. github.com/vektah/gqlparser/v2
    • go version? go version go1.19.3 windows/amd64

    Thank you

  • proposal: Add benchmark

    proposal: Add benchmark

    Hello!

    I think the guiding principles of gqlparser is excellent. And part of them says the following.

    fast: Where it doesn't impact on the above it should be fast. Avoid unnecessary allocs in hot paths.
    

    In fact it is fast enough to run LoadQuery in 4661 ns/op.

    ryicoh@ryicohs-MacBook-Air gqlparser % go test -bench BenchmarkLoadQuery -benchmem
    goos: darwin
    goarch: arm64
    pkg: github.com/vektah/gqlparser/v2
    BenchmarkLoadQuery-8      251343              4661 ns/op            6073 B/op        141 allocs/op
    

    To keep and improve the performance, I propose to add benchmarks and reporting per Pull Request by Github Actions. I've made the draft and you can see in PR. https://github.com/vektah/gqlparser/pull/245

  • Add benchmarks and workflows using benchstat

    Add benchmarks and workflows using benchstat

    This is a draft of issue https://github.com/vektah/gqlparser/issues/246.

    PR includes:

    • Benchmarks of LoadSchema and LoadQuery in gqlparser_bench_test.go.
    • GHA workflow to report benchmark comparisons with master branch to PR

    The reporting is displayed like this. Screen Shot 2022-09-25 at 14 27 12

    https://github.com/ryicoh/gqlparser/pull/3#issuecomment-1257117351

    There are a lot of things to discuss.

    • Is benchmarking really necessary?
    • How about schemas and queries for benchmarks. Test data already exists here. validator/testdata
    • Use golang.org/x/perf/cmd/benchstat ?
    • Made PAT(Personal Access Token) for a reporting, but how do we manage the Secret? https://github.com/vektah/gqlparser/blob/4a7076be7757e8b4853c0f46c688b24d57e683b1/.github/workflows/pull-request.yml#L111
  • Invalid but legal cyclic dependency

    Invalid but legal cyclic dependency

    https://go.dev/play/p/OpXdRkWugrA

    In the example above I demonstrate a violation of the GraphQL spec:

    input T { self: T! }
    

    Cyclic dependency is illegal for input types.

  • Missing `Query` from the `query` field of `schema`

    Missing `Query` from the `query` field of `schema`

    FormatSchema method omits schema if schema.query is Query (code). I confirmed the same behavior in case of Mutation and Subscription. Why does it omits Query, Mutation or Subscription from schema? If the process to omit Query, Mutation and Subscription is not needed, I would like to create a PR and fix it.

    Thank you.

    package main
    
    import (
    	"bytes"
    	"fmt"
    
    	"github.com/vektah/gqlparser/v2"
    	"github.com/vektah/gqlparser/v2/ast"
    	"github.com/vektah/gqlparser/v2/formatter"
    )
    
    func main() {
    	strSchema := `
    	schema {
    		query: Query
    	}
    	type Query {
    		noop: Boolean!
    	}`
    
    	var buf1 bytes.Buffer
    	f := formatter.NewFormatter(&buf1, formatter.WithIndent("  "))
    	schema, _ := gqlparser.LoadSchema(&ast.Source{
    		Input: strSchema,
    	})
    	f.FormatSchema(schema)
    	fmt.Println("formatted schema1:\n", buf1.String())
    
    	strSchema = `
    	schema {
    		query: RenamedQuery
    	}
    	type RenamedQuery {
    		noop: Boolean!
    	}`
    
    	var buf2 bytes.Buffer
    	f = formatter.NewFormatter(&buf2, formatter.WithIndent("  "))
    	schema, _ = gqlparser.LoadSchema(&ast.Source{
    		Input: strSchema,
    	})
    	f.FormatSchema(schema)
    	fmt.Println("formatted schema2:\n", buf2.String())
    }
    
    $ go run main.go
    formatted schema1:
     type Query {
      noop: Boolean!
    }
    
    formatted schema2:
     schema {
      query: RenamedQuery
    }
    type RenamedQuery {
      noop: Boolean!
    }
    
    
Related tags
Split multiple Kubernetes files into smaller files with ease. Split multi-YAML files into individual files.

Split multiple Kubernetes files into smaller files with ease. Split multi-YAML files into individual files.

Dec 29, 2022
Split multiple Kubernetes files into smaller files with ease. Split multi-YAML files into individual files.

kubectl-slice: split Kubernetes YAMLs into files kubectl-slice is a neat tool that allows you to split a single multi-YAML Kubernetes manifest into mu

Jan 3, 2023
go generate based graphql server library
go generate based graphql server library

gqlgen What is gqlgen? gqlgen is a Go library for building GraphQL servers without any fuss. gqlgen is based on a Schema first approach — You get to D

Dec 29, 2022
This is Go library for building GraphQL client with gqlgen

gqlgenc What is gqlgenc ? This is Go library for building GraphQL client with gqlgen Motivation Now, if you build GraphQL api client for Go, have choi

Jan 7, 2023
The easiest way to make API documents for GraphQL

Document Generator for GraphQL gqldoc is now alpha gqldoc is command line tool to generate documents from GraphQL schema or your GraphQL endpoint. the

Dec 20, 2022
gqlanalysis makes easy to develop static analysis tools for GraphQL in Go.
gqlanalysis makes easy to develop static analysis tools for GraphQL in Go.

gqlanalysis gqlanalysis defines the interface between a modular static analysis for GraphQL in Go. gqlanalysis is inspired by go/analysis. gqlanalysis

Dec 14, 2022
GoLang port of Google's libphonenumber library

phonenumbers golang port of Google's libphonenumber, forked from libphonenumber from ttacon which in turn is a port of the original Java library. You

Jan 4, 2023
Serial-locate - A simple tool for searching the COM port

serial-locate A simple tool for searching the COM port Usage Direct query serial

Jan 18, 2022
Golang flags parser with zero dependency

flags Golang flags parser with zero dependency. Usage See simple.go for basic usage. Concept flags gives a simple way to get flag's value from argumen

Jan 16, 2022
bebop is a bebop parser written in Go, for generating Go code.

bebop is a bebop parser written in Go, for generating Go code. bebop can read .bop files and output .go files representing them: package main i

Dec 24, 2022
A parser for Ontario's baby name data
A parser for Ontario's baby name data

obnp What? A parser for Ontario's baby name data Why? I wanted to see if a specific name existed in both the male and female datasets. This tool is mo

Mar 15, 2022
A data parser lib for Go with pythonic grammar sugar and as concern as possible for high performance

mapinterface - A data parser lib for Go with pythonic grammar sugar and as concern as possible for high performance mapinterface 旨在消灭对map/list解析而产生的层层

Nov 10, 2021
Getting into Golang 1.18

Go 1.18 New features of Golang 1.18 with tutorial and examples. In this repository I introduce new features of Golang version 1.18 with their examples

Aug 28, 2022
libraries for various programming languages that make it easy to generate per-process trace files that can be loaded into chrome://tracing
libraries for various programming languages that make it easy to generate per-process trace files that can be loaded into chrome://tracing

chrometracing: chrome://tracing trace_event files The chrometracing directory contains libraries for various programming languages that make it easy t

Oct 6, 2022
Go library for decoding generic map values into native Go structures and vice versa.

mapstructure mapstructure is a Go library for decoding generic map values to structures and vice versa, while providing helpful error handling. This l

Dec 28, 2022
go-i18n is a Go package and a command that helps you translate Go programs into multiple languages.

go-i18n is a Go package and a command that helps you translate Go programs into multiple languages.

Jan 2, 2023
simple GitHub action to parse Markdown Links into a .yaml file for Hugo

Obsidian Link Scrapper Used by Quartz This repository comes to you in two parts. GitHub Action (scrapes links into a .yml file) Hugo Partial (turns .y

Dec 30, 2022
Chaos Engineering tool for introducing failure into syscalls

Syscall monkey Chaos Engineering tool for tampering with syscalls.

Jun 11, 2022
A Go utility to convert Go example tests into jupyter notebooks.

go2colab Scientists (my main project's users) love jupyter notebook tutorials pkg.dev.go's runnable playground doesn't support file IO but I love exam

Jul 10, 2022