diff --git a/.crystal-version b/.crystal-version new file mode 100644 index 0000000..7b52f5e --- /dev/null +++ b/.crystal-version @@ -0,0 +1 @@ +0.35.0 diff --git a/__logs.log b/__logs.log new file mode 100644 index 0000000..c01e8f7 --- /dev/null +++ b/__logs.log @@ -0,0 +1 @@ +[{"time":"Sep 4 22:57:21","hostName":"localhost","userName":"Alice","process":{"name":"crystal_graphql_server","pid":1},"message":"something occured that need to be logged"},{"time":"now","hostName":"docker host","userName":"Anon","process":{"name":"crystal spec","pid":42},"message":"in a bottle"}] \ No newline at end of file diff --git a/benchmark/lib/libragphqlparserC.cr b/benchmark/lib/libragphqlparserC.cr index 5c8aa16..2a4b34d 100644 --- a/benchmark/lib/libragphqlparserC.cr +++ b/benchmark/lib/libragphqlparserC.cr @@ -45,9 +45,15 @@ lib GraphQLParser _pos : LibC::Int end + # ameba:disable Style/TypeNames alias X__OffT = LibC::Long + # ameba:enable Style/TypeNames + # ameba:disable Style/TypeNames alias X_IoLockT = Void + # ameba:enable Style/TypeNames + # ameba:disable Style/TypeNames alias X__Off64T = LibC::Long + # ameba:enable Style/TypeNames fun parse_file_with_experimental_schema_support = graphql_parse_file_with_experimental_schema_support(file : File*, error : LibC::Char**) : GraphQlAstNode* fun error_free = graphql_error_free(error : LibC::Char*) end diff --git a/example/simple_blog_example.cr b/example/simple_blog_example.cr index 3447119..6cdde64 100644 --- a/example/simple_blog_example.cr +++ b/example/simple_blog_example.cr @@ -1,6 +1,6 @@ # coding: utf-8 require "../src/graphql-crystal" -require "secure_random" +require "uuid" require "benchmark" module BlogExample @@ -55,21 +55,21 @@ module BlogExample # and create some fixtures to work with # USERS = [ - {id: SecureRandom.uuid, first_name: "Bob", last_name: "Bobson", role: UserRole::Author}, - {id: SecureRandom.uuid, first_name: "Alice", last_name: "Alicen", role: UserRole::Admin}, - {id: SecureRandom.uuid, first_name: "Grace", last_name: "Graham", role: UserRole::Reader}, + {id: UUID.random.to_s, first_name: "Bob", last_name: "Bobson", role: UserRole::Author}, + {id: UUID.random.to_s, first_name: "Alice", last_name: "Alicen", role: UserRole::Admin}, + {id: UUID.random.to_s, first_name: "Grace", last_name: "Graham", role: UserRole::Reader}, ].map { |args| User.new **args } POSTS = [ - {id: SecureRandom.uuid, author: USERS[0], title: "GraphQL for Dummies", body: "GraphQL is pretty simple."}, - {id: SecureRandom.uuid, author: USERS[0], title: "REST vs. GraphQL", body: "GraphQL has certain advantages over REST."}, - {id: SecureRandom.uuid, author: USERS[1], title: "The Crystal Programming Language ", body: "The nicest syntax on the planet now comes with typesafety, performance and parallelisation support(ójala!)"}, + {id: UUID.random.to_s, author: USERS[0], title: "GraphQL for Dummies", body: "GraphQL is pretty simple."}, + {id: UUID.random.to_s, author: USERS[0], title: "REST vs. GraphQL", body: "GraphQL has certain advantages over REST."}, + {id: UUID.random.to_s, author: USERS[1], title: "The Crystal Programming Language ", body: "The nicest syntax on the planet now comes with typesafety, performance and parallelisation support(ójala!)"}, ].map { |args| Post.new **args } COMMENTS = [ - {id: SecureRandom.uuid, author: USERS[2], post: POSTS[1], body: "I like rest more!"}, - {id: SecureRandom.uuid, author: USERS[2], post: POSTS[1], body: "But think of all the possibilities with GraphQL!"}, - {id: SecureRandom.uuid, author: USERS[1], post: POSTS[2], body: "When will I finally have static compilation support?"}, + {id: UUID.random.to_s, author: USERS[2], post: POSTS[1], body: "I like rest more!"}, + {id: UUID.random.to_s, author: USERS[2], post: POSTS[1], body: "But think of all the possibilities with GraphQL!"}, + {id: UUID.random.to_s, author: USERS[1], post: POSTS[2], body: "When will I finally have static compilation support?"}, ].map { |args| Comment.new **args } # @@ -228,7 +228,7 @@ module BlogExample field :lastName { last_name } field :fullName { "#{@first_name} #{@last_name}" } field :posts { POSTS.select &.author.==(self) } - field :postsCount { POSTS.select(&.author.==(self)).size } + field :postsCount { POSTS.count &.author.==(self) } field :role end @@ -258,7 +258,7 @@ module BlogExample raise "authorId doesn't exist!" unless author post = Post.new( - id: SecureRandom.uuid, author: author, + id: UUID.random.to_s, author: author, title: payload["title"].as(String), body: payload["body"].as(String) ) @@ -276,7 +276,7 @@ module BlogExample raise "postId doesn't exist!" unless post comment = Comment.new( - id: SecureRandom.uuid, author: author, + id: UUID.random.to_s, author: author, post: post, body: payload["body"].as(String) ) COMMENTS << comment diff --git a/example/simple_example.cr b/example/simple_example.cr index d9f81bd..0bed055 100644 --- a/example/simple_example.cr +++ b/example/simple_example.cr @@ -141,20 +141,20 @@ schema.mutation_resolver = MutationType describe "my graphql schema" do it "does queries" do schema.execute("{ users { name posts } }") - .should eq ({ - "data" => { - "users" => [ - { - "name" => "Alice", - "posts" => [] of String, - }, - { - "name" => "Bob", - "posts" => [] of String, - }, - ], - }, - }) + .should eq ({ + "data" => { + "users" => [ + { + "name" => "Alice", + "posts" => [] of String, + }, + { + "name" => "Bob", + "posts" => [] of String, + }, + ], + }, + }) end it "does mutations" do @@ -180,22 +180,22 @@ describe "my graphql schema" do } schema.execute(mutation_string, payload) - .should eq ({ - "data" => { - "post" => { - "title" => "the long and windy road", - "body" => "that leads to your door", - "author" => { - "name" => "Alice", - "posts" => [ - { - "title" => "the long and windy road", - }, - ], + .should eq ({ + "data" => { + "post" => { + "title" => "the long and windy road", + "body" => "that leads to your door", + "author" => { + "name" => "Alice", + "posts" => [ + { + "title" => "the long and windy road", + }, + ], + }, }, }, - }, - }) + }) end it "does introspection" do @@ -210,32 +210,32 @@ describe "my graphql schema" do } schema.execute(query_string) - .should eq ({ - "data" => { - "__schema" => { - "types" => [ - {"name" => "String"}, - {"name" => "Boolean"}, - {"name" => "Int"}, - {"name" => "Float"}, - {"name" => "ID"}, - {"name" => "QueryType"}, - {"name" => "MutationType"}, - {"name" => "PostInput"}, - {"name" => "UserType"}, - {"name" => "PostType"}, - {"name" => "__Schema"}, - {"name" => "__Type"}, - {"name" => "__Field"}, - {"name" => "__InputValue"}, - {"name" => "__EnumValue"}, - {"name" => "__Directive"}, - {"name" => "__TypeKind"}, - {"name" => "__DirectiveLocation"}, - ], + .should eq ({ + "data" => { + "__schema" => { + "types" => [ + {"name" => "String"}, + {"name" => "Boolean"}, + {"name" => "Int"}, + {"name" => "Float"}, + {"name" => "ID"}, + {"name" => "QueryType"}, + {"name" => "MutationType"}, + {"name" => "PostInput"}, + {"name" => "UserType"}, + {"name" => "PostType"}, + {"name" => "__Schema"}, + {"name" => "__Type"}, + {"name" => "__Field"}, + {"name" => "__InputValue"}, + {"name" => "__EnumValue"}, + {"name" => "__Directive"}, + {"name" => "__TypeKind"}, + {"name" => "__DirectiveLocation"}, + ], + }, }, - }, - } - ) + } + ) end end diff --git a/shard.yml b/shard.yml index 9b3b18e..769dbee 100644 --- a/shard.yml +++ b/shard.yml @@ -4,6 +4,6 @@ version: 0.1.6 authors: - ziprandom - -crystal: 0.33.0 +crystal: 0.35.0 license: MIT diff --git a/spec/graphql-crystal/directive_spec.cr b/spec/graphql-crystal/directive_spec.cr index ee90ab4..983315b 100644 --- a/spec/graphql-crystal/directive_spec.cr +++ b/spec/graphql-crystal/directive_spec.cr @@ -17,37 +17,37 @@ describe GraphQL::Directive do } it "indicates the defined deprecation" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string) .should eq({ - "data" => { - "__type" => { - "fields" => [ - { - "name" => "address", - "isDeprecated" => false, - "deprecationReason" => nil, - }, { - "name" => "friends", - "isDeprecated" => false, - "deprecationReason" => nil, - }, { - "name" => "full_address", - "isDeprecated" => false, - "deprecationReason" => nil, - }, { - "name" => "id", - "isDeprecated" => false, - "deprecationReason" => nil, - }, { - "name" => "name", - "isDeprecated" => true, - "deprecationReason" => "for no apparent Reason", + "data" => { + "__type" => { + "fields" => [ + { + "name" => "address", + "isDeprecated" => false, + "deprecationReason" => nil, + }, { + "name" => "friends", + "isDeprecated" => false, + "deprecationReason" => nil, + }, { + "name" => "full_address", + "isDeprecated" => false, + "deprecationReason" => nil, + }, { + "name" => "id", + "isDeprecated" => false, + "deprecationReason" => nil, + }, { + "name" => "name", + "isDeprecated" => true, + "deprecationReason" => "for no apparent Reason", + }, + ], }, - ], }, - }, - }) + }) end end @@ -66,33 +66,33 @@ describe GraphQL::Directive do } it "indicates the defined deprecation" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string) .should eq({ - "data" => { - "__type" => { - "name" => "City", - "enumValues" => [ - { - "name" => "London", "isDeprecated" => false, - "deprecationReason" => nil, - }, { - "name" => "Miami", - "isDeprecated" => true, - "deprecationReason" => "is not a capital", - }, { - "name" => "CABA", - "isDeprecated" => false, - "deprecationReason" => nil, - }, { - "name" => "Istanbul", - "isDeprecated" => false, - "deprecationReason" => nil, + "data" => { + "__type" => { + "name" => "City", + "enumValues" => [ + { + "name" => "London", "isDeprecated" => false, + "deprecationReason" => nil, + }, { + "name" => "Miami", + "isDeprecated" => true, + "deprecationReason" => "is not a capital", + }, { + "name" => "CABA", + "isDeprecated" => false, + "deprecationReason" => nil, + }, { + "name" => "Istanbul", + "isDeprecated" => false, + "deprecationReason" => nil, + }, + ], }, - ], }, - }, - }) + }) end end end @@ -111,27 +111,27 @@ describe GraphQL::Directive do } it "includes if :if argument is true" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"withName" => true}) .should eq({ - "data" => { - "user" => { - "id" => 0, - "name" => "otto neverthere", + "data" => { + "user" => { + "id" => 0, + "name" => "otto neverthere", + }, }, - }, - }) + }) end it "excludes if :if argument is false" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"withName" => false}) .should eq({ - "data" => { - "user" => { - "id" => 0, + "data" => { + "user" => { + "id" => 0, + }, }, - }, - }) + }) end end @@ -148,28 +148,28 @@ describe GraphQL::Directive do } it "includes if :if argument is true" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"withName" => true}) .should eq({ - "data" => { - "user" => { - "id" => 0, - "name" => "otto neverthere", + "data" => { + "user" => { + "id" => 0, + "name" => "otto neverthere", + }, }, - }, - }) + }) end it "excludes if :if argument is false" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"withName" => false}) .should eq({ - "data" => { - "user" => { - "id" => 0, + "data" => { + "user" => { + "id" => 0, + }, }, - }, - }) + }) end end @@ -187,28 +187,28 @@ describe GraphQL::Directive do } it "includes if :if argument is true" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"withName" => true}) .should eq({ - "data" => { - "user" => { - "id" => 0, - "name" => "otto neverthere", + "data" => { + "user" => { + "id" => 0, + "name" => "otto neverthere", + }, }, - }, - }) + }) end it "excludes if :if argument is false" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"withName" => false}) .should eq({ - "data" => { - "user" => { - "id" => 0, + "data" => { + "user" => { + "id" => 0, + }, }, - }, - }) + }) end end end @@ -227,27 +227,27 @@ describe GraphQL::Directive do } it "skips if :if argument is true" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"skipName" => true}) .should eq({ - "data" => { - "user" => { - "id" => 0, + "data" => { + "user" => { + "id" => 0, + }, }, - }, - }) + }) end it "includes if :if argument is false" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"skipName" => false}) .should eq({ - "data" => { - "user" => { - "id" => 0, - "name" => "otto neverthere", + "data" => { + "user" => { + "id" => 0, + "name" => "otto neverthere", + }, }, - }, - }) + }) end end @@ -264,28 +264,28 @@ describe GraphQL::Directive do } it "skips if :if argument is true" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"skipName" => true}) .should eq({ - "data" => { - "user" => { - "id" => 0, + "data" => { + "user" => { + "id" => 0, + }, }, - }, - }) + }) end it "includes if :if argument is false" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"skipName" => false}) .should eq({ - "data" => { - "user" => { - "id" => 0, - "name" => "otto neverthere", + "data" => { + "user" => { + "id" => 0, + "name" => "otto neverthere", + }, }, - }, - }) + }) end end @@ -303,28 +303,28 @@ describe GraphQL::Directive do } it "skips if :if argument is true" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"skipName" => true}) .should eq({ - "data" => { - "user" => { - "id" => 0, + "data" => { + "user" => { + "id" => 0, + }, }, - }, - }) + }) end it "includes if :if argument is false" do - TestSchema::Schema + TestSchema::SCHEMA .execute(query_string, {"skipName" => false}) .should eq({ - "data" => { - "user" => { - "id" => 0, - "name" => "otto neverthere", + "data" => { + "user" => { + "id" => 0, + "name" => "otto neverthere", + }, }, - }, - }) + }) end end end diff --git a/spec/graphql-crystal/introspection/directive_type_spec.cr b/spec/graphql-crystal/introspection/directive_type_spec.cr index a8c7ffb..89f3ca5 100644 --- a/spec/graphql-crystal/introspection/directive_type_spec.cr +++ b/spec/graphql-crystal/introspection/directive_type_spec.cr @@ -15,7 +15,7 @@ describe "GraphQL::Introspection::DirectiveType" do } } query_string - result = Dummy::Schema.execute(query_string) + result = Dummy::SCHEMA.execute(query_string) it "shows directive info " do expected = {"data" => { diff --git a/spec/graphql-crystal/introspection/input_value_type_spec.cr b/spec/graphql-crystal/introspection/input_value_type_spec.cr index cf1320e..7b55030 100644 --- a/spec/graphql-crystal/introspection/input_value_type_spec.cr +++ b/spec/graphql-crystal/introspection/input_value_type_spec.cr @@ -16,7 +16,7 @@ describe "GraphQL::Introspection::DirectiveType" do } } query_string - result = Dummy::Schema.execute(query_string) + result = Dummy::SCHEMA.execute(query_string) it "shows directive info " do expected = { diff --git a/spec/graphql-crystal/introspection/introspection_query_spec.cr b/spec/graphql-crystal/introspection/introspection_query_spec.cr index cf8cbea..5ae66e8 100644 --- a/spec/graphql-crystal/introspection/introspection_query_spec.cr +++ b/spec/graphql-crystal/introspection/introspection_query_spec.cr @@ -2,7 +2,7 @@ require "../../spec_helper" describe "GraphQL::Introspection::INTROSPECTION_QUERY" do query_string = GraphQL::Schema::INTROSPECTION_QUERY - result = Dummy::Schema.execute(query_string) + result = Dummy::SCHEMA.execute(query_string) it "runs" do result["data"].should be_truthy diff --git a/spec/graphql-crystal/introspection/schema_type_spec.cr b/spec/graphql-crystal/introspection/schema_type_spec.cr index 6bd23a4..b47e2f4 100644 --- a/spec/graphql-crystal/introspection/schema_type_spec.cr +++ b/spec/graphql-crystal/introspection/schema_type_spec.cr @@ -9,12 +9,12 @@ describe "GraphQL::Introspection::SchemaType" do } } } - result = Dummy::Schema.execute(query_string) + result = Dummy::SCHEMA.execute(query_string) it "exposes the schema" do expected = {"data" => { "__schema" => { - "types" => Dummy::Schema.types.values.map { |t| t.name.nil? ? (p t; raise("no name for #{t}")) : {"name" => t.name} }, + "types" => Dummy::SCHEMA.types.values.map { |t| t.name.nil? ? (p t; raise("no name for #{t}")) : {"name" => t.name} }, "queryType" => { "fields" => [ {"name" => "allDairy"}, diff --git a/spec/graphql-crystal/language/generation_spec.cr b/spec/graphql-crystal/language/generation_spec.cr index e4a5c17..8f6cc26 100644 --- a/spec/graphql-crystal/language/generation_spec.cr +++ b/spec/graphql-crystal/language/generation_spec.cr @@ -10,8 +10,8 @@ end def clean_string(string) string.gsub(/^ /m, "") - .gsub(/#[^\n]*\n/m, "\n") - .gsub(/[\n\s]+/m, "\n").strip + .gsub(/#[^\n]*\n/m, "\n") + .gsub(/[\n\s]+/m, "\n").strip end describe GraphQL::Language::Generation do diff --git a/spec/graphql-crystal/language/lexer_spec.cr b/spec/graphql-crystal/language/lexer_spec.cr index 24ca2a8..4da772c 100644 --- a/spec/graphql-crystal/language/lexer_spec.cr +++ b/spec/graphql-crystal/language/lexer_spec.cr @@ -6,18 +6,6 @@ describe GraphQL::Language::Lexer do subject = GraphQL::Language::Lexer describe ".lex" do - query_string = " \ - { \ - query getCheese { \ - cheese(id: 1) { \ - ... cheeseFields \ - } \ - } \ - } \ - " - - tokens = subject.lex(query_string) - it "makes utf-8 comments" do comment_token = subject.lex("# 不要!\n{") comment_token.value.should eq "不要!" diff --git a/spec/graphql-crystal/language/parser_spec.cr b/spec/graphql-crystal/language/parser_spec.cr index 3295951..5be3126 100644 --- a/spec/graphql-crystal/language/parser_spec.cr +++ b/spec/graphql-crystal/language/parser_spec.cr @@ -127,7 +127,9 @@ describe GraphQL::Language::Parser do end pending "parses the Dummy Schema" do + # ameba:disable Lint/UselessAssign document = subject.parse g + # ameba:enable Lint/UselessAssign end it "creates an anonymous fragment definition" do @@ -154,7 +156,7 @@ describe GraphQL::Language::Parser do strings.each do |query_str| doc = subject.parse(query_str) field = doc.definitions - .first.as(GraphQL::Language::OperationDefinition) + .first.as(GraphQL::Language::OperationDefinition) .selections.first.as(GraphQL::Language::Field) field.arguments.size.should eq 0 field.selections.size.should eq 1 diff --git a/spec/graphql-crystal/schema/variables_spec.cr b/spec/graphql-crystal/schema/variables_spec.cr index 770ea9d..bba02ad 100644 --- a/spec/graphql-crystal/schema/variables_spec.cr +++ b/spec/graphql-crystal/schema/variables_spec.cr @@ -2,7 +2,6 @@ require "../../spec_helper" describe GraphQL::Schema, "using variables" do - # When used in conjunction with Kemal as web framework, query variables # exposed by Kemal are of type Hash(String, JSON::Any). These should be able # to be parsed correctly. @@ -16,19 +15,18 @@ describe GraphQL::Schema, "using variables" do } } } - json_any = JSON.parse({ time: "now", - hostName: "docker host", - process: { - name: "crystal spec", - pid: 42 - }, - message: "in a bottle" }.to_json) - variables = { "input" => json_any } + json_any = JSON.parse({time: "now", + hostName: "docker host", + process: { + name: "crystal spec", + pid: 42, + }, + message: "in a bottle"}.to_json) + variables = {"input" => json_any} variables.class.should eq Hash(String, JSON::Any) CUSTOM_CONTEXT_SCHEMA.execute(mutation, params: variables, context: context).should eq( - { "data" => { "log" => { "time" => "now" } } } + {"data" => {"log" => {"time" => "now"}}} ) end - end diff --git a/spec/graphql-crystal/schema_spec.cr b/spec/graphql-crystal/schema_spec.cr index c7f22b0..9af737b 100644 --- a/spec/graphql-crystal/schema_spec.cr +++ b/spec/graphql-crystal/schema_spec.cr @@ -4,7 +4,7 @@ require "../spec_helper" describe GraphQL::Schema do describe "resolve" do it "answers a simple field request" do - TestSchema::Schema.execute("{ user(id: 0) { name } }").should eq({"data" => {"user" => {"name" => "otto neverthere"}}}) + TestSchema::SCHEMA.execute("{ user(id: 0) { name } }").should eq({"data" => {"user" => {"name" => "otto neverthere"}}}) end it "answers a simple field request for a field defined later in the inheritance chain (SpecialQuery)" do @@ -17,7 +17,7 @@ describe GraphQL::Schema do ], }, } - TestSchema::Schema.execute("{ addresses { city } }").should eq expected + TestSchema::SCHEMA.execute("{ addresses { city } }").should eq expected end it "answers a simple field request for a field defined later in the inheritance chain (SpecialQuery)" do @@ -29,7 +29,7 @@ describe GraphQL::Schema do ], }, } - TestSchema::Schema.execute(%{ + TestSchema::SCHEMA.execute(%{ { addresses(city: [London, Miami]) { city street number } } }).should eq expected end @@ -40,7 +40,7 @@ describe GraphQL::Schema do "addresses" => [] of Nil, }, } - TestSchema::Schema.execute(%{ + TestSchema::SCHEMA.execute(%{ { addresses(city: [Istanbul]) { city street number } } }).should eq expected end @@ -53,13 +53,13 @@ describe GraphQL::Schema do }, }, } - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "{ user(id: 0) { id, name } }" ).should eq(expected) end it "answers a simple field request for a nested resource" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "{ user(id: 0) { id, address { city } } }" ).should eq({ "data" => { @@ -74,7 +74,7 @@ describe GraphQL::Schema do end it "answers a more deep request for a list resource" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "{ user(id: 0) { id, friends { id, name } } }" ).should eq({ "data" => { @@ -90,7 +90,7 @@ describe GraphQL::Schema do end it "answers a request for a field with a custom resolve callback" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "{ user(id: 0) { full_address } }" ).should eq({"data" => { "user" => { @@ -112,7 +112,7 @@ describe GraphQL::Schema do }, } - TestSchema::Schema.execute(%{ + TestSchema::SCHEMA.execute(%{ { firstUser: user(id: 0) { name @@ -139,7 +139,7 @@ describe GraphQL::Schema do }, } - TestSchema::Schema.execute(%{ + TestSchema::SCHEMA.execute(%{ { firstUser: user(id: 0) { ... userFields @@ -164,7 +164,7 @@ describe GraphQL::Schema do }, } - TestSchema::Schema.execute(%{ + TestSchema::SCHEMA.execute(%{ { firstUser: user(id: 0) { ... on User { @@ -176,56 +176,56 @@ describe GraphQL::Schema do end it "answers a request with nullable arg and arg ommited" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "query getAddresses($city: [City]) { addresses(city: $city) { city } }" ).should eq({ "data" => { "addresses" => [ {"city" => "London"}, {"city" => "Miami"}, - {"city" => "CABA"} + {"city" => "CABA"}, ], - } + }, }) end it "answers a request with non-nullable arg and arg provided" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "query getAddresses($city: [City]!) { addresses(city: $city) { city } }", { - "city" => [ - "London" - ] - } + "city" => [ + "London", + ], + } ).should eq({ "data" => { "addresses" => [ - {"city" => "London"} + {"city" => "London"}, ], - } + }, }) end it "answers a request with non-nullable arg and arg provided with JSON::Any type" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "query getAddresses($city: [City]!) { addresses(city: $city) { city } }", JSON.parse({ - "city" => [ - "London" - ] - }.to_json) + "city" => [ + "London", + ], + }.to_json) ).should eq({ "data" => { "addresses" => [ - {"city" => "London"} + {"city" => "London"}, ], - } + }, }) end it "raises if non-nullable args ommited" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( "query getAddresses($city: [City]!) { addresses(city: $city) { city } }" ).should eq({ - "data" => nil, "errors" => [{"message" => "missing variable city", "path" => [] of String}] + "data" => nil, "errors" => [{"message" => "missing variable city", "path" => [] of String}], }) end @@ -243,7 +243,7 @@ describe GraphQL::Schema do ], } - TestSchema::Schema.execute(%{ + TestSchema::SCHEMA.execute(%{ { firstUser: user(id: 0) { ... on Droid { @@ -265,7 +265,7 @@ describe GraphQL::Schema do ], } - TestSchema::Schema.execute(%{ + TestSchema::SCHEMA.execute(%{ { firstUser: user(id: 0) { ... userFieldsNonExistent @@ -298,7 +298,7 @@ describe GraphQL::Schema do ], } - TestSchema::Schema.execute(bad_query_string).should eq expected + TestSchema::SCHEMA.execute(bad_query_string).should eq expected end it "raises an error if we request a field with an argument that hasn't been defined" do @@ -322,7 +322,7 @@ describe GraphQL::Schema do ], } - TestSchema::Schema.execute(bad_query_string).should eq expected + TestSchema::SCHEMA.execute(bad_query_string).should eq expected end it "raises an error if we request a field with defined argument using a wrong type" do @@ -346,13 +346,13 @@ describe GraphQL::Schema do ], } - TestSchema::Schema.execute(bad_query_string).should eq expected + TestSchema::SCHEMA.execute(bad_query_string).should eq expected end end describe "operationName" do it "multiple operations with valid operationName" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( %{ query UserOne{ user(id: 0) { name } } query UserTwo{ user(id: 0) { name } } @@ -363,7 +363,7 @@ describe GraphQL::Schema do end it "one operation ignore operationName" do - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( %{ query UserOne{ user(id: 0) { name } } }, @@ -381,7 +381,7 @@ describe GraphQL::Schema do }, ], } - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( %{ query UserOne{ user(id: 0) { name } } query UserTwo{ user(id: 0) { name } } @@ -398,7 +398,7 @@ describe GraphQL::Schema do }, ], } - TestSchema::Schema.execute( + TestSchema::SCHEMA.execute( %{ query UserOne{ user(id: 0) { name } } query UserTwo{ user(id: 0) { name } } diff --git a/spec/graphql-crystal/support/star_wars_schema_introspection_spec.cr b/spec/graphql-crystal/support/star_wars_schema_introspection_spec.cr index cc5116e..b607a8b 100644 --- a/spec/graphql-crystal/support/star_wars_schema_introspection_spec.cr +++ b/spec/graphql-crystal/support/star_wars_schema_introspection_spec.cr @@ -1,77 +1,76 @@ require "../../spec_helper" describe GraphQL::Schema::Schema do - - query_string = <<-query - query IntrospectionTypeQuery { - __schema { - types { - name - } - } + query_string = <<-query + query IntrospectionTypeQuery { + __schema { + types { + name + } } - query + } + query - expected = { - "data" => { - "__schema" => { - "types" => [ - { - "name" => "QueryType", - }, - { - "name" => "Episode", - }, - { - "name" => "Character", - }, - { - "name" => "String", - }, - { - "name" => "Human", - }, - { - "name" => "Droid", - }, - { - "name" => "__Schema", - }, - { - "name" => "__Type", - }, - { - "name" => "__TypeKind", - }, - { - "name" => "Boolean", - }, - { - "name" => "__Field", - }, - { - "name" => "__InputValue", - }, - { - "name" => "__EnumValue", - }, - { - "name" => "__Directive", - }, - # Not Implemented ATM - # { - # "name" => "__DirectiveLocation" - # } - ], - }, + expected = { + "data" => { + "__schema" => { + "types" => [ + { + "name" => "QueryType", + }, + { + "name" => "Episode", + }, + { + "name" => "Character", + }, + { + "name" => "String", + }, + { + "name" => "Human", + }, + { + "name" => "Droid", + }, + { + "name" => "__Schema", + }, + { + "name" => "__Type", + }, + { + "name" => "__TypeKind", + }, + { + "name" => "Boolean", + }, + { + "name" => "__Field", + }, + { + "name" => "__InputValue", + }, + { + "name" => "__EnumValue", + }, + { + "name" => "__Directive", + }, + # Not Implemented ATM + # { + # "name" => "__DirectiveLocation" + # } + ], }, - }["data"]["__schema"]["types"] + }, + }["data"]["__schema"]["types"] - result = StarWars::Schema.execute(query_string)["data"].as(Hash)["__schema"].as(Hash)["types"].as(Array) - missing = expected.reject { |element| result.includes? element } - superfluous = result.reject { |element| expected.includes? element } + result = StarWars::SCHEMA.execute(query_string)["data"].as(Hash)["__schema"].as(Hash)["types"].as(Array) + missing = expected.reject { |element| result.includes? element } + superfluous = result.reject { |element| expected.includes? element } - empty = [] of Hash(String, String) + empty = [] of Hash(String, String) it "Allows querying the schema for types" do missing.should eq empty diff --git a/spec/graphql-crystal/support/star_wars_schema_spec.cr b/spec/graphql-crystal/support/star_wars_schema_spec.cr index fc34475..0b82e95 100644 --- a/spec/graphql-crystal/support/star_wars_schema_spec.cr +++ b/spec/graphql-crystal/support/star_wars_schema_spec.cr @@ -1,6 +1,6 @@ require "../../spec_helper" -describe StarWars::Schema do +describe StarWars::SCHEMA do describe "Basic Queries" do it "Correctly identifies R2-D2 as the hero of the Star Wars Saga" do query_string = %{ @@ -10,7 +10,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "hero" => { @@ -31,7 +31,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "hero" => { @@ -69,7 +69,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "hero" => { @@ -142,7 +142,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "human" => { @@ -161,7 +161,7 @@ describe StarWars::Schema do } } params = {"someId" => "1000"} - result = StarWars::Schema.execute(query_string, params) + result = StarWars::SCHEMA.execute(query_string, params) result.should eq ({ "data" => { "human" => { @@ -180,7 +180,7 @@ describe StarWars::Schema do } } params = {"someId" => "1002"} - result = StarWars::Schema.execute(query_string, params) + result = StarWars::SCHEMA.execute(query_string, params) result.should eq ({ "data" => { "human" => { @@ -199,7 +199,7 @@ describe StarWars::Schema do } } params = {"id" => "not a valid id"} - result = StarWars::Schema.execute(query_string, params) + result = StarWars::SCHEMA.execute(query_string, params) result.should eq ({ "data" => { "human" => nil, @@ -217,7 +217,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "luke" => { @@ -238,7 +238,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "luke" => { @@ -266,7 +266,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "luke" => { @@ -296,7 +296,7 @@ describe StarWars::Schema do homePlanet } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "luke" => { @@ -321,7 +321,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "hero" => { @@ -341,7 +341,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "hero" => { @@ -363,7 +363,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "hero" => { @@ -394,7 +394,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "hero" => { @@ -444,7 +444,7 @@ describe StarWars::Schema do } } } - result = StarWars::Schema.execute(query_string) + result = StarWars::SCHEMA.execute(query_string) result.should eq ({ "data" => { "mainHero" => { diff --git a/spec/graphql-crystal/validation/evaluation_depth.cr b/spec/graphql-crystal/validation/evaluation_depth.cr index d28e66f..9b262c1 100644 --- a/spec/graphql-crystal/validation/evaluation_depth.cr +++ b/spec/graphql-crystal/validation/evaluation_depth.cr @@ -31,15 +31,15 @@ module EvaluationDepthTest field :firstElement { ListElement.new } end - Schema = GraphQL::Schema.from_schema(SCHEMA_STRING) - Schema.max_depth 5 - Schema.query_resolver = QueryType + SCHEMA = GraphQL::Schema.from_schema(SCHEMA_STRING) + SCHEMA.max_depth 5 + SCHEMA.query_resolver = QueryType end describe GraphQL::Schema do describe "Execution Depth Constraint" do it "allows queries that don't surpass the set max depth for the schema" do - EvaluationDepthTest::Schema.execute(%< { firstElement { index } } >).should eq ({ + EvaluationDepthTest::SCHEMA.execute(%< { firstElement { index } } >).should eq ({ "data" => { "firstElement" => { "index" => 0, @@ -47,60 +47,60 @@ describe GraphQL::Schema do }, }) - EvaluationDepthTest::Schema + EvaluationDepthTest::SCHEMA .execute(%< { firstElement { next { next { index } } } } >) .should eq ({ - "data" => { - "firstElement" => { - "next" => { + "data" => { + "firstElement" => { "next" => { - "index" => 2, + "next" => { + "index" => 2, + }, }, }, }, - }, - }) - EvaluationDepthTest::Schema + }) + EvaluationDepthTest::SCHEMA .execute(%< { firstElement { next { next { next { index } } } } } >) .should eq ({ - "data" => { - "firstElement" => { - "next" => { + "data" => { + "firstElement" => { "next" => { "next" => { - "index" => 3, + "next" => { + "index" => 3, + }, }, }, }, }, - }, - }) + }) end it "throws an error when the max execution depth is reached" do - EvaluationDepthTest::Schema + EvaluationDepthTest::SCHEMA .execute(%< { firstElement { next { next { next { next { index } } } } } } >) .should eq ({ - "data" => { - "firstElement" => { - "next" => { + "data" => { + "firstElement" => { "next" => { "next" => { - "next" => nil, + "next" => { + "next" => nil, + }, }, }, }, }, - }, - "errors" => [ - { - "message" => "max execution depth reached", - "path" => [ - "firstElement", "next", "next", "next", "next", - ], - }, - ], - }) + "errors" => [ + { + "message" => "max execution depth reached", + "path" => [ + "firstElement", "next", "next", "next", "next", + ], + }, + ], + }) end end end diff --git a/spec/graphql-crystal/validation/type_validation.cr b/spec/graphql-crystal/validation/type_validation.cr index 7f89889..a448170 100644 --- a/spec/graphql-crystal/validation/type_validation.cr +++ b/spec/graphql-crystal/validation/type_validation.cr @@ -57,11 +57,11 @@ def reject_other_than(type, leave_out) leave_out.is_a?(Array) ? leave_out : [leave_out] ).map { |name| VALUES[name] } VALUES.to_a.reject { |(_, val)| leave_out.includes?(val) } - .each do |(_, val)| - it "rejects '#{val.inspect}'" do - TYPE_VALIDATION.accepts?(type, val).should eq false + .each do |(_, val)| + it "rejects '#{val.inspect}'" do + TYPE_VALIDATION.accepts?(type, val).should eq false + end end - end end describe GraphQL::TypeValidation do diff --git a/spec/support/custom_context_schema.cr b/spec/support/custom_context_schema.cr index c6a8b98..9e6c7c4 100644 --- a/spec/support/custom_context_schema.cr +++ b/spec/support/custom_context_schema.cr @@ -26,8 +26,12 @@ class LogType include GraphQL::ObjectType def initialize( + # ameba:disable Style/VariableNames @time : String, @hostName : String, + # ameba:enable Style/VariableNames + # ameba:disable Style/VariableNames @userName : String, @process : ProcessType, + # ameba:enable Style/VariableNames @message : String ); end @@ -69,7 +73,9 @@ struct LogInput < GraphQL::Schema::InputType ) def to_log_type(username) + # ameba:disable Style/VariableNames LogType.new(@time, @hostName, username, @process.to_process_type, @message) + # ameba:enable Style/VariableNames end end @@ -95,7 +101,7 @@ module QueryType include ::GraphQL::ObjectType extend self - field :logs do |args, context| + field :logs do |_args, context| context = context.as(CustomContext) unless context.authenticated raise "you are not allowed to read the logs #{context.username}!" diff --git a/spec/support/dummy/dummy_data.cr b/spec/support/dummy/dummy_data.cr index 62e6cd7..847c0b1 100644 --- a/spec/support/dummy/dummy_data.cr +++ b/spec/support/dummy/dummy_data.cr @@ -47,7 +47,7 @@ module Dummy LAST_PRODUCED_DAIRY = MILKS[1] end - module MAYBE_NULL + module MAYBENULL extend GraphQL::ObjectType CHEESE = nil end diff --git a/spec/support/dummy/dummy_schema.cr b/spec/support/dummy/dummy_schema.cr index 8e35986..058ccc2 100644 --- a/spec/support/dummy/dummy_schema.cr +++ b/spec/support/dummy/dummy_schema.cr @@ -26,14 +26,16 @@ module Dummy products.first end end - field :allDairy do |args| + field :allDairy do |_args| + # ameba:disable Lint/UselessAssign result = CHEESES.values + MILKS.values + # ameba:enable Lint/UselessAssign end field :allEdible { CHEESES.values + MILKS.values } field :error { raise("This error was raised on purpose") } field :executionError { raise("I don't have a dedicated ExecutionErrorObject :(") } - field :maybeNull { Dummy::MAYBE_NULL } + field :maybeNull { Dummy::MAYBENULL } field :deepNonNull { nil } end @@ -45,12 +47,14 @@ module Dummy args["value"] end + # ameba:disable Lint/UnusedArgument field :replaceValues do |args| CHEESES.values + MILKS.values end + # ameba:enble Lint/UnusedArgument end - Schema = GraphQL::Schema.from_schema(Dummy::SCHEMA_STRING) - Schema.query_resolver = DairyAppQuery - Schema.mutation_resolver = DairyAppMutation + SCHEMA = GraphQL::Schema.from_schema(Dummy::SCHEMA_STRING) + SCHEMA.query_resolver = DairyAppQuery + SCHEMA.mutation_resolver = DairyAppMutation end diff --git a/spec/support/star_wars/star_wars_data.cr b/spec/support/star_wars/star_wars_data.cr index 5948331..90b26d4 100644 --- a/spec/support/star_wars/star_wars_data.cr +++ b/spec/support/star_wars/star_wars_data.cr @@ -27,7 +27,7 @@ module StarWars @appears_in : Array(EpisodeEnum), @primary_function : String?); end end - Characters = begin + CHARACTERS = begin luke = { type: "Human", id: "1000", diff --git a/spec/support/star_wars/star_wars_schema.cr b/spec/support/star_wars/star_wars_schema.cr index 3902351..514293c 100644 --- a/spec/support/star_wars/star_wars_schema.cr +++ b/spec/support/star_wars/star_wars_schema.cr @@ -62,7 +62,7 @@ module StarWars field :id field :name field :friends do - Characters.select { |c| self.friends.includes? c.id } + CHARACTERS.select { |c| self.friends.includes? c.id } end field :appearsIn { self.appears_in } @@ -85,25 +85,25 @@ module StarWars field :hero do |args| if (args["episode"]? == "EMPIRE") - Characters.find(&.id.==("1000")) + CHARACTERS.find(&.id.==("1000")) else - Characters.find(&.id.==("2001")) + CHARACTERS.find(&.id.==("2001")) end end field :humans do |args| - args["ids"].as(Array).map { |i| Characters.find(&.id.==(i)) } + args["ids"].as(Array).map { |i| CHARACTERS.find(&.id.==(i)) } end field :human do |args| - Characters.select(&.is_a?(Human)).find(&.id.==(args["id"])) + CHARACTERS.select(&.is_a?(Human)).find(&.id.==(args["id"])) end field :droid do |args| - Characters.select(&.is_a?(Droid)).find(&.id.==(args["id"])) + CHARACTERS.select(&.is_a?(Droid)).find(&.id.==(args["id"])) end end - Schema = GraphQL::Schema.from_schema(SCHEMA_DEFINITION) - Schema.query_resolver = QueryType + SCHEMA = GraphQL::Schema.from_schema(SCHEMA_DEFINITION) + SCHEMA.query_resolver = QueryType end diff --git a/spec/support/test_schema.cr b/spec/support/test_schema.cr index 8533188..e66c1e2 100644 --- a/spec/support/test_schema.cr +++ b/spec/support/test_schema.cr @@ -12,21 +12,21 @@ module TestSchema Istanbul end - Addresses = [ + ADDRESSES = [ {"Downing Street", 11, CityEnum::London, 3231}, {"Sunset Boulevard", 114, CityEnum::Miami, 123439}, {"Avenida Santa Fé", 3042, CityEnum::CABA, 12398}, ].map { |vars| Address.new *vars } - Users = [ + USERS = [ "otto neverthere", "jennifer nonone", "wilma nunca", ].map_with_index do |name, idx| - User.new idx, name, Addresses[idx] + User.new idx, name, ADDRESSES[idx] end - Users[2].friends = [Users[1], Users[0]] - Users[1].friends = [Users[2], Users[0]] - Users[0].friends = [Users[2], Users[1]] + USERS[2].friends = [USERS[1], USERS[0]] + USERS[1].friends = [USERS[2], USERS[0]] + USERS[0].friends = [USERS[2], USERS[1]] class Address include GraphQL::ObjectType @@ -151,19 +151,19 @@ module TestSchema extend self field :user do |args| - Users.find &.id.==(args["id"]) + USERS.find &.id.==(args["id"]) end field :addresses do |args| - (cities = args["city"]?) ? Addresses.select do |address| + (cities = args["city"]?) ? ADDRESSES.select do |address| cities.as(Array).includes? address.city.to_s - end : Addresses + end : ADDRESSES end end # # instantiate the schema and add the RootQuery Resolver # - Schema = GraphQL::Schema.from_schema(SCHEMA_DEFINITION) - Schema.query_resolver = QueryType + SCHEMA = GraphQL::Schema.from_schema(SCHEMA_DEFINITION) + SCHEMA.query_resolver = QueryType end diff --git a/src/graphql-crystal/language/ast.cr b/src/graphql-crystal/language/ast.cr index 36650c1..54310b6 100644 --- a/src/graphql-crystal/language/ast.cr +++ b/src/graphql-crystal/language/ast.cr @@ -39,34 +39,34 @@ module GraphQL end def self.values - NamedTuple.new() + NamedTuple.new end def values - NamedTuple.new() + NamedTuple.new end def ==(other) self.class == other.class end - # def inspect - # "#{self.class.name}(" + - # if vs = values - # vs.map do |k, v| - # value = if v.is_a?(Array) - # "[" + v.map{ |vv| vv.inspect.as(String) }.join(", ") + "]" - # elsif v.is_a?(Hash) - # "{" + v.map{ |kk, vv| "#{kk.inspect}: #{vv.inspect}".as(String) }.join(", ") + "}" - # else - # v.inspect - # end - # "#{k}: #{value}" - # end.join(", ") - # else - # "" - # end + ")" - # end + # def inspect + # "#{self.class.name}(" + + # if vs = values + # vs.map do |k, v| + # value = if v.is_a?(Array) + # "[" + v.map{ |vv| vv.inspect.as(String) }.join(", ") + "]" + # elsif v.is_a?(Hash) + # "{" + v.map{ |kk, vv| "#{kk.inspect}: #{vv.inspect}".as(String) }.join(", ") + "}" + # else + # v.inspect + # end + # "#{k}: #{value}" + # end.join(", ") + # else + # "" + # end + ")" + # end def_clone @@ -131,6 +131,7 @@ module GraphQL end end \{% end %} + else \{% end %}\ end \{% end %}\ @@ -168,7 +169,6 @@ module GraphQL end make_value_methods - end end -end \ No newline at end of file +end diff --git a/src/graphql-crystal/language/generation.cr b/src/graphql-crystal/language/generation.cr index 8a727b4..f4b7ce0 100644 --- a/src/graphql-crystal/language/generation.cr +++ b/src/graphql-crystal/language/generation.cr @@ -59,7 +59,7 @@ module GraphQL def self.generate(node : FragmentSpread, indent : String = "") out = "#{indent}...#{node.name}" if node.directives.any? - out += " " + node.directives.map { |d| generate(d).as(String) }.join(" ") + out + " " + node.directives.map { |d| generate(d).as(String) }.join(" ") end end @@ -122,13 +122,13 @@ module GraphQL out += " query: #{node.query}\n" if node.query out += " mutation: #{node.mutation}\n" if node.mutation out += " subscription: #{node.subscription}\n" if node.subscription - out += "}" + out + "}" end def self.generate(node : ScalarTypeDefinition, indent : String = "") out = generate_description(node) out += "scalar #{node.name}" - out += generate_directives(node.directives) + out + generate_directives(node.directives) end def self.generate(node : ObjectTypeDefinition, indent : String = "") @@ -136,13 +136,13 @@ module GraphQL out += "type #{node.name}" out += generate_directives(node.directives) out += " implements " + node.interfaces.map { |i| i.as(String) }.join(", ") unless node.interfaces.empty? - out += generate_field_definitions(node.fields) + out + generate_field_definitions(node.fields) end def self.generate(node : InputValueDefinition, indent : String = "") out = "#{node.name}: #{generate(node.type)}" out += " = #{generate(node.default_value)}" unless node.default_value.nil? - out += generate_directives(node.directives) + out + generate_directives(node.directives) end def self.generate(node : FieldDefinition, indent : String = "") @@ -151,21 +151,21 @@ module GraphQL out += "(" + node.arguments.map { |arg| generate(arg).as(String) }.join(", ") + ")" end out += ": #{generate(node.type)}" - out += generate_directives(node.directives) + out + generate_directives(node.directives) end def self.generate(node : InterfaceTypeDefinition, indent : String = "") out = generate_description(node) out += "interface #{node.name}" out += generate_directives(node.directives) - out += generate_field_definitions(node.fields) + out + generate_field_definitions(node.fields) end def self.generate(node : UnionTypeDefinition, indent : String = "") out = generate_description(node) out += "union #{node.name}" out += generate_directives(node.directives) - out += " = " + node.types.map { |t| t.as(NameOnlyNode).name }.join(" | ") + out + " = " + node.types.map { |t| t.as(NameOnlyNode).name }.join(" | ") end def self.generate(node : EnumTypeDefinition, indent : String = "") @@ -175,13 +175,13 @@ module GraphQL out += generate_description(value, indent: " ", first_in_block: i == 0) out += generate(value) || "" end - out += "}" + out + "}" end def self.generate(node : EnumValueDefinition, indent : String = "") out = " #{node.name}" out += generate_directives(node.directives) - out += "\n" + out + "\n" end def self.generate(node : InputObjectTypeDefinition, indent : String = "") @@ -200,7 +200,7 @@ module GraphQL out = generate_description(node) out += "directive @#{node.name}" out += "(#{node.arguments.map { |a| generate(a).as(String) }.join(", ")})" if node.arguments.any? - out += " on #{node.locations.join(" | ")}" + out + " on #{node.locations.join(" | ")}" end # def self.generate(node : AbstractNode, indent : String = "") @@ -226,7 +226,6 @@ module GraphQL def self.generate(node, indent : String = "") raise "TypeError (please define it :) )" - "" end def self.generate_directives(directives, indent : String = "") @@ -253,7 +252,7 @@ module GraphQL return "" unless node.description description = indent != "" && !first_in_block ? "\n" : "" - description += "#{indent}# #{node.description}\n" + description + "#{indent}# #{node.description}\n" end def self.generate_field_definitions(fields, indent : String = "") diff --git a/src/graphql-crystal/language/lexer_context.cr b/src/graphql-crystal/language/lexer_context.cr index 15dbdbf..ed459a0 100644 --- a/src/graphql-crystal/language/lexer_context.cr +++ b/src/graphql-crystal/language/lexer_context.cr @@ -24,7 +24,7 @@ class GraphQL::Language::LexerContext return read_number() if code.number? || code == '-' return read_string() if code == '"' - raise Exception.new("Unexpected character '#{code}' at #{@current_index} near #{@source[@current_index-15,30]}") + raise Exception.new("Unexpected character '#{code}' at #{@current_index} near #{@source[@current_index - 15, 30]}") end def only_hex_in_string(test) @@ -34,7 +34,7 @@ class GraphQL::Language::LexerContext def read_comment : Token start = @current_index chunk_start = (@current_index += 1) - + code = get_code value = "" @@ -53,25 +53,27 @@ class GraphQL::Language::LexerContext code = @source[start] code = self.next_code if code == '-' next_code_char = code == '0' ? self.next_code : read_digits_from_own_source(code) - raise Exception.new("Invalid number, unexpected digit after #{code}: #{next_code_char}") if (next_code_char.ord >= 48 && next_code_char.ord <= 57) + if (next_code_char.ord >= 48 && next_code_char.ord <= 57) + raise Exception.new("Invalid number, unexpected digit after #{code}: #{next_code_char}") + end code = next_code_char + if code == '.' is_float = true - code = read_digits_from_own_source(self.next_code()) + code = read_digits_from_own_source(self.next_code) end if code == 'E' || code == 'e' is_float = true - code = self.next_code() + code = self.next_code if code == '+' || code == '-' - code = self.next_code() + code = self.next_code end - code = read_digits_from_own_source(code) + read_digits_from_own_source(code) end - is_float ? create_float_token(start) - : create_int_token(start) + is_float ? create_float_token(start) : create_int_token(start) end def read_string : Token @@ -89,29 +91,31 @@ class GraphQL::Language::LexerContext value + @source[chunk_start, (@current_index - chunk_start - 1)] end - private def append_to_value_by_code(value, code) - case code - when '"' - value += '"' - when '/' - value += '/' - when '\\' - value += '\\' - when 'b' - value += '\b' - when 'f' - value += '\f' - when 'n' - value += '\n' - when 'r' - value += '\r' - when 't' - value += '\t' - when 'u' - value += get_unicode_char - else - raise Exception.new("Invalid character escape sequence: \\#{code}.") - end + private def append_to_value_by_code!(value, code) + # ameba:disable Lint/UselessAssign + value += case code + when '"' + '"' + when '/' + '/' + when '\\' + '\\' + when 'b' + '\b' + when 'f' + '\f' + when 'n' + '\n' + when 'r' + '\r' + when 't' + '\t' + when 'u' + get_unicode_char + else + raise Exception.new("Invalid character escape sequence: \\#{code}.") + end + # ameba:enable Lint/UselessAssign end private def check_for_invalid_characters(code) @@ -119,34 +123,18 @@ class GraphQL::Language::LexerContext end private def check_for_punctuation_tokens(code) - case code - when '!' - create_punctuation_token(Token::Kind::BANG, 1) - when '$' - create_punctuation_token(Token::Kind::DOLLAR, 1) - when '(' - create_punctuation_token(Token::Kind::PAREN_L, 1) - when ')' - create_punctuation_token(Token::Kind::PAREN_R, 1) - when '.' - check_for_spread_operator() - when ':' - create_punctuation_token(Token::Kind::COLON, 1) - when '=' - create_punctuation_token(Token::Kind::EQUALS, 1) - when '@' - create_punctuation_token(Token::Kind::AT, 1) - when '[' - create_punctuation_token(Token::Kind::BRACKET_L, 1) - when ']' - create_punctuation_token(Token::Kind::BRACKET_R, 1) - when '{' - create_punctuation_token(Token::Kind::BRACE_L, 1) - when '|' - create_punctuation_token(Token::Kind::PIPE, 1) - when '}' - create_punctuation_token(Token::Kind::BRACE_R, 1) - end + tokens = { + '!' => Token::Kind::BANG, '$' => Token::Kind::DOLLAR, + '(' => Token::Kind::PAREN_L, ')' => Token::Kind::PAREN_R, + ':' => Token::Kind::COLON, '=' =>Token::Kind::EQUALS, + '@' => Token::Kind::AT, '[' => Token::Kind::BRACKET_L, + ']' => Token::Kind::BRACKET_R, '{' => Token::Kind::BRACE_L, + '}' => Token::Kind::BRACE_R, '|' => Token::Kind::PIPE + } + + return create_punctuation_token(tokens[code], 1) if tokens[code]? + return check_for_spread_operator() if code == '.' + nil end private def check_for_spread_operator : Token? @@ -197,23 +185,23 @@ class GraphQL::Language::LexerContext end private def get_unicode_char - if @current_index + 5 > @source.size - truncated_expression = @source[@current_index, @source.size] - raise Exception.new("Invalid character escape sequence at EOF: \\#{truncated_expression}.") - end + if @current_index + 5 > @source.size + truncated_expression = @source[@current_index, @source.size] + raise Exception.new("Invalid character escape sequence at EOF: \\#{truncated_expression}.") + end - expression = @source[@current_index, 5] + expression = @source[@current_index, 5] - if !only_hex_in_string(expression[1, expression.size]) - raise Exception.new("Invalid character escape sequence: \\#{expression}.") - end + if !only_hex_in_string(expression[1, expression.size]) + raise Exception.new("Invalid character escape sequence: \\#{expression}.") + end - s = next_code.bytes << 12 | next_code.bytes << 8 | next_code.bytes << 4 | next_code.bytes - String.new(Slice.new(s.to_unsafe, 4))[0] + s = next_code.bytes << 12 | next_code.bytes << 8 | next_code.bytes << 4 | next_code.bytes + String.new(Slice.new(s.to_unsafe, 4))[0] end private def if_unicode_get_string : String - return @source.size > @current_index + 5 && + @source.size > @current_index + 5 && only_hex_in_string(@source[(@current_index + 2), 4]) ? @source[@current_index, 6] : null end @@ -223,7 +211,7 @@ class GraphQL::Language::LexerContext private def next_code @current_index += 1 - return is_not_at_the_end_of_query() ? @source[@current_index] : Char::ZERO + is_not_at_the_end_of_query() ? @source[@current_index] : Char::ZERO end private def process_character(value_ptr, chunk_start_ptr) @@ -231,13 +219,13 @@ class GraphQL::Language::LexerContext @current_index += 1 if code == '\\' - value_ptr.value = append_to_value_by_code(append_characters_from_last_chunk(value_ptr.value, chunk_start_ptr.value), get_code) + value_ptr.value = append_to_value_by_code!(append_characters_from_last_chunk(value_ptr.value, chunk_start_ptr.value), get_code) @current_index += 1 chunk_start_ptr.value = @current_index end - return get_code + get_code end private def process_string_chunks @@ -264,9 +252,10 @@ class GraphQL::Language::LexerContext raise Exception.new("Invalid number, expected digit but got: #{resolve_char_name(code)}") end - while true + condition = false + while !condition code = (position += 1) < body.size ? body[position] : Char::ZERO - break unless code.number? + condition = true unless code.number? end position @@ -281,10 +270,11 @@ class GraphQL::Language::LexerContext start = @current_index code = Char::ZERO - while true + condition = false + while !condition @current_index += 1 code = get_code - break unless is_not_at_the_end_of_query && is_valid_name_character(code) + condition = true unless is_not_at_the_end_of_query && is_valid_name_character(code) end create_name_token(start) @@ -294,7 +284,7 @@ class GraphQL::Language::LexerContext return "" if (code == '\0') return "\"#{unicode_string}\"" if unicode_string && !unicode_string.blank? - return "\"#{code}\"" + "\"#{code}\"" end private def validate_character_code(code) @@ -308,10 +298,10 @@ class GraphQL::Language::LexerContext while (position += 1) < body.size && (code = body[position]) != 0 && (code.ord > 0x001F || code.ord == 0x0009) && code.ord != 0x000A && code.ord != 0x000D end - return position + position end private def get_code - return is_not_at_the_end_of_query ? @source[@current_index] : Char::ZERO + is_not_at_the_end_of_query ? @source[@current_index] : Char::ZERO end -end \ No newline at end of file +end diff --git a/src/graphql-crystal/language/nodes.cr b/src/graphql-crystal/language/nodes.cr index c2c9161..8104e04 100644 --- a/src/graphql-crystal/language/nodes.cr +++ b/src/graphql-crystal/language/nodes.cr @@ -56,10 +56,8 @@ module GraphQL # # { ... } # class Document < AbstractNode - values({definitions: Array( - OperationDefinition | FragmentDefinition | SchemaDefinition | ObjectTypeDefinition | InputObjectTypeDefinition | - ScalarTypeDefinition | DirectiveDefinition | EnumTypeDefinition | InterfaceTypeDefinition | UnionTypeDefinition - )}) + values({definitions: Array(OperationDefinition | FragmentDefinition | SchemaDefinition | ObjectTypeDefinition | InputObjectTypeDefinition | + ScalarTypeDefinition | DirectiveDefinition | EnumTypeDefinition | InterfaceTypeDefinition | UnionTypeDefinition)}) traverse :children, :definitions def to_query_string @@ -68,6 +66,7 @@ module GraphQL # def slice_definition(name) # GraphQL::Language::DefinitionSlice.slice(self, name) # end + end class SchemaDefinition < AbstractNode @@ -183,7 +182,7 @@ module GraphQL when InputObject v.to_h when Array - v.map { |v| v.as(FValue) } + v.map { |i| i.as(FValue) } else v end.as(FValue) diff --git a/src/graphql-crystal/language/parser.cr b/src/graphql-crystal/language/parser.cr index 5e062ab..cee0f51 100644 --- a/src/graphql-crystal/language/parser.cr +++ b/src/graphql-crystal/language/parser.cr @@ -11,4 +11,4 @@ class GraphQL::Language::Parser context = Language::ParserContext.new(source, @lexer) context.parse end -end \ No newline at end of file +end diff --git a/src/graphql-crystal/language/parser_context.cr b/src/graphql-crystal/language/parser_context.cr index b60c46a..be7e172 100644 --- a/src/graphql-crystal/language/parser_context.cr +++ b/src/graphql-crystal/language/parser_context.cr @@ -20,7 +20,7 @@ class GraphQL::Language::ParserContext end def get_comment - @comments.size > 0 ? @comments.pop() : nil + @comments.size > 0 ? @comments.pop : nil end private def advance @@ -71,7 +71,6 @@ class GraphQL::Language::ParserContext private def create_inline_fragment(start) Language::InlineFragment.new( - get_type_condition, parse_directives, parse_selection_set, @@ -79,7 +78,9 @@ class GraphQL::Language::ParserContext end private def create_operation_definition(start, operation, name) + # ameba:disable Lint/UselessAssign comment = get_comment + # ameba:enable Lint/UselessAssign Language::OperationDefinition.new( operation_type: operation, name: name, @@ -90,7 +91,9 @@ class GraphQL::Language::ParserContext end private def create_operation_definition(start) + # ameba:disable Lint/UselessAssign comment = get_comment + # ameba:enable Lint/UselessAssign Language::OperationDefinition.new( operation_type: "query", name: nil, @@ -120,7 +123,7 @@ class GraphQL::Language::ParserContext return end - raise Exception.new("Expected \"#{keyword}\", found Name \"#{token.value}\""); + raise Exception.new("Expected \"#{keyword}\", found Name \"#{token.value}\"") end private def expect_on_keyword_and_parse_named_type @@ -138,7 +141,7 @@ class GraphQL::Language::ParserContext end private def get_name - peek(Token::Kind::NAME) ? parse_name : nil; + peek(Token::Kind::NAME) ? parse_name : nil end private def get_type_condition @@ -165,8 +168,12 @@ class GraphQL::Language::ParserContext end private def parse_argument + # ameba:disable Lint/UselessAssign comment = get_comment + # ameba:enable Lint/UselessAssign + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign Language::Argument.new( name: parse_name, @@ -176,14 +183,14 @@ class GraphQL::Language::ParserContext private def parse_argument_defs if !peek(Token::Kind::PAREN_L) - return [] of Language::InputValueDefinition; + return [] of Language::InputValueDefinition end many(Token::Kind::PAREN_L, ->{ parse_input_value_def }, Token::Kind::PAREN_R) end private def parse_arguments - peek(Token::Kind::PAREN_L) ? many(Token::Kind::PAREN_L, -> { parse_argument }, Token::Kind::PAREN_R) : [] of Language::Argument + peek(Token::Kind::PAREN_L) ? many(Token::Kind::PAREN_L, ->{ parse_argument }, Token::Kind::PAREN_R) : [] of Language::Argument end private def parse_boolean_value(token) @@ -195,6 +202,8 @@ class GraphQL::Language::ParserContext parse_value_literal(true) end + # Checks `@current_token` for tokenizing + # Decides what to do based on token private def parse_definition parse_comment @@ -212,13 +221,17 @@ class GraphQL::Language::ParserContext raise Exception.new("Unexpected #{@current_token.kind} '#{@current_token.value}' at #{@current_token.start_position},#{@current_token.end_position}") end + # Main method to convert the schema string + # into a Language::Document private def parse_definitions_if_not_eof definitions = [] of Language::AbstractNode if @current_token.kind != Token::Kind::EOF - while true + # Iterates through the string and tokenizes each char + condition = false + while !condition # yield parse_definition definitions.push(parse_definition) - break unless !skip(Token::Kind::EOF) + condition = true if skip(Token::Kind::EOF) end end definitions @@ -230,14 +243,17 @@ class GraphQL::Language::ParserContext end text = [] of String? + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign end_position : Int32 - while true + condition = false + while !condition text.push(@current_token.value) end_position = @current_token.end_position advance - break unless @current_token.kind == Token::Kind::COMMENT + condition = true unless @current_token.kind == Token::Kind::COMMENT end comment = text.join("\n") @@ -246,18 +262,21 @@ class GraphQL::Language::ParserContext end private def parse_directive + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect(Token::Kind::AT) Language::Directive.new( name: parse_name, arguments: parse_arguments, - ) end private def parse_directive_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("directive") expect(Token::Kind::AT) @@ -278,9 +297,10 @@ class GraphQL::Language::ParserContext private def parse_directive_locations locations = [] of String? - while true + condition = false + while !condition locations.push(parse_name) - break unless skip(Token::Kind::PIPE) + condition = true unless skip(Token::Kind::PIPE) end locations @@ -304,7 +324,9 @@ class GraphQL::Language::ParserContext private def parse_enum_type_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("enum") Language::EnumTypeDefinition.new( @@ -322,7 +344,9 @@ class GraphQL::Language::ParserContext private def parse_enum_value_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign Language::EnumValueDefinition.new( name: parse_name, @@ -334,7 +358,9 @@ class GraphQL::Language::ParserContext private def parse_field_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign name = parse_name args = parse_argument_defs expect(Token::Kind::COLON) @@ -375,7 +401,7 @@ class GraphQL::Language::ParserContext start = @current_token.start_position expect(Token::Kind::SPREAD) - if peek(Token::Kind::NAME) && !@current_token.value. == "on" + if peek(Token::Kind::NAME) && !@current_token.value.== "on" return create_graphql_fragment_spread(start) end @@ -383,8 +409,12 @@ class GraphQL::Language::ParserContext end private def parse_fragment_definition + # ameba:disable Lint/UselessAssign comment = get_comment + # ameba:enable Lint/UselessAssign + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("fragment") Language::FragmentDefinition.new( @@ -410,9 +440,10 @@ class GraphQL::Language::ParserContext if @current_token.value == "implements" advance - while true + condition = false + while !condition types.push(parse_name) - break unless peek(Token::Kind::NAME) + condition = true unless peek(Token::Kind::NAME) end end @@ -421,7 +452,9 @@ class GraphQL::Language::ParserContext private def parse_input_object_type_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("input") Language::InputObjectTypeDefinition.new( @@ -434,10 +467,11 @@ class GraphQL::Language::ParserContext private def parse_input_value_def comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position - name = parse_name; - expect(Token::Kind::COLON); - + # ameba:enable Lint/UselessAssign + name = parse_name + expect(Token::Kind::COLON) Language::InputValueDefinition.new( name: name, type: parse_type, @@ -455,7 +489,9 @@ class GraphQL::Language::ParserContext private def parse_interface_type_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("interface") Language::InterfaceTypeDefinition.new( @@ -467,7 +503,9 @@ class GraphQL::Language::ParserContext end private def parse_list(is_constant) : Language::ArgumentValue + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign constant = Proc(Language::ArgumentValue).new { parse_constant_value } value = Proc(Language::ArgumentValue).new { parse_value_value } @@ -475,7 +513,9 @@ class GraphQL::Language::ParserContext end private def parse_name : String? + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign value = @current_token.value expect(Token::Kind::NAME) @@ -483,42 +523,39 @@ class GraphQL::Language::ParserContext end private def parse_named_definition - case @current_token.value - when "query", "mutation", "subscription" - parse_operation_definition - when "fragment" - parse_fragment_definition - when "schema" - parse_schema_definition - when "scalar" - parse_scalar_type_definition - when "type" - parse_object_type_definition - when "interface" - parse_interface_type_definition - when "union" - parse_union_type_definition - when "enum" - parse_enum_type_definition - when "input" - parse_input_object_type_definition - # when "extend" - # parse_type_extension_definition - when "directive" - parse_directive_definition - else - nil - end + {% begin %} + case @current_token.value + {% for i in ["interface", "union", "enum", "scalar"] %} + when {{i}} + parse_{{i.id}}_type_definition + {% end %} + when "type" + parse_object_type_definition + when "input" + parse_input_object_type_definition + when "directive" + parse_directive_definition + {% for i in ["query", "mutation", "subscription", "fragment", "schema"] %} + when {{i}} + parse_{{i.id}}_definition + {% end %} + # when "extend" + # parse_type_extension_definition + else + nil + end + {% end %} end private def parse_named_type : Language::TypeName + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign Language::TypeName.new(name: parse_name) end private def parse_name_value(is_constant) - token = @current_token; - + token = @current_token if token.value == "true" || token.value == "false" return parse_boolean_value(token) elsif !token.value.nil? @@ -533,8 +570,12 @@ class GraphQL::Language::ParserContext end private def parse_object(is_constant) + # ameba:disable Lint/UselessAssign comment = get_comment + # ameba:enable Lint/UselessAssign + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign Language::InputObject.new(arguments: parse_object_fields(is_constant)) end @@ -545,8 +586,12 @@ class GraphQL::Language::ParserContext end private def parse_object_field(is_constant) + # ameba:disable Lint/UselessAssign comment = get_comment + # ameba:enable Lint/UselessAssign + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign Language::Argument.new( name: parse_name, value: expect_colon_and_parse_value_literal(is_constant) @@ -567,7 +612,9 @@ class GraphQL::Language::ParserContext private def parse_object_type_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("type") Language::ObjectTypeDefinition.new( @@ -579,6 +626,18 @@ class GraphQL::Language::ParserContext ) end + private def parse_query_definition + parse_operation_definition + end + + private def parse_mutation_definition + parse_operation_definition + end + + private def parse_subscription_definition + parse_operation_definition + end + private def parse_operation_definition start = @current_token.start_position @@ -586,7 +645,7 @@ class GraphQL::Language::ParserContext return create_operation_definition(start) end - create_operation_definition(start, parse_operation_type, get_name); + create_operation_definition(start, parse_operation_type, get_name) end private def parse_operation_type @@ -596,7 +655,9 @@ class GraphQL::Language::ParserContext end private def parse_operation_type_definition + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign operation = parse_operation_type expect(Token::Kind::COLON) type = parse_named_type @@ -606,7 +667,9 @@ class GraphQL::Language::ParserContext private def parse_scalar_type_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("scalar") name = parse_name directives = parse_directives @@ -619,14 +682,20 @@ class GraphQL::Language::ParserContext end private def parse_schema_definition + # ameba:disable Lint/UselessAssign comment = get_comment + # ameba:enable Lint/UselessAssign + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("schema") + # ameba:disable Lint/UselessAssign directives = parse_directives + # ameba:enable Lint/UselessAssign definitions = many(Token::Kind::BRACE_L, ->{ parse_operation_type_definition }, Token::Kind::BRACE_R) definitions = definitions.as(Array).reduce(Hash(String, String).new) do |memo, pair| - pair.as(Tuple(String, GraphQL::Language::TypeName)).tap { |pair| memo[pair[0]] = pair[1].name } + pair.as(Tuple(String, GraphQL::Language::TypeName)).tap { |_pair| memo[_pair[0]] = _pair[1].name } memo end @@ -638,11 +707,13 @@ class GraphQL::Language::ParserContext end private def parse_selection - return peek(Token::Kind::SPREAD) ? parse_fragment : parse_field_selection + peek(Token::Kind::SPREAD) ? parse_fragment : parse_field_selection end private def parse_selection_set + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign many(Token::Kind::BRACE_L, ->{ parse_selection }, Token::Kind::BRACE_R) end @@ -654,7 +725,9 @@ class GraphQL::Language::ParserContext private def parse_type type = nil + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign if skip(Token::Kind::BRACKET_L) type = parse_type expect(Token::Kind::BRACKET_R) @@ -684,8 +757,7 @@ class GraphQL::Language::ParserContext private def parse_union_members members = [] of Language::TypeName - while - members.push(Language::TypeName.new(name: parse_named_type.name)) + while members.push(Language::TypeName.new(name: parse_named_type.name)) break unless skip(Token::Kind::PIPE) end @@ -694,7 +766,9 @@ class GraphQL::Language::ParserContext private def parse_union_type_definition comment = get_comment + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect_keyword("union") name = parse_name directives = parse_directives @@ -727,9 +801,10 @@ class GraphQL::Language::ParserContext return parse_name_value(is_constant) when Token::Kind::DOLLAR return parse_variable if !is_constant + else end - raise Exception.new("Unexpected #{@current_token.kind} at #{@current_token.start_position} near #{@source[@current_token.start_position-15,30]}") + raise Exception.new("Unexpected #{@current_token.kind} at #{@current_token.start_position} near #{@source[@current_token.start_position - 15, 30]}") end private def parse_value_value : Language::ArgumentValue @@ -737,14 +812,18 @@ class GraphQL::Language::ParserContext end private def parse_variable + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign expect(Token::Kind::DOLLAR) Language::VariableIdentifier.new(name: get_name) end private def parse_variable_definition : Language::VariableDefinition + # ameba:disable Lint/UselessAssign start = @current_token.start_position + # ameba:enable Lint/UselessAssign Language::VariableDefinition.new( name: parse_variable.name, type: advance_through_colon_and_parse_type, @@ -753,9 +832,7 @@ class GraphQL::Language::ParserContext end private def parse_variable_definitions : Array(Language::VariableDefinition) - return peek(Token::Kind::PAREN_L) ? - many(Token::Kind::PAREN_L, ->{ parse_variable_definition }, Token::Kind::PAREN_R) : - [] of Language::VariableDefinition + peek(Token::Kind::PAREN_L) ? many(Token::Kind::PAREN_L, ->{ parse_variable_definition }, Token::Kind::PAREN_R) : [] of Language::VariableDefinition end private def peek(kind) diff --git a/src/graphql-crystal/language/token.cr b/src/graphql-crystal/language/token.cr index 1516146..b448b43 100644 --- a/src/graphql-crystal/language/token.cr +++ b/src/graphql-crystal/language/token.cr @@ -36,47 +36,85 @@ class GraphQL::Language::Token end def self.get_token_kind_description(kind : Kind) : String - case kind - when Kind::EOF - "EOF" - when Kind::BANG - "!" - when Kind::DOLLAR - "$" - when Kind::PAREN_L - "(" - when Kind::PAREN_R - ")" - when Kind::SPREAD - "..." - when Kind::COLON - ":" - when Kind::EQUALS - "=" - when Kind::AT - "@" - when Kind::BRACKET_L - "[" - when Kind::BRACKET_R - "]" - when Kind::BRACE_L - "{" - when Kind::PIPE - "|" - when Kind::BRACE_R - "}" - when Kind::NAME - "Name" - when Kind::INT - "Int" - when Kind::FLOAT - "Float" - when Kind::STRING - "String" - when Kind::COMMENT - "#" - else - ""; - end - end -end \ No newline at end of file + case kind + else + "" + end + end + + def self.get_token_kind_description(kind : Kind::EOF) : String + "EOF" + end + + def self.get_token_kind_description(kind : Kind::BANG) : String + "!" + end + + def self.get_token_kind_description(kind : Kind::DOLLAR) : String + "$" + end + + def self.get_token_kind_description(kind : Kind::PAREN_L) : String + "(" + end + + def self.get_token_kind_description(kind : Kind::PAREN_R) : String + ")" + end + + def self.get_token_kind_description(kind : Kind::SPREAD) : String + "..." + end + + def self.get_token_kind_description(kind : Kind::EQUALS) : String + "=" + end + + def self.get_token_kind_description(kind : Kind::AT) : String + "@" + end + + def self.get_token_kind_description(kind : Kind::COLON) : String + ":" + end + + def self.get_token_kind_description(kind : Kind::BRACKET_L) : String + "[" + end + + def self.get_token_kind_description(kind : Kind::BRACKET_R) : String + "]" + end + + def self.get_token_kind_description(kind : Kind::BRACE_L) : String + "{" + end + + def self.get_token_kind_description(kind : Kind::BRACE_R) : String + "}" + end + + def self.get_token_kind_description(kind : Kind::PIPE) : String + "|" + end + + def self.get_token_kind_description(kind : Kind::NAME) : String + "Name" + end + + def self.get_token_kind_description(kind : Kind::INT) : String + "Int" + end + + def self.get_token_kind_description(kind : Kind::FLOAT) : String + "Float" + end + + def self.get_token_kind_description(kind : Kind::STRING) : String + "String" + end + + def self.get_token_kind_description(kind : Kind::COMMENT) : String + "#" + end +end diff --git a/src/graphql-crystal/schema/schema.cr b/src/graphql-crystal/schema/schema.cr index c10d340..5fa1700 100644 --- a/src/graphql-crystal/schema/schema.cr +++ b/src/graphql-crystal/schema/schema.cr @@ -76,7 +76,7 @@ module GraphQL # # Descriptions for Scalar Types # - ScalarTypes = { + SCALARTYPES = { {"String", "A String Value"}, {"Boolean", "A Boolean Value"}, {"Int", "An Integer Number"}, @@ -114,23 +114,24 @@ module GraphQL schema = uninitialized Language::SchemaDefinition - ScalarTypes.each do |(type_name, description)| + SCALARTYPES.each do |(type_name, description)| types[type_name] = Language::ScalarTypeDefinition.new( name: type_name, description: description, directives: [] of Language::Directive ) end - node.map_children do |node| - case node + node.map_children do |n| + case n when Language::SchemaDefinition - schema = node + schema = n when Language::TypeDefinition - types[node.name] = node + types[n.name] = n when Language::DirectiveDefinition - directives[node.name] = node + directives[n.name] = n + else end - node + n end return {schema, types, directives} end diff --git a/src/graphql-crystal/schema/schema_execute.cr b/src/graphql-crystal/schema/schema_execute.cr index cbda2c2..282e0bc 100644 --- a/src/graphql-crystal/schema/schema_execute.cr +++ b/src/graphql-crystal/schema/schema_execute.cr @@ -131,15 +131,13 @@ module GraphQL end private def resolve_selections_for(field_definition, selections, resolved, context) - begin - _selections = GraphQL::Schema::FragmentResolver.resolve( - selections, - context.fragments - ) - _resolve_selections_for(field_definition, _selections.map(&.as(Language::Selection)).as(Array(Language::Selection)), resolved, context) - rescue e - {nil, [Error.new(message: e.message.as(String), path: [] of Int32 | String)]} - end + _selections = GraphQL::Schema::FragmentResolver.resolve( + selections, + context.fragments + ) + _resolve_selections_for(field_definition, _selections.map(&.as(Language::Selection)).as(Array(Language::Selection)), resolved, context) + rescue e + {nil, [Error.new(message: e.message.as(String), path: [] of Int32 | String)]} end # @@ -388,6 +386,7 @@ module GraphQL if value.is_a?(Language::VariableIdentifier) node.value = full_params[value.name].as(Language::ArgumentValue) end + else end node end @@ -402,9 +401,9 @@ module GraphQL defined.reduce({} of String => ReturnType) do |args, definition| provided = given.find(&.name.==(definition.name)) provided = provided ? provided.value : definition.default_value + unless @type_validation.accepts?(definition.type, provided) # # TODO: Custom Exceptions here please - raise %{argument "#{definition.name}" is expected to be of type: \ "#{Language::Generation.generate(definition.type)}"} end @@ -427,18 +426,19 @@ module GraphQL private def inline_inline_fragment_selections(type, selections) type = type.as(Language::ObjectTypeDefinition) - selections.reduce([] of Language::Field) do |selections, selection| + selections.reduce([] of Language::Field) do |_selections, selection| case selection when Language::Field - selections << selection + _selections << selection when Language::InlineFragment if selection.type.as(Language::TypeName).name == type.name # assign the fragments directive to the field # for later evaluation. - selections += selection.selections.map(&.as(Language::Field).tap { |f| f.directives = selection.directives }) + _selections += selection.selections.map(&.as(Language::Field).tap { |f| f.directives = selection.directives }) end + else end - selections + _selections end end @@ -455,6 +455,7 @@ module GraphQL collection << node when Language::FragmentDefinition result[2] << node + else end node end @@ -483,7 +484,7 @@ module GraphQL raw = v.raw case raw when Array - raw.map{|vv| cast_jsonany_to_jsontype(vv).as(JSONType)} + raw.map { |vv| cast_jsonany_to_jsontype(vv).as(JSONType) } when Hash raw.keys.reduce(Hash(String, JSONType).new) do |hash, key| hash[key] = cast_jsonany_to_jsontype(raw[key]) @@ -515,19 +516,22 @@ module GraphQL end private def cast_to_return(value) - case value - when Hash - value.reduce(Hash(String, ReturnType).new) do |memo, h| - memo[h[0]] = cast_to_return(h[1]).as(ReturnType) - memo - end - when Array - value.map { |v| cast_to_return(v).as(ReturnType) } - when GraphQL::Language::AEnum - value.name - else - value - end.as(ReturnType) + value.as(ReturnType) + end + + private def cast_to_return(value : Hash) + value.reduce(Hash(String, ReturnType).new) do |memo, h| + memo[h[0]] = cast_to_return(h[1]).as(ReturnType) + memo + end + end + + private def cast_to_return(value : Array) + value.map { |v| cast_to_return(v).as(ReturnType) } + end + + private def cast_to_return(value : GraphQL::Language::AEnum) + value.name.as(ReturnType) end end end diff --git a/src/graphql-crystal/schema/schema_introspection.cr b/src/graphql-crystal/schema/schema_introspection.cr index 6c66aed..1ccd284 100644 --- a/src/graphql-crystal/schema/schema_introspection.cr +++ b/src/graphql-crystal/schema/schema_introspection.cr @@ -275,16 +275,16 @@ module GraphQL field :fields do |args, context| _fields = (resolved_interfaces(context.schema).flat_map(&.fields) + fields) .reduce(Hash(String, FieldDefinition).new) do |dict, field| - dict[field.name] = field - dict - end.values.sort_by &.name + dict[field.name] = field + dict + end.values.sort_by &.name if args["includeDeprecated"] _fields else _fields.reject(&.directives.any?(&.name.==("deprecated"))) end end - field :interfaces { |args, context| resolved_interfaces(context.schema) } + field :interfaces { |_args, context| resolved_interfaces(context.schema) } def resolved_interfaces(schema) interfaces.map do |iface_name| @@ -295,12 +295,12 @@ module GraphQL class GraphQL::Language::UnionTypeDefinition field :kind { "UNION" } - field :possibleTypes { |args, context| types.map { |t| context.schema.type_resolve(t) } } + field :possibleTypes { |_args, context| types.map { |t| context.schema.type_resolve(t) } } end class GraphQL::Language::InterfaceTypeDefinition field :kind { "INTERFACE" } - field :possibleTypes do |args, context| + field :possibleTypes do |_args, context| context.schema.types.values.select do |t| t.is_a?(ObjectTypeDefinition) && t.interfaces.includes?(self.name) end @@ -315,7 +315,7 @@ module GraphQL class GraphQL::Language::WrapperType field :name { nil } - field :ofType { |args, context| context.schema.type_resolve(of_type) } + field :ofType { |_args, context| context.schema.type_resolve(of_type) } end class GraphQL::Language::ListType @@ -335,7 +335,7 @@ module GraphQL field :name field :description field :args { self.arguments } - field :type { |args, context| context.schema.type_resolve(type) } + field :type { |_args, context| context.schema.type_resolve(type) } end class GraphQL::Language::InputObjectTypeDefinition @@ -347,7 +347,7 @@ module GraphQL class GraphQL::Language::InputValueDefinition field :name field :description - field :type { |args, context| context.schema.type_resolve(type) } + field :type { |_args, context| context.schema.type_resolve(type) } field :defaultValue do val = ( default_value.is_a?(Language::AbstractNode) ? GraphQL::Language::Generation.generate(default_value) : ( diff --git a/src/graphql-crystal/schema/variable_resolver.cr b/src/graphql-crystal/schema/variable_resolver.cr index f36c367..a3a09dd 100644 --- a/src/graphql-crystal/schema/variable_resolver.cr +++ b/src/graphql-crystal/schema/variable_resolver.cr @@ -14,9 +14,9 @@ module GraphQL end def self.visit(field : Language::Field, params) - field.tap do |field| - field.selections = visit(field.selections, params).map &.as(Language::Selection) - field.arguments = visit(field.arguments, params).map &.as(Language::Argument) + field.tap do |_field| + _field.selections = visit(_field.selections, params).map &.as(Language::Selection) + _field.arguments = visit(_field.arguments, params).map &.as(Language::Argument) end end diff --git a/src/graphql-crystal/types/type_validation.cr b/src/graphql-crystal/types/type_validation.cr index b2c5760..7626bd3 100644 --- a/src/graphql-crystal/types/type_validation.cr +++ b/src/graphql-crystal/types/type_validation.cr @@ -12,65 +12,101 @@ module GraphQL # Returns true if `value` corresponds to # `type_definition`. # - def accepts?(type_definition : GraphQL::Language::AbstractNode, value) : Bool + def accepts?(type_definition : Language::EnumTypeDefinition, value) : Bool # Nillable by default .. - if value == nil && !type_definition.is_a?(Language::NonNullType) - return true + return true if value_is_nil?(type_definition, value) + + if value.is_a?(Language::AEnum) || value.is_a?(String) + @enum_values_cache[type_definition.name] ||= type_definition.fvalues.map(&.as(Language::EnumValueDefinition).name) + value_name = value.is_a?(Language::AEnum) ? value.name : value + @enum_values_cache[type_definition.name].not_nil!.includes? value_name + else + false end + end - case type_definition - when Language::EnumTypeDefinition - if value.is_a?(Language::AEnum) || value.is_a?(String) - @enum_values_cache[type_definition.name] ||= type_definition.fvalues.map(&.as(Language::EnumValueDefinition).name) - value_name = value.is_a?(Language::AEnum) ? value.name : value - @enum_values_cache[type_definition.name].not_nil!.includes? value_name - else - false - end - when Language::UnionTypeDefinition - type_definition.types.any? { |_type| accepts?(_type, value) } - when Language::NonNullType - value != nil ? accepts?(type_definition.of_type, value) : false - when Language::ListType - if value.is_a?(Array) - value.map { |v| accepts?(type_definition.of_type, v).as(Bool) }.all? { |r| !!r } - else - false - end - when Language::ScalarTypeDefinition - case type_definition.name - when "ID" - value.is_a?(Int) || value.is_a?(String) - when "Int" - value.is_a?(Int) - when "Float" - value.is_a?(Number) - when "String" - value.is_a?(String) - when "Boolean" - value.is_a?(Bool) - else - false - end - when Language::InputObjectTypeDefinition - _value = value.is_a?(Language::InputObject) ? value.to_h : value - return false unless _value.is_a? Hash - (type_definition.fields.map(&.name) + _value.keys).uniq.each do |key| - return false unless field = type_definition.fields.find(&.name.==(key)) - if _value.has_key?(field.name) - return false unless accepts?(field.type, _value[field.name]) - elsif field.default_value - return false unless accepts?(field.type, field.default_value) - else - return accepts?(field.type, nil) - end - end - return true - when Language::TypeName - accepts?(@types[type_definition.name], value) + def accepts?(type_definition : Language::UnionTypeDefinition, value) : Bool + # Nillable by default .. + return true if value_is_nil?(type_definition, value) + + type_definition.types.any? { |_type| accepts?(_type, value) } + end + + def accepts?(type_definition : Language::NonNullType, value) : Bool + # Nillable by default .. + return true if value_is_nil?(type_definition, value) + + value != nil ? accepts?(type_definition.of_type, value) : false + end + + def accepts?(type_definition : Language::ListType, value) : Bool + # Nillable by default .. + return true if value_is_nil?(type_definition, value) + + if value.is_a?(Array) + value.map { |v| accepts?(type_definition.of_type, v).as(Bool) }.all? { |r| !!r } else false end end + + def accepts?(type_definition : Language::ScalarTypeDefinition, value) : Bool + # Nillable by default .. + return true if value_is_nil?(type_definition, value) + + case type_definition.name + when "ID" + value.is_a?(Int) || value.is_a?(String) + when "Int" + value.is_a?(Int) + when "Float" + value.is_a?(Number) + when "String" + value.is_a?(String) + when "Boolean" + value.is_a?(Bool) + else + false + end + end + + def accepts?(type_definition : Language::InputObjectTypeDefinition, value) : Bool + # Nillable by default .. + return true if value_is_nil?(type_definition, value) + + _value = value.is_a?(Language::InputObject) ? value.to_h : value + return false unless _value.is_a? Hash + + (type_definition.fields.map(&.name) + _value.keys).uniq.each do |key| + return false unless field = type_definition.fields.find(&.name.==(key)) + + if _value.has_key?(field.name) + return false unless accepts?(field.type, _value[field.name]) + elsif field.default_value + return false unless accepts?(field.type, field.default_value) + else + return accepts?(field.type, nil) + end + end + true + end + + def accepts?(type_definition : Language::TypeName, value) : Bool + # Nillable by default .. + return true if value_is_nil?(type_definition, value) + + accepts?(@types[type_definition.name], value) + end + + def accepts?(type_definition : GraphQL::Language::AbstractNode, value) : Bool + # Nillable by default .. + return true if value_is_nil?(type_definition, value) + + false + end + + private def value_is_nil?(type_definition : GraphQL::Language::AbstractNode, value) : Bool + value == nil && !type_definition.is_a?(Language::NonNullType) + end end end