From 474f91845992d8a2be4405482819656c45693b84 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Tue, 6 Aug 2024 22:02:29 +0200 Subject: [PATCH 01/22] Add support for comments in arguments --- lib/graphql/language.rb | 1 + lib/graphql/language/comment.rb | 18 +++++++++++ .../document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 4 +-- lib/graphql/language/printer.rb | 10 +++++- lib/graphql/schema/argument.rb | 14 ++++++++- spec/graphql/language/printer_spec.rb | 31 +++++++++++++++++++ spec/graphql/schema/argument_spec.rb | 21 ++++++++++++- 8 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 lib/graphql/language/comment.rb diff --git a/lib/graphql/language.rb b/lib/graphql/language.rb index a98eefb637..4fdef5b736 100644 --- a/lib/graphql/language.rb +++ b/lib/graphql/language.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true require "graphql/language/block_string" +require "graphql/language/comment" require "graphql/language/printer" require "graphql/language/sanitized_printer" require "graphql/language/document_from_schema_definition" diff --git a/lib/graphql/language/comment.rb b/lib/graphql/language/comment.rb new file mode 100644 index 0000000000..a1064399bf --- /dev/null +++ b/lib/graphql/language/comment.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true +module GraphQL + module Language + module Comment + def self.print(str, indent: '') + lines = str.split("\n").map do |line| + comment_str = "".dup + comment_str << indent + comment_str << "# " + comment_str << line + comment_str.rstrip + end + + lines.join("\n") + "\n" + end + end + end +end diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index 2c4426ead1..b6134a293d 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -134,6 +134,7 @@ def build_argument_node(argument) type: build_type_name_node(argument.type), default_value: default_value, directives: directives(argument), + comment: argument.comment ) argument_node diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index 7a3220e3a5..4592157201 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -278,7 +278,7 @@ def generate_initialize scalar_method_names = @scalar_methods # TODO: These probably should be scalar methods, but `types` returns an array - [:types, :description].each do |extra_method| + [:types, :description, :comment].each do |extra_method| if method_defined?(extra_method) scalar_method_names += [extra_method] end @@ -652,7 +652,7 @@ class ScalarTypeExtension < AbstractNode end class InputValueDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name, :type, :default_value children_methods({ directives: GraphQL::Language::Nodes::Directive, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 04563c2c1d..445968609d 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -294,7 +294,7 @@ def print_input_value_definition(input_value) end def print_arguments(arguments, indent: "") - if arguments.all? { |arg| !arg.description } + if arguments.all? { |arg| !arg.description && !arg.comment } print_string("(") arguments.each_with_index do |arg, i| print_input_value_definition(arg) @@ -306,6 +306,7 @@ def print_arguments(arguments, indent: "") print_string("(\n") arguments.each_with_index do |arg, i| + print_comment(arg, indent: " " + indent, first_in_block: i == 0) print_description(arg, indent: " " + indent, first_in_block: i == 0) print_string(" ") print_string(indent) @@ -424,6 +425,13 @@ def print_description(node, indent: "", first_in_block: true) print_string(GraphQL::Language::BlockString.print(node.description, indent: indent)) end + def print_comment(node, indent: "", first_in_block: true) + return unless node.comment + + print_string("\n") if indent != "" && !first_in_block + print_string(GraphQL::Language::Comment.print(node.comment, indent: indent)) + end + def print_field_definitions(fields) return if fields.empty? diff --git a/lib/graphql/schema/argument.rb b/lib/graphql/schema/argument.rb index 08cbc90086..6f2a2d65e6 100644 --- a/lib/graphql/schema/argument.rb +++ b/lib/graphql/schema/argument.rb @@ -50,11 +50,12 @@ def from_resolver? # @param deprecation_reason [String] # @param validates [Hash, nil] Options for building validators, if any should be applied # @param replace_null_with_default [Boolean] if `true`, incoming values of `null` will be replaced with the configured `default_value` - def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block) + def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, comment: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block) arg_name ||= name @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s) @type_expr = type_expr || type @description = desc || description + @comment = comment @null = required != true @default_value = default_value if replace_null_with_default @@ -129,6 +130,17 @@ def description(text = nil) end end + attr_writer :comment + + # @return [String] Comment for this argument + def comment(text = nil) + if text + @comment = text + else + @comment + end + end + # @return [String] Deprecation reason for this argument def deprecation_reason(text = nil) if text diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index 894a6e0e69..21cbbb3328 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -263,6 +263,37 @@ end end + it "handles comments" do + query_type = Class.new(GraphQL::Schema::Object) do + graphql_name "Query" + field :issue, Integer do + argument :number, Integer, comment: "Argument comment" + end + + def issue(number:) + number + end + end + + schema = Class.new(GraphQL::Schema) do + query(query_type) + end + + expected = <<~SCHEMA.chomp + type Query { + issue( + # Argument comment + number: Int! + ): Int + } + SCHEMA + + assert_equal( + expected, + printer.print(schema.to_document), + ) + end + it "handles large ints" do query_type = Class.new(GraphQL::Schema::Object) do graphql_name "Query" diff --git a/spec/graphql/schema/argument_spec.rb b/spec/graphql/schema/argument_spec.rb index 6e1ddfe0f0..cc2079c6b2 100644 --- a/spec/graphql/schema/argument_spec.rb +++ b/spec/graphql/schema/argument_spec.rb @@ -29,11 +29,12 @@ def resolve(instruments:) class Query < GraphQL::Schema::Object field :field, String do - argument :arg, String, description: "test", required: false + argument :arg, String, description: "test", comment: "test comment", required: false argument :deprecated_arg, String, deprecation_reason: "don't use me!", required: false argument :arg_with_block, String, required: false do description "test" + comment "test comment" end argument :required_with_default_arg, Int, default_value: 1 argument :aliased_arg, String, required: false, as: :renamed @@ -147,6 +148,24 @@ def self.resolve_type(type, obj, ctx) end end + describe "#comment" do + let(:arg) { SchemaArgumentTest::Query.fields["field"].arguments["arg"] } + + it "sets comment" do + arg.comment "new comment" + assert_equal "new comment", arg.comment + end + + it "returns comment" do + assert_equal "test comment", SchemaArgumentTest::Query.fields["field"].arguments["argWithBlock"].comment + end + + it "has an assignment method" do + arg.comment = "another new comment" + assert_equal "another new comment", arg.comment + end + end + describe "as:" do it "uses that Symbol for Ruby kwargs" do query_str = <<-GRAPHQL From e99d99f164d0089780c823dadd75dc0e6c0bb305 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 12:32:09 +0200 Subject: [PATCH 02/22] Add support for comments of scalars --- .../language/document_from_schema_definition.rb | 3 ++- lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 7 ++++++- lib/graphql/schema/member/base_dsl_methods.rb | 14 ++++++++++++++ spec/graphql/language/printer_spec.rb | 12 ++++++++++++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index b6134a293d..d94d9003a4 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -116,6 +116,7 @@ def build_enum_value_node(enum_value) def build_scalar_type_node(scalar_type) GraphQL::Language::Nodes::ScalarTypeDefinition.new( name: scalar_type.graphql_name, + comment: scalar_type.comment, description: scalar_type.description, directives: directives(scalar_type), ) @@ -130,11 +131,11 @@ def build_argument_node(argument) argument_node = GraphQL::Language::Nodes::InputValueDefinition.new( name: argument.graphql_name, + comment: argument.comment, description: argument.description, type: build_type_name_node(argument.type), default_value: default_value, directives: directives(argument), - comment: argument.comment ) argument_node diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index 4592157201..fb3805044e 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -635,7 +635,7 @@ class SchemaExtension < AbstractNode end class ScalarTypeDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name children_methods({ directives: GraphQL::Language::Nodes::Directive, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 445968609d..6fae9e1f0a 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -255,7 +255,7 @@ def print_schema_definition(schema, extension: false) def print_scalar_type_definition(scalar_type, extension: false) - extension ? print_string("extend ") : print_description(scalar_type) + extension ? print_string("extend ") : print_description_and_comment(scalar_type) print_string("scalar ") print_string(scalar_type.name) print_directives(scalar_type.directives) @@ -432,6 +432,11 @@ def print_comment(node, indent: "", first_in_block: true) print_string(GraphQL::Language::Comment.print(node.comment, indent: indent)) end + def print_description_and_comment(node) + print_description(node) + print_comment(node) + end + def print_field_definitions(fields) return if fields.empty? diff --git a/lib/graphql/schema/member/base_dsl_methods.rb b/lib/graphql/schema/member/base_dsl_methods.rb index 8f97dd6d7b..12bcaf2af5 100644 --- a/lib/graphql/schema/member/base_dsl_methods.rb +++ b/lib/graphql/schema/member/base_dsl_methods.rb @@ -50,6 +50,20 @@ def description(new_description = nil) end end + # Call this method to provide a new comment; OR + # call it without an argument to get the comment + # @param new_comment [String] + # @return [String, nil] + def comment(new_comment = nil) + if new_comment + @comment = new_comment + elsif defined?(comment) + @comment + else + @comment = nil + end + end + # This pushes some configurations _down_ the inheritance tree, # in order to prevent repetitive lookups at runtime. module ConfigurationExtension diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index 21cbbb3328..f7719cd58e 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -264,10 +264,17 @@ end it "handles comments" do + scalar = Class.new(GraphQL::Schema::Scalar) do + graphql_name "DateTime" + + comment "Scalar comment" + end + query_type = Class.new(GraphQL::Schema::Object) do graphql_name "Query" field :issue, Integer do argument :number, Integer, comment: "Argument comment" + argument :date_time, scalar end def issue(number:) @@ -280,8 +287,13 @@ def issue(number:) end expected = <<~SCHEMA.chomp + # Scalar comment + scalar DateTime + type Query { issue( + dateTime: DateTime! + # Argument comment number: Int! ): Int From 13c6555b0d9afcf8dd3ab022c77440291a3c4274 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 13:25:23 +0200 Subject: [PATCH 03/22] Add support for comments of interfaces --- .../document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 2 +- lib/graphql/schema/interface.rb | 2 +- spec/graphql/language/printer_spec.rb | 13 ++++++++++++- spec/graphql/schema/interface_spec.rb | 19 +++++++++++++++++++ 6 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index d94d9003a4..2dd626039a 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -87,6 +87,7 @@ def build_union_type_node(union_type) def build_interface_type_node(interface_type) GraphQL::Language::Nodes::InterfaceTypeDefinition.new( name: interface_type.graphql_name, + comment: interface_type.comment, interfaces: @types.interfaces(interface_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) }, description: interface_type.description, fields: build_field_nodes(@types.fields(interface_type)), diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index fb3805044e..1a60bd957c 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -700,7 +700,7 @@ class ObjectTypeExtension < AbstractNode end class InterfaceTypeDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name children_methods({ interfaces: GraphQL::Language::Nodes::TypeName, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 6fae9e1f0a..533602f95a 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -329,7 +329,7 @@ def print_field_definition(field) end def print_interface_type_definition(interface_type, extension: false) - extension ? print_string("extend ") : print_description(interface_type) + extension ? print_string("extend ") : print_description_and_comment(interface_type) print_string("interface ") print_string(interface_type.name) print_implements(interface_type) if interface_type.interfaces.any? diff --git a/lib/graphql/schema/interface.rb b/lib/graphql/schema/interface.rb index bf04573333..42724771f9 100644 --- a/lib/graphql/schema/interface.rb +++ b/lib/graphql/schema/interface.rb @@ -22,7 +22,7 @@ module DefinitionMethods def definition_methods(&block) # Use an instance variable to tell whether it's been included previously or not; # You can't use constant detection because constants are brought into scope - # by `include`, which has already happened at this point. + # by `incllready happenude`, which has aed at this point. if !defined?(@_definition_methods) defn_methods_module = Module.new @_definition_methods = defn_methods_module diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index f7719cd58e..bcc3fbf7a0 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -264,6 +264,12 @@ end it "handles comments" do + module MyInterface + include GraphQL::Schema::Interface + + comment "Interface comment" + end + scalar = Class.new(GraphQL::Schema::Scalar) do graphql_name "DateTime" @@ -271,6 +277,8 @@ end query_type = Class.new(GraphQL::Schema::Object) do + implements MyInterface + graphql_name "Query" field :issue, Integer do argument :number, Integer, comment: "Argument comment" @@ -289,8 +297,11 @@ def issue(number:) expected = <<~SCHEMA.chomp # Scalar comment scalar DateTime + + # Interface comment + interface MyInterface - type Query { + type Query implements MyInterface { issue( dateTime: DateTime! diff --git a/spec/graphql/schema/interface_spec.rb b/spec/graphql/schema/interface_spec.rb index 851da35deb..ef309f9a52 100644 --- a/spec/graphql/schema/interface_spec.rb +++ b/spec/graphql/schema/interface_spec.rb @@ -158,6 +158,25 @@ module InterfaceE end end + describe "comments" do + class SchemaWithInterface < GraphQL::Schema + module InterfaceWithComment + include GraphQL::Schema::Interface + comment "Interface comment" + end + + class Query < GraphQL::Schema::Object + implements InterfaceWithComment + end + + query(Query) + end + + it "assigns comment to the interface" do + assert_equal("Interface comment", SchemaWithInterface::Query.interfaces[0].comment) + end + end + describe "can implement other interfaces" do class InterfaceImplementsSchema < GraphQL::Schema module InterfaceA From 8ce7361d74441be31b58cf3b696c3324511a09ba Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 13:43:32 +0200 Subject: [PATCH 04/22] Add support for comments of fields --- .../document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 1 + lib/graphql/schema/field.rb | 24 +++++++++++++++++-- lib/graphql/schema/resolver.rb | 3 ++- spec/graphql/language/printer_spec.rb | 3 ++- spec/graphql/schema/field_spec.rb | 8 +++++++ 7 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index 2dd626039a..725b329f92 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -68,6 +68,7 @@ def build_object_type_node(object_type) def build_field_node(field) GraphQL::Language::Nodes::FieldDefinition.new( name: field.graphql_name, + comment: field.comment, arguments: build_argument_nodes(@types.arguments(field)), type: build_type_name_node(field.type), description: field.description, diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index 1a60bd957c..c058cf3a37 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -661,7 +661,7 @@ class InputValueDefinition < AbstractNode end class FieldDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name, :type children_methods({ arguments: GraphQL::Language::Nodes::InputValueDefinition, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 533602f95a..9f65bdf516 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -444,6 +444,7 @@ def print_field_definitions(fields) i = 0 fields.each do |field| print_description(field, indent: " ", first_in_block: i == 0) + print_comment(field, indent: " ", first_in_block: i == 0) print_string(" ") print_field_definition(field) print_string("\n") diff --git a/lib/graphql/schema/field.rb b/lib/graphql/schema/field.rb index da7a3bf52f..94fe420ea9 100644 --- a/lib/graphql/schema/field.rb +++ b/lib/graphql/schema/field.rb @@ -106,7 +106,7 @@ def subscription_scope # @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration # @return [GraphQL::Schema:Field] an instance of `self` # @see {.initialize} for other options - def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block) + def self.from_options(name = nil, type = nil, desc = nil, comment: nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block) if (resolver_class = resolver || mutation || subscription) # Add a reference to that parent class kwargs[:resolver_class] = resolver_class @@ -116,6 +116,10 @@ def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutatio kwargs[:name] = name end + if comment + kwargs[:comment] = comment + end + if !type.nil? if desc if kwargs[:description] @@ -212,6 +216,7 @@ def method_conflict_warning? # @param owner [Class] The type that this field belongs to # @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null` # @param description [String] Field description + # @param comment [String] Field comment # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message # @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`) # @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`) @@ -236,7 +241,7 @@ def method_conflict_warning? # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method # @param validates [Array] Configurations for validating this field # @param fallback_value [Object] A fallback value if the method is not defined - def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block) + def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block) if name.nil? raise ArgumentError, "missing first `name` argument or keyword `name:`" end @@ -252,6 +257,7 @@ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CON @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s) @description = description + @comment = comment @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary self.deprecation_reason = deprecation_reason @@ -400,6 +406,20 @@ def description(text = nil) end end + # @param text [String] + # @return [String, nil] + def comment(text = nil) + if text + @comment = text + elsif !NOT_CONFIGURED.equal?(@comment) + @comment + elsif @resolver_class + @resolver_class.comment + else + nil + end + end + # Read extension instances from this field, # or add new classes/options to be initialized on this field. # Extensions are executed in the order they are added. diff --git a/lib/graphql/schema/resolver.rb b/lib/graphql/schema/resolver.rb index ce5e746885..703ba5f349 100644 --- a/lib/graphql/schema/resolver.rb +++ b/lib/graphql/schema/resolver.rb @@ -8,6 +8,7 @@ class Schema # - Arguments, via `.argument(...)` helper, which will be applied to the field. # - Return type, via `.type(..., null: ...)`, which will be applied to the field. # - Description, via `.description(...)`, which will be applied to the field + # - Comment, via `.comment(...)`, which will be applied to the field # - Resolution, via `#resolve(**args)` method, which will be called to resolve the field. # - `#object` and `#context` accessors for use during `#resolve`. # @@ -19,7 +20,7 @@ class Schema # @see {GraphQL::Function} `Resolver` is a replacement for `GraphQL::Function` class Resolver include Schema::Member::GraphQLTypeNames - # Really we only need description from here, but: + # Really we only need description & comment from here, but: extend Schema::Member::BaseDSLMethods extend GraphQL::Schema::Member::HasArguments extend GraphQL::Schema::Member::HasValidators diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index bcc3fbf7a0..5c10af0d11 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -280,7 +280,7 @@ module MyInterface implements MyInterface graphql_name "Query" - field :issue, Integer do + field :issue, Integer, comment: "Field comment" do argument :number, Integer, comment: "Argument comment" argument :date_time, scalar end @@ -302,6 +302,7 @@ def issue(number:) interface MyInterface type Query implements MyInterface { + # Field comment issue( dateTime: DateTime! diff --git a/spec/graphql/schema/field_spec.rb b/spec/graphql/schema/field_spec.rb index 129900bc34..1ace9ffc8a 100644 --- a/spec/graphql/schema/field_spec.rb +++ b/spec/graphql/schema/field_spec.rb @@ -61,6 +61,7 @@ field_defn = field :test do argument :test, String description "A Description." + comment "A Comment." type String end end @@ -72,6 +73,7 @@ assert_equal "test", object.fields["test"].arguments["test"].name assert_equal "A Description.", object.fields["test"].description + assert_equal "A Comment.", object.fields["test"].comment end it "sets connection? when type is given in a block" do @@ -100,11 +102,13 @@ field :test, String do |field| field.argument :test, String field.description "A Description." + field.comment "A Comment." end end assert_equal "test", object.fields["test"].arguments["test"].name assert_equal "A Description.", object.fields["test"].description + assert_equal "A Comment.", object.fields["test"].comment end it "accepts anonymous classes as type" do @@ -744,6 +748,7 @@ def ostruct_results it "Delegates many properties to its @resolver_class" do resolver = Class.new(GraphQL::Schema::Resolver) do description "description 1" + comment "comment 1" type [GraphQL::Types::Float], null: true argument :b, GraphQL::Types::Float @@ -755,6 +760,7 @@ def ostruct_results field.ensure_loaded assert_equal "description 1", field.description + assert_equal "comment 1", field.comment assert_equal "[Float!]", field.type.to_type_signature assert_equal 1, field.complexity assert_equal :resolve_with_support, field.resolver_method @@ -766,6 +772,7 @@ def ostruct_results assert_equal true, field.scoped? resolver.description("description 2") + resolver.comment("comment 2") resolver.type(GraphQL::Types::String, null: false) resolver.complexity(5) resolver.resolver_method(:blah) @@ -775,6 +782,7 @@ def ostruct_results resolver.argument(:c, GraphQL::Types::Boolean) assert_equal "description 2", field.description + assert_equal "comment 2", field.comment assert_equal "String!", field.type.to_type_signature assert_equal 5, field.complexity assert_equal :blah, field.resolver_method From 3cd87f0fc882ed9f993fa972ef97dd25ff0f46db Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 13:56:34 +0200 Subject: [PATCH 05/22] Add support for comments of mutations --- spec/graphql/language/printer_spec.rb | 29 ++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index 5c10af0d11..28013b52bb 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -290,17 +290,44 @@ def issue(number:) end end + mutation = Class.new(GraphQL::Schema::Mutation) do + graphql_name "CreateUser" + comment "Mutation comment" + + # TODO: Use Boolean + field :success, GraphQL::Types::Boolean, null: false + end + + mutation_type = Class.new(GraphQL::Schema::Object) do + graphql_name "Mutation" + + field :create_user, mutation: mutation + end + schema = Class.new(GraphQL::Schema) do query(query_type) + mutation(mutation_type) end expected = <<~SCHEMA.chomp + """ + Autogenerated return type of CreateUser. + """ + type CreateUserPayload { + success: Boolean! + } + # Scalar comment scalar DateTime + type Mutation { + # Mutation comment + createUser: CreateUserPayload + } + # Interface comment interface MyInterface - + type Query implements MyInterface { # Field comment issue( From 89058a0b08df37efb6e42ae5889c335998ecdeb5 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 14:06:41 +0200 Subject: [PATCH 06/22] Add support for comments of input object --- .../document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 2 +- spec/graphql/language/printer_spec.rb | 21 +++++++++++++++++-- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index 725b329f92..8d1ed926d7 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -146,6 +146,7 @@ def build_argument_node(argument) def build_input_object_node(input_object) GraphQL::Language::Nodes::InputObjectTypeDefinition.new( name: input_object.graphql_name, + comment: input_object.comment, fields: build_argument_nodes(@types.arguments(input_object)), description: input_object.description, directives: directives(input_object), diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index c058cf3a37..8ce2abd45b 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -767,7 +767,7 @@ class EnumTypeExtension < AbstractNode end class InputObjectTypeDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name children_methods({ directives: GraphQL::Language::Nodes::Directive, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 9f65bdf516..46c06f6e13 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -378,7 +378,7 @@ def print_enum_value_definition(enum_value) end def print_input_object_type_definition(input_object_type, extension: false) - extension ? print_string("extend ") : print_description(input_object_type) + extension ? print_string("extend ") : print_description_and_comment(input_object_type) print_string("input ") print_string(input_object_type.name) print_directives(input_object_type.directives) diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index 28013b52bb..a972825ab5 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -290,11 +290,20 @@ def issue(number:) end end + input_object = Class.new(GraphQL::Schema::InputObject) do + graphql_name "CreateUserInput" + + comment "Input object comment" + + argument :first_name, String + end + mutation = Class.new(GraphQL::Schema::Mutation) do graphql_name "CreateUser" comment "Mutation comment" - # TODO: Use Boolean + argument :input, input_object, comment: "Input argument comment" + field :success, GraphQL::Types::Boolean, null: false end @@ -310,6 +319,11 @@ def issue(number:) end expected = <<~SCHEMA.chomp + # Input object comment + input CreateUserInput { + firstName: String! + } + """ Autogenerated return type of CreateUser. """ @@ -322,7 +336,10 @@ def issue(number:) type Mutation { # Mutation comment - createUser: CreateUserPayload + createUser( + # Input argument comment + input: CreateUserInput! + ): CreateUserPayload } # Interface comment From d1408001faa7a81003deacf366cde476197527d6 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 14:23:09 +0200 Subject: [PATCH 07/22] Add support for coments of enum types --- .../language/document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 2 +- spec/graphql/language/printer_spec.rb | 17 +++++++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index 8d1ed926d7..057529b0a4 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -99,6 +99,7 @@ def build_interface_type_node(interface_type) def build_enum_type_node(enum_type) GraphQL::Language::Nodes::EnumTypeDefinition.new( name: enum_type.graphql_name, + comment: enum_type.comment, values: @types.enum_values(enum_type).sort_by(&:graphql_name).map do |enum_value| build_enum_value_node(enum_value) end, diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index 8ce2abd45b..b859ca9c41 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -748,7 +748,7 @@ class EnumValueDefinition < AbstractNode end class EnumTypeDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name children_methods({ directives: GraphQL::Language::Nodes::Directive, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 46c06f6e13..0a0a89a49f 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -356,7 +356,7 @@ def print_union_type_definition(union_type, extension: false) end def print_enum_type_definition(enum_type, extension: false) - extension ? print_string("extend ") : print_description(enum_type) + extension ? print_string("extend ") : print_description_and_comment(enum_type) print_string("enum ") print_string(enum_type.name) print_directives(enum_type.directives) diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index a972825ab5..fe9a1ba17f 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -290,12 +290,22 @@ def issue(number:) end end + enum_type = Class.new(GraphQL::Schema::Enum) do + graphql_name "UserRole" + + comment "Enum comment" + + value "ADMIN" + value "VIEWER" + end + input_object = Class.new(GraphQL::Schema::InputObject) do graphql_name "CreateUserInput" comment "Input object comment" argument :first_name, String + argument :role, enum_type end mutation = Class.new(GraphQL::Schema::Mutation) do @@ -322,6 +332,7 @@ def issue(number:) # Input object comment input CreateUserInput { firstName: String! + role: UserRole! } """ @@ -354,6 +365,12 @@ def issue(number:) number: Int! ): Int } + + # Enum comment + enum UserRole { + ADMIN + VIEWER + } SCHEMA assert_equal( From 0d27329be0ccb6968175d581b66b8b3ab607c200 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 14:30:03 +0200 Subject: [PATCH 08/22] Add support for comments of enum values --- .../language/document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 1 + lib/graphql/schema/enum.rb | 1 + lib/graphql/schema/enum_value.rb | 10 +++++++++- spec/graphql/language/printer_spec.rb | 4 +++- 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index 057529b0a4..f407383bcf 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -111,6 +111,7 @@ def build_enum_type_node(enum_type) def build_enum_value_node(enum_value) GraphQL::Language::Nodes::EnumValueDefinition.new( name: enum_value.graphql_name, + comment: enum_value.comment, description: enum_value.description, directives: directives(enum_value), ) diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index b859ca9c41..61d63e2232 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -739,7 +739,7 @@ class UnionTypeExtension < AbstractNode end class EnumValueDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name children_methods({ directives: GraphQL::Language::Nodes::Directive, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 0a0a89a49f..627a31a3ac 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -364,6 +364,7 @@ def print_enum_type_definition(enum_type, extension: false) print_string(" {\n") enum_type.values.each.with_index do |value, i| print_description(value, indent: " ", first_in_block: i == 0) + print_comment(value, indent: " ", first_in_block: i == 0) print_enum_value_definition(value) end print_string("}") diff --git a/lib/graphql/schema/enum.rb b/lib/graphql/schema/enum.rb index 3a20c59300..006963d7ab 100644 --- a/lib/graphql/schema/enum.rb +++ b/lib/graphql/schema/enum.rb @@ -59,6 +59,7 @@ class << self # Define a value for this enum # @option kwargs [String, Symbol] :graphql_name the GraphQL value for this, usually `SCREAMING_CASE` # @option kwargs [String] :description, the GraphQL description for this value, present in documentation + # @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation # @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`) # @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here # @return [void] diff --git a/lib/graphql/schema/enum_value.rb b/lib/graphql/schema/enum_value.rb index 8510f909de..9a28e0d4ac 100644 --- a/lib/graphql/schema/enum_value.rb +++ b/lib/graphql/schema/enum_value.rb @@ -30,10 +30,11 @@ class EnumValue < GraphQL::Schema::Member # @return [Class] The enum type that owns this value attr_reader :owner - def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, value: NOT_CONFIGURED, deprecation_reason: nil, &block) + def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, comment: nil, value: NOT_CONFIGURED, deprecation_reason: nil, &block) @graphql_name = graphql_name.to_s GraphQL::NameValidator.validate!(@graphql_name) @description = desc || description + @comment = comment @value = value == NOT_CONFIGURED ? @graphql_name : value if deprecation_reason self.deprecation_reason = deprecation_reason @@ -58,6 +59,13 @@ def description(new_desc = nil) @description end + def comment(new_comment = nil) + if new_comment + @comment = new_comment + end + @comment + end + def value(new_val = nil) unless new_val.nil? @value = new_val diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index fe9a1ba17f..107b6bf23b 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -296,7 +296,7 @@ def issue(number:) comment "Enum comment" value "ADMIN" - value "VIEWER" + value "VIEWER", comment: "Enum value comment" end input_object = Class.new(GraphQL::Schema::InputObject) do @@ -369,6 +369,8 @@ def issue(number:) # Enum comment enum UserRole { ADMIN + + # Enum value comment VIEWER } SCHEMA From 5329682e817f89a2b589e5815999c56c31a8f6c6 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 15:54:34 +0200 Subject: [PATCH 09/22] Add support for comments of object types --- .../document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 2 +- spec/graphql/language/printer_spec.rb | 42 ++++++++++++++++++- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index f407383bcf..8393ab6be0 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -58,6 +58,7 @@ def build_object_type_node(object_type) GraphQL::Language::Nodes::ObjectTypeDefinition.new( name: object_type.graphql_name, + comment: object_type.comment, interfaces: ints, fields: build_field_nodes(@types.fields(object_type)), description: object_type.description, diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index 61d63e2232..f5936fe840 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -681,7 +681,7 @@ def merge(new_options) end class ObjectTypeDefinition < AbstractNode - attr_reader :description + attr_reader :description, :comment scalar_methods :name, :interfaces children_methods({ directives: GraphQL::Language::Nodes::Directive, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 627a31a3ac..16622f6f47 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -262,7 +262,7 @@ def print_scalar_type_definition(scalar_type, extension: false) end def print_object_type_definition(object_type, extension: false) - extension ? print_string("extend ") : print_description(object_type) + extension ? print_string("extend ") : print_description_and_comment(object_type) print_string("type ") print_string(object_type.name) print_implements(object_type) unless object_type.interfaces.empty? diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index 107b6bf23b..50e52555b4 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -308,13 +308,36 @@ def issue(number:) argument :role, enum_type end + union = Class.new(GraphQL::Schema::Union) do + graphql_name "CreateUserResponse" + + possible_types( + Class.new(GraphQL::Schema::Object) do + graphql_name "CreateUserSuccess" + + field :user, (Class.new(GraphQL::Schema::Object) do + graphql_name "User" + + field :first_name, String + end) + end, + Class.new(GraphQL::Schema::Object) do + graphql_name "CreateUserError" + + comment "Object type comment" + + field :message, String, null: false + end + ) + end + mutation = Class.new(GraphQL::Schema::Mutation) do graphql_name "CreateUser" comment "Mutation comment" argument :input, input_object, comment: "Input argument comment" - field :success, GraphQL::Types::Boolean, null: false + field :payload, union, null: false end mutation_type = Class.new(GraphQL::Schema::Object) do @@ -329,6 +352,11 @@ def issue(number:) end expected = <<~SCHEMA.chomp + # Object type comment + type CreateUserError { + message: String! + } + # Input object comment input CreateUserInput { firstName: String! @@ -339,7 +367,13 @@ def issue(number:) Autogenerated return type of CreateUser. """ type CreateUserPayload { - success: Boolean! + payload: CreateUserResponse! + } + + union CreateUserResponse = CreateUserError | CreateUserSuccess + + type CreateUserSuccess { + user: User } # Scalar comment @@ -366,6 +400,10 @@ def issue(number:) ): Int } + type User { + firstName: String + } + # Enum comment enum UserRole { ADMIN From 3f72809cba06c7dcd9b2adc3afbb19c524c3308a Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 15:58:56 +0200 Subject: [PATCH 10/22] Add support for comments of unions --- lib/graphql/language/document_from_schema_definition.rb | 1 + lib/graphql/language/nodes.rb | 2 +- lib/graphql/language/printer.rb | 2 +- spec/graphql/language/printer_spec.rb | 3 +++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/graphql/language/document_from_schema_definition.rb b/lib/graphql/language/document_from_schema_definition.rb index 8393ab6be0..6dd6c43773 100644 --- a/lib/graphql/language/document_from_schema_definition.rb +++ b/lib/graphql/language/document_from_schema_definition.rb @@ -80,6 +80,7 @@ def build_field_node(field) def build_union_type_node(union_type) GraphQL::Language::Nodes::UnionTypeDefinition.new( name: union_type.graphql_name, + comment: union_type.comment, description: union_type.description, types: @types.possible_types(union_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) }, directives: directives(union_type), diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index f5936fe840..961f9b0ec5 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -721,7 +721,7 @@ class InterfaceTypeExtension < AbstractNode end class UnionTypeDefinition < AbstractNode - attr_reader :description, :types + attr_reader :description, :comment, :types scalar_methods :name children_methods({ directives: GraphQL::Language::Nodes::Directive, diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 16622f6f47..497c974e7c 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -338,7 +338,7 @@ def print_interface_type_definition(interface_type, extension: false) end def print_union_type_definition(union_type, extension: false) - extension ? print_string("extend ") : print_description(union_type) + extension ? print_string("extend ") : print_description_and_comment(union_type) print_string("union ") print_string(union_type.name) print_directives(union_type.directives) diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index 50e52555b4..6302dcb686 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -311,6 +311,8 @@ def issue(number:) union = Class.new(GraphQL::Schema::Union) do graphql_name "CreateUserResponse" + comment "Union comment" + possible_types( Class.new(GraphQL::Schema::Object) do graphql_name "CreateUserSuccess" @@ -370,6 +372,7 @@ def issue(number:) payload: CreateUserResponse! } + # Union comment union CreateUserResponse = CreateUserError | CreateUserSuccess type CreateUserSuccess { From 7d1097b5c76b6d3c93397fe285d693e2f823ab80 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Wed, 14 Aug 2024 16:07:58 +0200 Subject: [PATCH 11/22] Add support for comments of input object arguments --- lib/graphql/language/printer.rb | 1 + spec/graphql/language/printer_spec.rb | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/graphql/language/printer.rb b/lib/graphql/language/printer.rb index 497c974e7c..eb8a683351 100644 --- a/lib/graphql/language/printer.rb +++ b/lib/graphql/language/printer.rb @@ -387,6 +387,7 @@ def print_input_object_type_definition(input_object_type, extension: false) print_string(" {\n") input_object_type.fields.each.with_index do |field, i| print_description(field, indent: " ", first_in_block: i == 0) + print_comment(field, indent: " ", first_in_block: i == 0) print_string(" ") print_input_value_definition(field) print_string("\n") diff --git a/spec/graphql/language/printer_spec.rb b/spec/graphql/language/printer_spec.rb index 6302dcb686..220357277b 100644 --- a/spec/graphql/language/printer_spec.rb +++ b/spec/graphql/language/printer_spec.rb @@ -304,8 +304,10 @@ def issue(number:) comment "Input object comment" - argument :first_name, String - argument :role, enum_type + argument :first_name, String, comment: "Argument comment" + argument :role, enum_type do + comment "Argument comment" + end end union = Class.new(GraphQL::Schema::Union) do @@ -320,7 +322,7 @@ def issue(number:) field :user, (Class.new(GraphQL::Schema::Object) do graphql_name "User" - field :first_name, String + field :first_name, String, comment: "Field comment" end) end, Class.new(GraphQL::Schema::Object) do @@ -328,7 +330,9 @@ def issue(number:) comment "Object type comment" - field :message, String, null: false + field :message, String, null: false do + comment "Field comment" + end end ) end @@ -356,12 +360,16 @@ def issue(number:) expected = <<~SCHEMA.chomp # Object type comment type CreateUserError { + # Field comment message: String! } # Input object comment input CreateUserInput { + # Argument comment firstName: String! + + # Argument comment role: UserRole! } @@ -404,6 +412,7 @@ def issue(number:) } type User { + # Field comment firstName: String } From 53875dfffb83b70723aa27ae1629788634d65f11 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Fri, 23 Aug 2024 12:39:22 +0200 Subject: [PATCH 12/22] Add default empty param when marshalling the node --- lib/graphql/language/nodes.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index 961f9b0ec5..4f72600467 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -271,14 +271,17 @@ def scalars "pos: nil", "filename: nil", "source: nil", + "comment: nil" ] + IGNORED_MARSHALLING_KEYWORDS = [:comment] + def generate_initialize return if method_defined?(:marshal_load, false) # checking for `:initialize` doesn't work right scalar_method_names = @scalar_methods # TODO: These probably should be scalar methods, but `types` returns an array - [:types, :description, :comment].each do |extra_method| + [:types, :description].each do |extra_method| if method_defined?(extra_method) scalar_method_names += [extra_method] end @@ -307,6 +310,10 @@ def generate_initialize keywords = scalar_method_names.map { |m| "#{m}: #{m}"} + children_method_names.map { |m| "#{m}: #{m}" } + ignored_keywords = IGNORED_MARSHALLING_KEYWORDS.map do |keyword| + "#{keyword.to_s}: nil" + end + module_eval <<-RUBY, __FILE__, __LINE__ def initialize(#{arguments.join(", ")}) @line = line @@ -317,7 +324,7 @@ def initialize(#{arguments.join(", ")}) #{assignments.join("\n")} end - def self.from_a(filename, line, col, #{all_method_names.join(", ")}) + def self.from_a(filename, line, col, #{all_method_names.join(", ")}, #{ignored_keywords.join(", ")}) self.new(filename: filename, line: line, col: col, #{keywords.join(", ")}) end From de82aa38c3a01fd07c561949ac549304817d1035 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Fri, 23 Aug 2024 14:24:04 +0200 Subject: [PATCH 13/22] fixup! Add default empty param when marshalling the node --- lib/graphql/language/nodes.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/graphql/language/nodes.rb b/lib/graphql/language/nodes.rb index 4f72600467..cd60f358d2 100644 --- a/lib/graphql/language/nodes.rb +++ b/lib/graphql/language/nodes.rb @@ -270,8 +270,7 @@ def scalars "col: nil", "pos: nil", "filename: nil", - "source: nil", - "comment: nil" + "source: nil" ] IGNORED_MARSHALLING_KEYWORDS = [:comment] @@ -281,7 +280,7 @@ def generate_initialize scalar_method_names = @scalar_methods # TODO: These probably should be scalar methods, but `types` returns an array - [:types, :description].each do |extra_method| + [:types, :description, :comment].each do |extra_method| if method_defined?(extra_method) scalar_method_names += [extra_method] end @@ -314,6 +313,8 @@ def generate_initialize "#{keyword.to_s}: nil" end + marshalling_method_names = all_method_names - IGNORED_MARSHALLING_KEYWORDS + module_eval <<-RUBY, __FILE__, __LINE__ def initialize(#{arguments.join(", ")}) @line = line @@ -324,7 +325,7 @@ def initialize(#{arguments.join(", ")}) #{assignments.join("\n")} end - def self.from_a(filename, line, col, #{all_method_names.join(", ")}, #{ignored_keywords.join(", ")}) + def self.from_a(filename, line, col, #{marshalling_method_names.join(", ")}, #{ignored_keywords.join(", ")}) self.new(filename: filename, line: line, col: col, #{keywords.join(", ")}) end @@ -332,12 +333,12 @@ def marshal_dump [ line, col, # use methods here to force them to be calculated @filename, - #{all_method_names.map { |n| "@#{n}," }.join} + #{marshalling_method_names.map { |n| "@#{n}," }.join} ] end def marshal_load(values) - @line, @col, @filename #{all_method_names.map { |n| ", @#{n}"}.join} = values + @line, @col, @filename #{marshalling_method_names.map { |n| ", @#{n}"}.join} = values end RUBY end From be6320a9c9dccbe438b072ace07a353e4757d459 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Fri, 23 Aug 2024 15:26:19 +0200 Subject: [PATCH 14/22] Update docs vol. 1 --- guides/fields/introduction.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/guides/fields/introduction.md b/guides/fields/introduction.md index 1c62a52f39..c84fd8d3d9 100644 --- a/guides/fields/introduction.md +++ b/guides/fields/introduction.md @@ -21,7 +21,7 @@ The different elements of field definition are addressed below: - [Names](#field-names) identify the field in GraphQL - [Return types](#field-return-type) say what kind of data this field returns -- [Documentation](#field-documentation) includes description and deprecation notes +- [Documentation](#field-documentation) includes description, comments and deprecation notes - [Resolution behavior](#field-resolution) hooks up Ruby code to the GraphQL field - [Arguments](#field-arguments) allow fields to take input when they're queried - [Extra field metadata](#extra-field-metadata) for low-level access to the GraphQL-Ruby runtime @@ -67,7 +67,7 @@ field :scores, [Integer, null: true] # `[Int]`, may return a list or `nil`, the ## Field Documentation -Fields may be documented with a __description__ and may be __deprecated__. +Fields may be documented with a __description__, __comment__ and may be __deprecated__. __Descriptions__ can be added with the `field(...)` method as a positional argument, a keyword argument, or inside the block: @@ -85,6 +85,26 @@ field :name, String, null: false do end ``` +__Comments__ can be added with the `field(...)` method as a keyword argument, or inside the block: +```ruby +# `comment:` keyword +field :name, String, null: false, comment: "Rename to full name" + +# inside the block +field :name, String, null: false do + comment "Rename to full name" +end +``` + +Generates field name with comment above "Rename to full name" above. + +```graphql +type Foo { + # Rename to full name + name: String! +} +``` + __Deprecated__ fields can be marked by adding a `deprecation_reason:` keyword argument: ```ruby From a158259a073356757992f8226955ba1104cb93a0 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Fri, 23 Aug 2024 15:40:27 +0200 Subject: [PATCH 15/22] Fix flaky test --- spec/graphql/schema/object_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/graphql/schema/object_spec.rb b/spec/graphql/schema/object_spec.rb index b732a68c4b..9ec280b0d9 100644 --- a/spec/graphql/schema/object_spec.rb +++ b/spec/graphql/schema/object_spec.rb @@ -375,6 +375,7 @@ def self.type_error(err, ctx) shape.delete(:@configs) shape.delete(:@future_schema) shape.delete(:@metadata) + shape.delete(:@comment) if type_defn_shapes.add?(shape) example_shapes_by_name[cls.graphql_name] = shape end From 620ed193a6c2e9251009aa42dcc10398da6ed588 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Fri, 23 Aug 2024 15:52:26 +0200 Subject: [PATCH 16/22] Mention comments in the docs of enums, interaces, objects, scalars and unions --- guides/type_definitions/enums.md | 1 + guides/type_definitions/interfaces.md | 1 + guides/type_definitions/objects.md | 1 + guides/type_definitions/scalars.md | 1 + guides/type_definitions/unions.md | 1 + 5 files changed, 5 insertions(+) diff --git a/guides/type_definitions/enums.md b/guides/type_definitions/enums.md index 9feda4e5ed..120c08cfc9 100644 --- a/guides/type_definitions/enums.md +++ b/guides/type_definitions/enums.md @@ -59,6 +59,7 @@ end Each value may have: - A description (as the second argument or `description:` keyword) +- A comment (as a `comment:` keyword) - A deprecation reason (as `deprecation_reason:`), marking this value as deprecated - A corresponding Ruby value (as `value:`), see below diff --git a/guides/type_definitions/interfaces.md b/guides/type_definitions/interfaces.md index 7e95678d2a..254cfd18bf 100644 --- a/guides/type_definitions/interfaces.md +++ b/guides/type_definitions/interfaces.md @@ -80,6 +80,7 @@ Then, include that into each interface: ```ruby module Types::RetailItem include Types::BaseInterface + comment "TODO comment in the RetailItem interface" description "Something that can be bought" field :price, Types::Price, "How much this item costs", null: false diff --git a/guides/type_definitions/objects.md b/guides/type_definitions/objects.md index a61fec57a5..1718c2810b 100644 --- a/guides/type_definitions/objects.md +++ b/guides/type_definitions/objects.md @@ -71,6 +71,7 @@ end # then... class Types::TodoList < Types::BaseObject + comment "Comment of the TodoList type" description "A list of items which may be completed" field :name, String, "The unique name of this list", null: false diff --git a/guides/type_definitions/scalars.md b/guides/type_definitions/scalars.md index cada689ef3..209ef1cead 100644 --- a/guides/type_definitions/scalars.md +++ b/guides/type_definitions/scalars.md @@ -73,6 +73,7 @@ end # app/graphql/types/url.rb class Types::Url < Types::BaseScalar + comment "TODO comment of the scalar" description "A valid URL, transported as a string" def self.coerce_input(input_value, context) diff --git a/guides/type_definitions/unions.md b/guides/type_definitions/unions.md index d9ec127709..2c03a5b122 100644 --- a/guides/type_definitions/unions.md +++ b/guides/type_definitions/unions.md @@ -54,6 +54,7 @@ Then, extend that one for each union in your schema: ```ruby class Types::CommentSubject < Types::BaseUnion + comment "TODO comment on the union" description "Objects which may be commented on" possible_types Types::Post, Types::Image From 2a4f493e06d2cd58944f4d32a1fd6c0c61ad6301 Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Mon, 26 Aug 2024 09:30:43 +0200 Subject: [PATCH 17/22] Implemented review feedback --- lib/graphql/schema/member/base_dsl_methods.rb | 1 + spec/graphql/schema/object_spec.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/graphql/schema/member/base_dsl_methods.rb b/lib/graphql/schema/member/base_dsl_methods.rb index 12bcaf2af5..3a3911047d 100644 --- a/lib/graphql/schema/member/base_dsl_methods.rb +++ b/lib/graphql/schema/member/base_dsl_methods.rb @@ -70,6 +70,7 @@ module ConfigurationExtension def inherited(child_class) child_class.introspection(introspection) child_class.description(description) + child_class.comment(comment) child_class.default_graphql_name = nil if defined?(@graphql_name) && @graphql_name && (self.name.nil? || graphql_name != default_graphql_name) diff --git a/spec/graphql/schema/object_spec.rb b/spec/graphql/schema/object_spec.rb index 9ec280b0d9..b732a68c4b 100644 --- a/spec/graphql/schema/object_spec.rb +++ b/spec/graphql/schema/object_spec.rb @@ -375,7 +375,6 @@ def self.type_error(err, ctx) shape.delete(:@configs) shape.delete(:@future_schema) shape.delete(:@metadata) - shape.delete(:@comment) if type_defn_shapes.add?(shape) example_shapes_by_name[cls.graphql_name] = shape end From a0576b700c73dd5efe6c561e7264e1c7005ba8ac Mon Sep 17 00:00:00 2001 From: vaclavbohac Date: Mon, 26 Aug 2024 09:32:14 +0200 Subject: [PATCH 18/22] Revert typo --- lib/graphql/schema/interface.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/graphql/schema/interface.rb b/lib/graphql/schema/interface.rb index 42724771f9..bf04573333 100644 --- a/lib/graphql/schema/interface.rb +++ b/lib/graphql/schema/interface.rb @@ -22,7 +22,7 @@ module DefinitionMethods def definition_methods(&block) # Use an instance variable to tell whether it's been included previously or not; # You can't use constant detection because constants are brought into scope - # by `incllready happenude`, which has aed at this point. + # by `include`, which has already happened at this point. if !defined?(@_definition_methods) defn_methods_module = Module.new @_definition_methods = defn_methods_module From bce6080728f102db98f73eaae52865feb0acde25 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Wed, 28 Aug 2024 16:20:44 -0400 Subject: [PATCH 19/22] Add debugging info to shapes test --- spec/graphql/schema/object_spec.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/spec/graphql/schema/object_spec.rb b/spec/graphql/schema/object_spec.rb index b732a68c4b..c56a34cad2 100644 --- a/spec/graphql/schema/object_spec.rb +++ b/spec/graphql/schema/object_spec.rb @@ -396,15 +396,22 @@ def self.type_error(err, ctx) default_edge_shape = Class.new(GraphQL::Types::Relay::BaseEdge).instance_variables default_connection_shape = Class.new(GraphQL::Types::Relay::BaseConnection).instance_variables default_mutation_payload_shape = Class.new(GraphQL::Schema::RelayClassicMutation) { graphql_name("DoSomething") }.payload_type.instance_variables - expected_default_shapes = Set.new([ + expected_default_shapes = [ default_shape, default_shape_with_connection_type, default_edge_shape, default_connection_shape, default_mutation_payload_shape - ]) + ] - assert_equal expected_default_shapes, type_defn_shapes + type_defn_shapes_a = type_defn_shapes.to_a + assert type_defn_shapes_a.find { |sh| sh == default_shape }, "There's a match for default_shape" + assert type_defn_shapes_a.find { |sh| sh == default_shape_with_connection_type }, "There's a match for default_shape_with_connection_type" + assert type_defn_shapes_a.find { |sh| sh == default_edge_shape }, "There's a match for default_edge_shape" + assert type_defn_shapes_a.find { |sh| sh == default_connection_shape }, "There's a match for default_connection_shape" + assert type_defn_shapes_a.find { |sh| sh == default_mutation_payload_shape }, "There's a match for default_mutation_payload_shape" + + assert_equal [], type_defn_shapes_a - expected_default_shapes, "There aren't any other shape profiles" end describe "overriding wrap" do From 6aa0a94d884c1fac1bf283ee1d641bb830f648c3 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Wed, 28 Aug 2024 16:26:05 -0400 Subject: [PATCH 20/22] more debug info --- spec/graphql/schema/object_spec.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/graphql/schema/object_spec.rb b/spec/graphql/schema/object_spec.rb index c56a34cad2..14ce045ee9 100644 --- a/spec/graphql/schema/object_spec.rb +++ b/spec/graphql/schema/object_spec.rb @@ -411,7 +411,13 @@ def self.type_error(err, ctx) assert type_defn_shapes_a.find { |sh| sh == default_connection_shape }, "There's a match for default_connection_shape" assert type_defn_shapes_a.find { |sh| sh == default_mutation_payload_shape }, "There's a match for default_mutation_payload_shape" - assert_equal [], type_defn_shapes_a - expected_default_shapes, "There aren't any other shape profiles" + extra_shapes = type_defn_shapes_a - expected_default_shapes + extra_shapes_by_name = {} + extra_shapes.each do |shape| + name = example_shapes_by_name.key(shape) + extra_shapes_by_name[name] = shape + end + assert_equal({}, extra_shapes_by_name, "There aren't any extras shape profiles") end describe "overriding wrap" do From 2433e07741d18bb799eb19420629d641b3e71dc9 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Wed, 28 Aug 2024 16:28:12 -0400 Subject: [PATCH 21/22] Set comment for interfaces --- lib/graphql/schema/interface.rb | 1 + lib/graphql/schema/member/base_dsl_methods.rb | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/graphql/schema/interface.rb b/lib/graphql/schema/interface.rb index bf04573333..ada8e2428d 100644 --- a/lib/graphql/schema/interface.rb +++ b/lib/graphql/schema/interface.rb @@ -63,6 +63,7 @@ def included(child_class) child_class.introspection(introspection) child_class.description(description) + child_class.comment(nil) # If interfaces are mixed into each other, only define this class once if !child_class.const_defined?(:UnresolvedTypeError, false) add_unresolved_type_error(child_class) diff --git a/lib/graphql/schema/member/base_dsl_methods.rb b/lib/graphql/schema/member/base_dsl_methods.rb index 3a3911047d..ea197ff8dd 100644 --- a/lib/graphql/schema/member/base_dsl_methods.rb +++ b/lib/graphql/schema/member/base_dsl_methods.rb @@ -54,13 +54,13 @@ def description(new_description = nil) # call it without an argument to get the comment # @param new_comment [String] # @return [String, nil] - def comment(new_comment = nil) - if new_comment + def comment(new_comment = NOT_CONFIGURED) + if !NOT_CONFIGURED.equal?(new_comment) @comment = new_comment - elsif defined?(comment) + elsif defined?(@comment) @comment else - @comment = nil + nil end end @@ -70,7 +70,7 @@ module ConfigurationExtension def inherited(child_class) child_class.introspection(introspection) child_class.description(description) - child_class.comment(comment) + child_class.comment(nil) child_class.default_graphql_name = nil if defined?(@graphql_name) && @graphql_name && (self.name.nil? || graphql_name != default_graphql_name) From 0211989b9cc9502e4984ca09e06893db99f1dde4 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Wed, 28 Aug 2024 16:32:54 -0400 Subject: [PATCH 22/22] Add tests for comment inheritance and nil --- spec/graphql/schema/interface_spec.rb | 18 ++++++++++++++++++ spec/graphql/schema/object_spec.rb | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/spec/graphql/schema/interface_spec.rb b/spec/graphql/schema/interface_spec.rb index ef309f9a52..cbfa41c691 100644 --- a/spec/graphql/schema/interface_spec.rb +++ b/spec/graphql/schema/interface_spec.rb @@ -681,4 +681,22 @@ def pet(name:) end end end + + describe ".comment" do + it "isn't inherited" do + int1 = Module.new do + include GraphQL::Schema::Interface + graphql_name "Int1" + comment "TODO: fix this" + end + + int2 = Module.new do + include int1 + graphql_name "Int2" + end + + assert_equal "TODO: fix this", int1.comment + assert_nil int2.comment + end + end end diff --git a/spec/graphql/schema/object_spec.rb b/spec/graphql/schema/object_spec.rb index 14ce045ee9..cc7fd003fd 100644 --- a/spec/graphql/schema/object_spec.rb +++ b/spec/graphql/schema/object_spec.rb @@ -489,4 +489,22 @@ def self.wrap(obj, ctx) assert_equal expected_log, log end end + + describe ".comment" do + it "isn't inherited and can be set to nil" do + obj1 = Class.new(GraphQL::Schema::Object) do + graphql_name "Obj1" + comment "TODO: fix this" + end + + obj2 = Class.new(obj1) do + graphql_name("Obj2") + end + + assert_equal "TODO: fix this", obj1.comment + assert_nil obj2.comment + obj1.comment(nil) + assert_nil obj1.comment + end + end end