graphql 1.13.3 → 1.13.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +0 -7
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
  8. data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
  9. data/lib/generators/graphql/install_generator.rb +1 -1
  10. data/lib/generators/graphql/interface_generator.rb +7 -7
  11. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  12. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  13. data/lib/generators/graphql/mutation_generator.rb +5 -30
  14. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  15. data/lib/generators/graphql/object_generator.rb +8 -37
  16. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  17. data/lib/generators/graphql/scalar_generator.rb +4 -2
  18. data/lib/generators/graphql/templates/enum.erb +5 -1
  19. data/lib/generators/graphql/templates/input.erb +9 -0
  20. data/lib/generators/graphql/templates/interface.erb +4 -2
  21. data/lib/generators/graphql/templates/mutation.erb +1 -1
  22. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  23. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  24. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  25. data/lib/generators/graphql/templates/object.erb +4 -2
  26. data/lib/generators/graphql/templates/scalar.erb +3 -1
  27. data/lib/generators/graphql/templates/union.erb +4 -2
  28. data/lib/generators/graphql/type_generator.rb +46 -9
  29. data/lib/generators/graphql/union_generator.rb +5 -5
  30. data/lib/graphql/analysis/ast/visitor.rb +2 -1
  31. data/lib/graphql/dataloader/source.rb +2 -2
  32. data/lib/graphql/date_encoding_error.rb +16 -0
  33. data/lib/graphql/execution/interpreter/arguments_cache.rb +4 -2
  34. data/lib/graphql/execution/interpreter/runtime.rb +33 -17
  35. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  36. data/lib/graphql/introspection/directive_type.rb +2 -0
  37. data/lib/graphql/introspection/schema_type.rb +5 -0
  38. data/lib/graphql/introspection/type_type.rb +9 -3
  39. data/lib/graphql/introspection.rb +3 -0
  40. data/lib/graphql/language/document_from_schema_definition.rb +1 -0
  41. data/lib/graphql/language/lexer.rb +50 -25
  42. data/lib/graphql/language/lexer.rl +2 -0
  43. data/lib/graphql/language/nodes.rb +1 -1
  44. data/lib/graphql/language/parser.rb +829 -816
  45. data/lib/graphql/language/parser.y +8 -2
  46. data/lib/graphql/language/printer.rb +4 -0
  47. data/lib/graphql/pagination/relation_connection.rb +8 -5
  48. data/lib/graphql/rubocop/graphql/default_required_true.rb +4 -4
  49. data/lib/graphql/schema/argument.rb +18 -1
  50. data/lib/graphql/schema/build_from_definition.rb +1 -0
  51. data/lib/graphql/schema/directive.rb +15 -0
  52. data/lib/graphql/schema/field.rb +13 -7
  53. data/lib/graphql/schema/loader.rb +3 -0
  54. data/lib/graphql/schema/scalar.rb +12 -0
  55. data/lib/graphql/schema.rb +12 -5
  56. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  57. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  58. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  59. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  60. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  61. data/lib/graphql/static_validation/validation_context.rb +4 -0
  62. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  63. data/lib/graphql/tracing/data_dog_tracing.rb +6 -1
  64. data/lib/graphql/tracing/platform_tracing.rb +11 -6
  65. data/lib/graphql/types/iso_8601_date.rb +13 -5
  66. data/lib/graphql/types/iso_8601_date_time.rb +8 -1
  67. data/lib/graphql/types/relay/connection_behaviors.rb +2 -1
  68. data/lib/graphql/types/relay/node_field.rb +0 -12
  69. data/lib/graphql/types/relay/nodes_field.rb +6 -0
  70. data/lib/graphql/version.rb +1 -1
  71. data/lib/graphql.rb +12 -0
  72. metadata +20 -8
@@ -8,13 +8,28 @@ require_relative 'core'
8
8
 
9
9
  module Graphql
10
10
  module Generators
11
- class TypeGeneratorBase < Rails::Generators::Base
11
+ class TypeGeneratorBase < Rails::Generators::NamedBase
12
12
  include Core
13
13
 
14
- argument :type_name,
15
- type: :string,
16
- banner: "TypeName",
17
- desc: "Name of this object type (expressed as Ruby or GraphQL)"
14
+ class_option 'namespaced_types',
15
+ type: :boolean,
16
+ required: false,
17
+ default: false,
18
+ banner: "Namespaced",
19
+ desc: "If the generated types will be namespaced"
20
+
21
+ argument :custom_fields,
22
+ type: :array,
23
+ default: [],
24
+ banner: "name:type name:type ...",
25
+ desc: "Fields for this object (type may be expressed as Ruby or GraphQL)"
26
+
27
+
28
+ attr_accessor :graphql_type
29
+
30
+ def create_type_file
31
+ template "#{graphql_type}.erb", "#{options[:directory]}/types#{subdirectory}/#{type_file_name}.rb"
32
+ end
18
33
 
19
34
  # Take a type expression in any combination of GraphQL or Ruby styles
20
35
  # and return it in a specified output style
@@ -60,12 +75,12 @@ module Graphql
60
75
 
61
76
  # @return [String] The user-provided type name, normalized to Ruby code
62
77
  def type_ruby_name
63
- @type_ruby_name ||= self.class.normalize_type_expression(type_name, mode: :ruby)[0]
78
+ @type_ruby_name ||= self.class.normalize_type_expression(name, mode: :ruby)[0]
64
79
  end
65
80
 
66
81
  # @return [String] The user-provided type name, as a GraphQL name
67
82
  def type_graphql_name
68
- @type_graphql_name ||= self.class.normalize_type_expression(type_name, mode: :graphql)[0]
83
+ @type_graphql_name ||= self.class.normalize_type_expression(name, mode: :graphql)[0]
69
84
  end
70
85
 
71
86
  # @return [String] The user-provided type name, as a file name (without extension)
@@ -82,6 +97,24 @@ module Graphql
82
97
  }
83
98
  end
84
99
 
100
+ def ruby_class_name
101
+ class_prefix =
102
+ if options[:namespaced_types]
103
+ "#{graphql_type.pluralize.camelize}::"
104
+ else
105
+ ""
106
+ end
107
+ @ruby_class_name || class_prefix + type_ruby_name.sub(/^Types::/, "")
108
+ end
109
+
110
+ def subdirectory
111
+ if options[:namespaced_types]
112
+ "/#{graphql_type.pluralize}"
113
+ else
114
+ ""
115
+ end
116
+ end
117
+
85
118
  class NormalizedField
86
119
  def initialize(name, type_expr, null)
87
120
  @name = name
@@ -89,8 +122,12 @@ module Graphql
89
122
  @null = null
90
123
  end
91
124
 
92
- def to_ruby
93
- "field :#{@name}, #{@type_expr}, null: #{@null}"
125
+ def to_object_field
126
+ "field :#{@name}, #{@type_expr}#{@null ? '' : ', null: false'}"
127
+ end
128
+
129
+ def to_input_argument
130
+ "argument :#{@name}, #{@type_expr}, required: false"
94
131
  end
95
132
  end
96
133
  end
@@ -19,14 +19,14 @@ module Graphql
19
19
  banner: "type type ...",
20
20
  desc: "Possible types for this union (expressed as Ruby or GraphQL)"
21
21
 
22
- def create_type_file
23
- template "union.erb", "#{options[:directory]}/types/#{type_file_name}.rb"
24
- end
25
-
26
22
  private
27
23
 
24
+ def graphql_type
25
+ "union"
26
+ end
27
+
28
28
  def normalized_possible_types
29
- possible_types.map { |t| self.class.normalize_type_expression(t, mode: :ruby)[0] }
29
+ custom_fields.map { |t| self.class.normalize_type_expression(t, mode: :ruby)[0] }
30
30
  end
31
31
  end
32
32
  end
@@ -100,7 +100,8 @@ module GraphQL
100
100
  def on_field(node, parent)
101
101
  @response_path.push(node.alias || node.name)
102
102
  parent_type = @object_types.last
103
- field_definition = @schema.get_field(parent_type, node.name, @query.context)
103
+ # This could be nil if the previous field wasn't found:
104
+ field_definition = parent_type && @schema.get_field(parent_type, node.name, @query.context)
104
105
  @field_definitions.push(field_definition)
105
106
  if !field_definition.nil?
106
107
  next_object_type = field_definition.type.unwrap
@@ -138,8 +138,8 @@ module GraphQL
138
138
  # @api private
139
139
  def result_for(key)
140
140
  if !@results.key?(key)
141
- raise <<-ERR
142
- Invariant: fetching result for a key on #{self.class} that hasn't been loaded yet (#{key.inspect}, loaded: #{@results.keys})
141
+ raise GraphQL::InvariantError, <<-ERR
142
+ Fetching result for a key on #{self.class} that hasn't been loaded yet (#{key.inspect}, loaded: #{@results.keys})
143
143
 
144
144
  This key should have been loaded already. This is a bug in GraphQL::Dataloader, please report it on GitHub: https://github.com/rmosolgo/graphql-ruby/issues/new.
145
145
  ERR
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ # This error is raised when `Types::ISO8601Date` is asked to return a value
4
+ # that cannot be parsed to a Ruby Date.
5
+ #
6
+ # @see GraphQL::Types::ISO8601Date which raises this error
7
+ class DateEncodingError < GraphQL::RuntimeTypeError
8
+ # The value which couldn't be encoded
9
+ attr_reader :date_value
10
+
11
+ def initialize(value)
12
+ @date_value = value
13
+ super("Date cannot be parsed: #{value}. \nDate must be be able to be parsed as a Ruby Date object.")
14
+ end
15
+ end
16
+ end
@@ -31,8 +31,10 @@ module GraphQL
31
31
  # If any jobs were enqueued, run them now,
32
32
  # since this might have been called outside of execution.
33
33
  # (The jobs are responsible for updating `result` in-place.)
34
- @dataloader.run_isolated do
35
- @storage[ast_node][argument_owner][parent_object]
34
+ if !@storage.key?(ast_node) || !@storage[ast_node].key?(argument_owner)
35
+ @dataloader.run_isolated do
36
+ @storage[ast_node][argument_owner][parent_object]
37
+ end
36
38
  end
37
39
  # Ack, the _hash_ is updated, but the key is eventually
38
40
  # overridden with an immutable arguments instance.
@@ -159,7 +159,8 @@ module GraphQL
159
159
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
160
160
  @runtime_directive_names = []
161
161
  noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
162
- schema.directives.each do |name, dir_defn|
162
+ @schema_directives = schema.directives
163
+ @schema_directives.each do |name, dir_defn|
163
164
  if dir_defn.method(:resolve).owner != noop_resolve_owner
164
165
  @runtime_directive_names << name
165
166
  end
@@ -206,7 +207,7 @@ module GraphQL
206
207
  # Root .authorized? returned false.
207
208
  @response = nil
208
209
  else
209
- resolve_with_directives(object_proxy, root_operation.directives) do # execute query level directives
210
+ call_method_on_directives(:resolve, object_proxy, root_operation.directives) do # execute query level directives
210
211
  gathered_selections = gather_selections(object_proxy, root_type, root_operation.selections)
211
212
  # This is kind of a hack -- `gathered_selections` is an Array if any of the selections
212
213
  # require isolation during execution (because of runtime directives). In that case,
@@ -226,7 +227,7 @@ module GraphQL
226
227
 
227
228
  @dataloader.append_job {
228
229
  set_all_interpreter_context(query.root_value, nil, nil, path)
229
- resolve_with_directives(object_proxy, selections.graphql_directives) do
230
+ call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
230
231
  evaluate_selections(
231
232
  path,
232
233
  context.scoped_context,
@@ -502,7 +503,7 @@ module GraphQL
502
503
  }
503
504
  end
504
505
 
505
- field_result = resolve_with_directives(object, directives) do
506
+ field_result = call_method_on_directives(:resolve, object, directives) do
506
507
  # Actually call the field resolver and capture the result
507
508
  app_result = begin
508
509
  query.with_error_handling do
@@ -734,7 +735,7 @@ module GraphQL
734
735
  final_result = nil
735
736
  end
736
737
  set_all_interpreter_context(continue_value, nil, nil, path) # reset this mutable state
737
- resolve_with_directives(continue_value, selections.graphql_directives) do
738
+ call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
738
739
  evaluate_selections(
739
740
  path,
740
741
  context.scoped_context,
@@ -753,6 +754,8 @@ module GraphQL
753
754
  end
754
755
  when "LIST"
755
756
  inner_type = current_type.of_type
757
+ # This is true for objects, unions, and interfaces
758
+ use_dataloader_job = !inner_type.unwrap.kind.input?
756
759
  response_list = GraphQLResultArray.new(result_name, selection_result)
757
760
  response_list.graphql_non_null_list_items = inner_type.non_null?
758
761
  set_result(selection_result, result_name, response_list)
@@ -767,12 +770,12 @@ module GraphQL
767
770
  this_idx = idx
768
771
  next_path.freeze
769
772
  idx += 1
770
- # This will update `response_list` with the lazy
771
- after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
772
- continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
773
- if HALT != continue_value
774
- continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
773
+ if use_dataloader_job
774
+ @dataloader.append_job do
775
+ resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
775
776
  end
777
+ else
778
+ resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
776
779
  end
777
780
  end
778
781
  rescue NoMethodError => err
@@ -792,17 +795,30 @@ module GraphQL
792
795
  end
793
796
  end
794
797
 
795
- def resolve_with_directives(object, directives, &block)
798
+ def resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
799
+ set_all_interpreter_context(nil, nil, nil, next_path)
800
+ call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
801
+ # This will update `response_list` with the lazy
802
+ after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
803
+ continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
804
+ if HALT != continue_value
805
+ continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
806
+ end
807
+ end
808
+ end
809
+ end
810
+
811
+ def call_method_on_directives(method_name, object, directives, &block)
796
812
  return yield if directives.nil? || directives.empty?
797
- run_directive(object, directives, 0, &block)
813
+ run_directive(method_name, object, directives, 0, &block)
798
814
  end
799
815
 
800
- def run_directive(object, directives, idx, &block)
816
+ def run_directive(method_name, object, directives, idx, &block)
801
817
  dir_node = directives[idx]
802
818
  if !dir_node
803
819
  yield
804
820
  else
805
- dir_defn = schema.directives.fetch(dir_node.name)
821
+ dir_defn = @schema_directives.fetch(dir_node.name)
806
822
  if !dir_defn.is_a?(Class)
807
823
  dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
808
824
  end
@@ -821,8 +837,8 @@ module GraphQL
821
837
  if dir_args == HALT
822
838
  nil
823
839
  else
824
- dir_defn.resolve(object, dir_args, context) do
825
- run_directive(object, directives, idx + 1, &block)
840
+ dir_defn.public_send(method_name, object, dir_args, context) do
841
+ run_directive(method_name, object, directives, idx + 1, &block)
826
842
  end
827
843
  end
828
844
  end
@@ -831,7 +847,7 @@ module GraphQL
831
847
  # Check {Schema::Directive.include?} for each directive that's present
832
848
  def directives_include?(node, graphql_object, parent_type)
833
849
  node.directives.each do |dir_node|
834
- dir_defn = schema.directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
850
+ dir_defn = @schema_directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
835
851
  args = arguments(graphql_object, dir_defn, dir_node)
836
852
  if !dir_defn.include?(graphql_object, args, context)
837
853
  return false
@@ -6,8 +6,8 @@ module GraphQL
6
6
  description "A Directive can be adjacent to many parts of the GraphQL language, "\
7
7
  "a __DirectiveLocation describes one such possible adjacencies."
8
8
 
9
- GraphQL::Directive::LOCATIONS.each do |location|
10
- value(location.to_s, GraphQL::Directive::LOCATION_DESCRIPTIONS[location], value: location)
9
+ GraphQL::Schema::Directive::LOCATIONS.each do |location|
10
+ value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
11
11
  end
12
12
  introspection true
13
13
  end
@@ -19,6 +19,8 @@ module GraphQL
19
19
  field :on_fragment, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_fragment?
20
20
  field :on_field, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_field?
21
21
 
22
+ field :is_repeatable, Boolean, method: :repeatable?
23
+
22
24
  def args(include_deprecated:)
23
25
  args = @context.warden.arguments(@object)
24
26
  args = args.reject(&:deprecation_reason) unless include_deprecated
@@ -13,6 +13,11 @@ module GraphQL
13
13
  field :mutation_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server supports mutation, the type that mutation operations will be rooted at."
14
14
  field :subscription_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server support subscription, the type that subscription operations will be rooted at."
15
15
  field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false
16
+ field :description, String, resolver_method: :schema_description
17
+
18
+ def schema_description
19
+ context.schema.description
20
+ end
16
21
 
17
22
  def types
18
23
  @context.warden.reachable_types.sort_by(&:graphql_name)
@@ -12,7 +12,7 @@ module GraphQL
12
12
  "possible at runtime. List and NonNull types compose other types."
13
13
 
14
14
  field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false
15
- field :name, String
15
+ field :name, String, method: :graphql_name
16
16
  field :description, String
17
17
  field :fields, [GraphQL::Schema::LateBoundType.new("__Field")] do
18
18
  argument :include_deprecated, Boolean, required: false, default_value: false
@@ -27,8 +27,14 @@ module GraphQL
27
27
  end
28
28
  field :of_type, GraphQL::Schema::LateBoundType.new("__Type")
29
29
 
30
- def name
31
- object.graphql_name
30
+ field :specified_by_url, String
31
+
32
+ def specified_by_url
33
+ if object.kind.scalar?
34
+ object.specified_by_url
35
+ else
36
+ nil
37
+ end
32
38
  end
33
39
 
34
40
  def kind
@@ -7,6 +7,7 @@ module GraphQL
7
7
  <<-QUERY
8
8
  query IntrospectionQuery {
9
9
  __schema {
10
+ description
10
11
  queryType { name }
11
12
  mutationType { name }
12
13
  subscriptionType { name }
@@ -17,6 +18,7 @@ query IntrospectionQuery {
17
18
  name
18
19
  description
19
20
  locations
21
+ isRepeatable
20
22
  args#{include_deprecated_args ? '(includeDeprecated: true)' : ''} {
21
23
  ...InputValue
22
24
  }
@@ -27,6 +29,7 @@ fragment FullType on __Type {
27
29
  kind
28
30
  name
29
31
  description
32
+ specifiedByUrl
30
33
  fields(includeDeprecated: true) {
31
34
  name
32
35
  description
@@ -152,6 +152,7 @@ module GraphQL
152
152
  def build_directive_node(directive)
153
153
  GraphQL::Language::Nodes::DirectiveDefinition.new(
154
154
  name: directive.graphql_name,
155
+ repeatable: directive.repeatable?,
155
156
  arguments: build_argument_nodes(warden.arguments(directive)),
156
157
  locations: build_directive_location_nodes(directive.locations),
157
158
  description: directive.description,