Giter Site home page Giter Site logo

graphql-rust / graphql-parser Goto Github PK

View Code? Open in Web Editor NEW
349.0 14.0 76.0 183 KB

A graphql query language and schema definition language parser and formatter for rust

License: Apache License 2.0

Rust 100.00%
rust graphql-query-language graphql schema formatter parser graphql-schema-language

graphql-parser's Introduction

GraphQL Parser

Documentation | Github | Crate

A parser, formatter and AST for graphql query and schema definition language for rust.

Supported extensions:

  1. Subscriptions
  2. Block (triple quoted) strings

License

Licensed under either of

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

graphql-parser's People

Contributors

bojanserafimov avatar davidpdrsn avatar dylanowen avatar fpacanowski avatar humb1t avatar kamilkisiela avatar leoyvens avatar mathstuf avatar obi1kenobi avatar obmarg avatar saihaj avatar tailhook avatar that3percent avatar theduke avatar timsuchanek avatar vmagro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-parser's Issues

mutations are not printed properly

This mutation:

mutation($first: Int, $second: Int) {
  field1(first: $first)
  field2(second: $second)
}

gets printed like this:

mutation {
  field1(first: $first)
  field2(second: $second)
}

It's the same problem as in #47, but for mutations.

Support interfaces that implement other interfaces

It seems that graphql-parser currently does not allow interfaces to extend other interfaces. This is a feature that was released in the most recent GraphQL.js major release, v15, and allows some neat functionality: https://dev.to/mikemarcacci/intermediate-interfaces-generic-utility-types-in-graphql-50e8

Direct link to the PR where this was added to the GraphQL spec: graphql/graphql-spec#373
Link to the GraphQL.js PR where this functionality was added: graphql/graphql-js#2084

I'm still learning Rust but I'm open to helping implement this if the maintainers don't mind reading code from a Rust noob ๐Ÿ˜ƒ

failure on directives with comments

When I want to parse a schema with these definitions, this parser fails. It works and validates with the js tools. Removing the block strings makes the parser succeed.

"""
Directs the executor to include this field or fragment only when the `if` argument is true.
"""
directive @include(
  """
  Included when true.
  """
  if: Boolean!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

"""
Directs the executor to skip this field or fragment when the `if` argument is true.
"""
directive @skip(
  """
  Skipped when true.
  """
  if: Boolean!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

Implement From serde_json::Value for Value

This should be straight forward for all types except for Number. Serde_jsons underlying type is an enum of either u64, i64 or f64. Being able to convert from these would involve lifting Number to i128 to provide a lossless conversion from u64 or implementing Number as an enum like serde_json. I think I would go with the first option.

Please let me know if you want me to submit a PR for this!
Thanks

spec: Directives on variable definitions

as per spec https://spec.graphql.org/draft/#ExecutableDirectiveLocation directives can exist on VARIABLE_DEFINITION need to update this.

impl DirectiveLocation {
/// Returns GraphQL syntax compatible name of the directive
pub fn as_str(&self) -> &'static str {
use self::DirectiveLocation::*;
match *self {
Query => "QUERY",
Mutation => "MUTATION",
Subscription => "SUBSCRIPTION",
Field => "FIELD",
FragmentDefinition => "FRAGMENT_DEFINITION",
FragmentSpread => "FRAGMENT_SPREAD",
InlineFragment => "INLINE_FRAGMENT",
Schema => "SCHEMA",
Scalar => "SCALAR",
Object => "OBJECT",
FieldDefinition => "FIELD_DEFINITION",
ArgumentDefinition => "ARGUMENT_DEFINITION",
Interface => "INTERFACE",
Union => "UNION",
Enum => "ENUM",
EnumValue => "ENUM_VALUE",
InputObject => "INPUT_OBJECT",
InputFieldDefinition => "INPUT_FIELD_DEFINITION",

Support for interface implementing another interface doesn't seem to work.

#49 implemented interfaces that implement other interfaces for #44. However, when I try to use this in the wild, it doesn't seem to work.

Example:

This doesn't seem to work in the wild.

e.g.

query.graphl:

query FooList(bazId: ID!) {
  baz
}

schema.graphql:

interface Foo {
  foo: Int
}

interface Bar implements Foo {
  bar: Int
}

type Baz implements Bar {
  baz: ID!
}
$ graphql-client generate query.graphql --schema-path schema.graphql 
Error: Error generating module code: Parser error: schema parse error: Parse error at 5:15
Unexpected `implements[Name]`
Expected `end of input`


Location: /home/ehiggs/.cargo/registry/src/github.com-1ecc6299db9ec823/graphql_client_cli-0.10.0/src/generate.rs:75:24

I've run into this in the wild when dealing with Atlassian's (experimental) Jira Graphql schema derived using python's gql:

gql-cli https://jdog.atlassian.net/gateway/api/graphql --print-schema

Release new version that includes the `combine` version bump?

Would be nice for this crate to publish an updated version that includes the combine major version bump, so that it can get picked up by dependencies and utilized by downstream consumers.

While we could use a [patch] directive in our own Cargo.toml to pull in this crate at the commit that introduced the version bump, that shouldn't be required of all applications.

Add to the awesome-graphql list?

There is a repository to collect projects in the GraphQL ecosystem, and there is already a rust section. Since there are other parsers in the list I think this library has its place, and it would make the Rust section bigger :) I can make the PR if you prefer.

By the way, I am working on the graphql-client crate and I am using graphql-parser internally so I'll take this occasion to say thank you!

Using spans instead of positions

I'm the maintainer of https://crates.io/crates/juniper-from-schema which uses graphql-parser to parse schemas at compile time and generate juniper code.

Currently the error messages I generate look something like

error: proc macro panicked
 --> src/main.rs:3:1
  |
3 | juniper_from_schema::graphql_schema_from_file!("schema.graphql");
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: message:

          error: You have to define a custom scalar called `Url` to use this type
           --> schema:6:3
            |
          6 |      url: Url!
            |      ^


          aborting due to previous error

However I would like to go the extra mile and use something like https://crates.io/crates/codespan-reporting. That would also allow me to remove all my custom error formatting code.

However codespan-reporting requires spans (specifically Range<usize>) rather than single positions. This is unfortunate because graphql-parser only provides Pos which have a line and column number, not a range of where a syntax node appears in the source text.

Changing this would obviously be a breaking change but what are your thoughts about replacing Pos with somekind of Span? If you're up for it I wouldn't mind making a draft PR for you to review. I'm guessing the changes required would be something like this:

pub struct Span {
    start: usize,
    end: usize,
}

pub fn input_value<'a, X>(input: &mut TokenStream<'a>)
    -> ParseResult<InputValue<'a, X>, TokenStream<'a>>
    where X: Text<'a>,
{
    (
        position(), // <- capture start position
        optional(parser(string)),
        name::<'a, X>(),
        punct(":").with(parser(parse_type)),
        optional(punct("=").with(parser(default_value))),
        parser(directives),
        position(), // <- capture end position
    )
    .map(|(start, description, name, value_type, default_value, directives, end)|
    {
        InputValue {
            position: Span { start, end }, description, name, value_type, default_value, directives,
        }
    })
    .parse_stream(input)
}

While somehow also changing TokenStream's position type to use byte index rather than line and column numbers. We could of course still provide helper methods to get line and column numbers.

Visitor trait?

As part of juniper-from-schema I have made a visitor trait that handles walking over the schema AST. I'm thinking if it makes sense to move that trait upstream to here so others can use it as well. I would also make one for query documents ofc.

If you think thats a good idea I'll cook up a PR.

please document the format of the AST

in the docs, you show the result of printing the AST -- what's the format of the data structure? It would be handy to look at. Is it just a nested JSON?

Input Validation AST passes

Sorry for asking a question in an issue!

Are there any reference implementations floating around for required AST passes like validating

  • VariableDefinition's match the locations they're used within a query Document
  • variable value match VariableDefinitions
  • Coercing single element inputs to List (as needed)
    ?

thanks!

New release on crates.io

Juniper would like to use the borrowed AST on master. What is outstanding to cut a new release?

Avoid using BTreeMap in `Value::Object` and use `Vec<(String, Value::Object)` instead?

GraphQL-JS (the reference implementation) implements the values as an array of key->value.

This is done in order to allow parsing things like:

field(arg: { f: true, f: false })

And be able to have in the result [ {key: "f", value: true}, {key: "f", value: false }]. Today it's just { f: false} in graphql-parser.

Where a field can be specified multiple times. In terms of parsing, it should be better to use the array.

The spec/graphql-js also implements a validation rule (UniqueInputFieldNamesRule) to strictly enforce the existence of only one field.

Currently, this creates an ambiguity issue, since the consumer of the parsed Value is getting the "latest" value specific.

Parameter ordering after formatting

Hello,
What is the expected ordering of parameters after parsing and formatting?
The result I am seeing is somewhat unexpected - nested object keys are sorted alphanumerically but top-level parameters are not. Is this expected and can it be relied on to be deterministic? Thanks

let s = parse_query::<String>(r#"mutation m {doFoo(c: 3, b: 2, d: 3, a: {z: 1, y: 2, x: 3 }, f: [{c: 3, b: 2, a:1}] ){error}}"#).unwrap().to_string();
println!("{}", s);

Outputs:

mutation m {
  doFoo(c: 3, b: 2, d: 3, a: {x: 3, y: 2, z: 1}, f: [{a: 1, b: 2, c: 3}]) {
    error
  }
}

Move to graphql-rust repo

Hey @tailhook , sorry for the long delay,
but are you still interested in moving this repo to the graphql-rust organisation?

If so, I can give you permissions on the organization so you can move it over.
Of course you'd retain ownership of the repo in the org and we wouldn't do anything against your wishes.

Arguments as structs?

Currently arguments are represented as a tuple of name and value. I think they should be refactored as structs.
Arguments:

  1. Convenience of use, less re-typing same type signature. Named fields provide a more readable notation;
  2. Improved consistency with the rest of API, where most of the nodes are represented as structs or enums;
  3. Being able to read the position of an arg, which is currently impossible.

If you find it reasonable I can do a PR

Extend Style to allow more control of string formatting

We would like to have more control over GraphQL string formatting. Currently the only parameters to control formatting are indent and multiline_arguments. Specifically we would like to be able to disable newlines in the GraphQL string.
Thanks!

Block string parsing doesn't conform to GraphQL spec.

Currently the following block string:

"""
    
  Hello,
    World!

  Yours,
    GraphQL.

"""

gets parsed as "\nHello,\n World!\n\nYours,\n GraphQL.\n\n".

GraphQL specification requires the leading and trailing empty lines (i.e. lines containing only whitespace) to be stripped from block string value. Quoting the block string spec:

4. While the first item line in lines contains only WhiteSpace:
    a. Remove the first item from lines.

5. While the last item line in lines contains only WhiteSpace:
    a. Remove the last item from lines.

So the above block string should be parsed as "Hello,\n World!\n\nYours,\n GraphQL.".

proposal:embed graphql-parser as a secure graphql parser for multiple languages

Currently the reference implementation and python graphql-core have a stack problem (this project most probably too, but easily fixable via the generator hack, see graphql-core issue) and the evaluation of graphql strings is slow (performance bottleneck which could be used for a ddos):

You can specify highly nested graphs and the parser crashes before any security software can evaluate the tree.

See issue:

So my idea is to fix the projects properly by replacing their parsers with a high performance graphql string parser. And this could be something like this project.

Are you interested in this idea?

Given that I have no rust knowledge yet I would need some guidance should we start the project. Also it would require some coordination between the three projects (not sure how this can be handled).

Requiring Text to be Clone

I have a case where to clone structures like Field, I need to have an additional constraint for T to be Clone, which quickly proliferates through the codebase when T is kept generic.

Is there any specific reason for the Text trait not requiring Clone from the get-go? For the implementations defined in the project, it works just fine:

diff --git a/src/common.rs b/src/common.rs
index c5189fa..f44b5b2 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -11,7 +11,7 @@ use crate::position::Pos;
 
 /// Text abstracts over types that hold a string value.
 /// It is used to make the AST generic over the string type.
-pub trait Text<'a>: 'a {
+pub trait Text<'a>: 'a + Clone {
     type Value: 'a + From<&'a str> + AsRef<str> + std::borrow::Borrow<str> + PartialEq + Eq + PartialOrd + Ord + fmt::Debug + Clone; 
 }

Is there an anticipation that somebody will use an implementation of Text that is not cloneable?

Nom query

I'd like to have an API something like pub fn nom_query<'a, S>(s: &'a str) -> Result<(Document<'a, S>, &'a str), ParseError> which consumes a query from the input and returns the remaining string to be consumed. The use-case is that I need to parse a query that is contained within a larger document which also needs to be parsed.

Would this be difficult to add? I forked the repo to tinker a bit and see if I could add it but it looked less straightforward than I imagined.

Thoughts on rustfmt?

@tailhook I notice the codebase isn't currently formatted with rustfmt. Any particular reason for that or would you be open to a PR that formats everything?

Why does Document need a lifetime?

I'm trying to parse a schema into AST, and I'm wondering why the Document type it produces requires a lifetime. I'd like to be able to use the document as an owned struct without having to worry about lifetimes at all.

Time for a 0.3.1 release?

It's been a year since the last release, and there have been a few patches since. If you could cut a new release that would be great!

format broken for nameless operation with variables

Formatting this query:

query ($first: Int, $second: Int) {
  field1(first: $first)
  field2(second: $second)
}

returns this result:

query {
  field1(first: $first)
  field2(second: $second)
}

Test code:

use graphql_parser::parse_query;

fn main() {
    let ast = parse_query::<&str>(
        "\
query ($first: Int, $second: Int) {
  field1(first: $first)
  field2(second: $second)
}",
    )
    .unwrap();
    // Format canonical representation
    println!("{}", ast);
}

And I think the culprit is here: https://github.com/graphql-rust/graphql-parser/blob/master/src/query/format.rs#L156

Name/TypeName/StringValue AST Node?

Would you ever consider having a TypeName/StringValue AST node? This would help some tooling be more precise when it comes to the location of a certain node.

Currently working on a linter and noticed I can only reference the position of a type definition and it would be quite useful to get the location of a TypeName/StringValue node ๐Ÿ‘‡

error: my_type should be PascalCase

type my_type {
     ^^^^^^^
  a: String!
}

Happy to contribute something if you see value in it. Looks like the Text trait / NameMatch token could potentially have a Pos?

Example from GraphQL-JS (https://github.com/graphql/graphql-js/blob/master/src/language/ast.js#L238-L242)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.