graphql 1.9.17 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/templates/schema.erb +7 -0
  3. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  4. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  5. data/lib/graphql/analysis/ast.rb +12 -11
  6. data/lib/graphql/argument.rb +7 -35
  7. data/lib/graphql/backtrace/table.rb +10 -2
  8. data/lib/graphql/base_type.rb +4 -0
  9. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  10. data/lib/graphql/define/assign_enum_value.rb +1 -1
  11. data/lib/graphql/define/assign_object_field.rb +3 -3
  12. data/lib/graphql/define/defined_object_proxy.rb +8 -2
  13. data/lib/graphql/define/instance_definable.rb +10 -106
  14. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  15. data/lib/graphql/directive.rb +4 -1
  16. data/lib/graphql/enum_type.rb +5 -71
  17. data/lib/graphql/execution/directive_checks.rb +2 -2
  18. data/lib/graphql/execution/errors.rb +2 -3
  19. data/lib/graphql/execution/execute.rb +1 -1
  20. data/lib/graphql/execution/interpreter/runtime.rb +106 -55
  21. data/lib/graphql/execution/interpreter.rb +5 -11
  22. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  23. data/lib/graphql/execution/lookahead.rb +5 -5
  24. data/lib/graphql/execution/multiplex.rb +13 -3
  25. data/lib/graphql/field.rb +9 -117
  26. data/lib/graphql/filter.rb +1 -1
  27. data/lib/graphql/function.rb +1 -30
  28. data/lib/graphql/input_object_type.rb +2 -24
  29. data/lib/graphql/interface_type.rb +2 -23
  30. data/lib/graphql/introspection/base_object.rb +2 -5
  31. data/lib/graphql/introspection/directive_type.rb +1 -1
  32. data/lib/graphql/introspection/entry_points.rb +7 -7
  33. data/lib/graphql/introspection/input_value_type.rb +27 -9
  34. data/lib/graphql/introspection/schema_type.rb +1 -6
  35. data/lib/graphql/introspection/type_type.rb +5 -5
  36. data/lib/graphql/language/definition_slice.rb +21 -10
  37. data/lib/graphql/language/document_from_schema_definition.rb +50 -44
  38. data/lib/graphql/language/nodes.rb +3 -3
  39. data/lib/graphql/language/parser.rb +644 -646
  40. data/lib/graphql/language/parser.y +6 -4
  41. data/lib/graphql/language.rb +1 -1
  42. data/lib/graphql/non_null_type.rb +0 -10
  43. data/lib/graphql/object_type.rb +1 -21
  44. data/lib/graphql/pagination/active_record_relation_connection.rb +35 -0
  45. data/lib/graphql/pagination/array_connection.rb +77 -0
  46. data/lib/graphql/pagination/connection.rb +171 -0
  47. data/lib/graphql/pagination/connections.rb +108 -0
  48. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  49. data/lib/graphql/pagination/relation_connection.rb +151 -0
  50. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  51. data/lib/graphql/pagination.rb +6 -0
  52. data/lib/graphql/query/arguments.rb +2 -1
  53. data/lib/graphql/query/context.rb +2 -5
  54. data/lib/graphql/query/literal_input.rb +30 -10
  55. data/lib/graphql/query/variable_validation_error.rb +1 -1
  56. data/lib/graphql/query/variables.rb +7 -3
  57. data/lib/graphql/query.rb +9 -5
  58. data/lib/graphql/relay/base_connection.rb +4 -0
  59. data/lib/graphql/relay/connection_type.rb +2 -1
  60. data/lib/graphql/relay/edge_type.rb +1 -0
  61. data/lib/graphql/relay/edges_instrumentation.rb +1 -1
  62. data/lib/graphql/relay/mutation.rb +1 -86
  63. data/lib/graphql/relay/node.rb +2 -2
  64. data/lib/graphql/scalar_type.rb +1 -58
  65. data/lib/graphql/schema/argument.rb +51 -6
  66. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  67. data/lib/graphql/schema/build_from_definition/resolve_map.rb +10 -4
  68. data/lib/graphql/schema/build_from_definition.rb +167 -178
  69. data/lib/graphql/schema/built_in_types.rb +5 -5
  70. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  71. data/lib/graphql/schema/directive.rb +28 -2
  72. data/lib/graphql/schema/enum.rb +40 -3
  73. data/lib/graphql/schema/enum_value.rb +5 -1
  74. data/lib/graphql/schema/field/connection_extension.rb +11 -1
  75. data/lib/graphql/schema/field.rb +59 -31
  76. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  77. data/lib/graphql/schema/finder.rb +13 -11
  78. data/lib/graphql/schema/input_object.rb +107 -2
  79. data/lib/graphql/schema/interface.rb +10 -7
  80. data/lib/graphql/schema/introspection_system.rb +108 -37
  81. data/lib/graphql/schema/late_bound_type.rb +1 -0
  82. data/lib/graphql/schema/list.rb +41 -0
  83. data/lib/graphql/schema/loader.rb +16 -4
  84. data/lib/graphql/schema/member/base_dsl_methods.rb +21 -11
  85. data/lib/graphql/schema/member/build_type.rb +5 -1
  86. data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
  87. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  88. data/lib/graphql/schema/member/has_ast_node.rb +17 -0
  89. data/lib/graphql/schema/member/has_fields.rb +4 -4
  90. data/lib/graphql/schema/member/validates_input.rb +33 -0
  91. data/lib/graphql/schema/member.rb +5 -0
  92. data/lib/graphql/schema/mutation.rb +1 -1
  93. data/lib/graphql/schema/non_null.rb +25 -0
  94. data/lib/graphql/schema/object.rb +15 -5
  95. data/lib/graphql/schema/printer.rb +1 -2
  96. data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
  97. data/lib/graphql/schema/resolver.rb +3 -15
  98. data/lib/graphql/schema/scalar.rb +19 -3
  99. data/lib/graphql/schema/subscription.rb +5 -5
  100. data/lib/graphql/schema/traversal.rb +1 -1
  101. data/lib/graphql/schema/type_expression.rb +21 -13
  102. data/lib/graphql/schema/type_membership.rb +2 -2
  103. data/lib/graphql/schema/union.rb +2 -3
  104. data/lib/graphql/schema/validation.rb +2 -2
  105. data/lib/graphql/schema/warden.rb +45 -20
  106. data/lib/graphql/schema.rb +764 -151
  107. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  108. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +9 -4
  109. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +10 -7
  110. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  111. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  112. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  113. data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
  114. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  115. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  116. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -3
  117. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
  118. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  119. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -1
  120. data/lib/graphql/static_validation/type_stack.rb +2 -2
  121. data/lib/graphql/static_validation/validator.rb +1 -1
  122. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -3
  123. data/lib/graphql/subscriptions/event.rb +7 -4
  124. data/lib/graphql/subscriptions/instrumentation.rb +10 -5
  125. data/lib/graphql/subscriptions/subscription_root.rb +0 -1
  126. data/lib/graphql/subscriptions.rb +34 -9
  127. data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
  128. data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
  129. data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
  130. data/lib/graphql/tracing/new_relic_tracing.rb +8 -0
  131. data/lib/graphql/tracing/platform_tracing.rb +26 -6
  132. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  133. data/lib/graphql/tracing/scout_tracing.rb +8 -0
  134. data/lib/graphql/tracing/skylight_tracing.rb +8 -0
  135. data/lib/graphql/tracing.rb +7 -3
  136. data/lib/graphql/types/int.rb +1 -1
  137. data/lib/graphql/types/relay/base_connection.rb +3 -1
  138. data/lib/graphql/union_type.rb +13 -28
  139. data/lib/graphql/unresolved_type_error.rb +2 -2
  140. data/lib/graphql/version.rb +1 -1
  141. data/lib/graphql.rb +2 -1
  142. metadata +15 -4
@@ -2,44 +2,46 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class IntrospectionSystem
5
- attr_reader :schema_type, :type_type, :typename_field
5
+ attr_reader :types, :possible_types
6
6
 
7
7
  def initialize(schema)
8
8
  @schema = schema
9
+ @class_based = !!@schema.is_a?(Class)
9
10
  @built_in_namespace = GraphQL::Introspection
10
- @custom_namespace = schema.introspection_namespace || @built_in_namespace
11
-
12
- # Use to-graphql to avoid sharing with any previous instantiations
13
- @schema_type = load_constant(:SchemaType).to_graphql
14
- @type_type = load_constant(:TypeType).to_graphql
15
- @field_type = load_constant(:FieldType).to_graphql
16
- @directive_type = load_constant(:DirectiveType).to_graphql
17
- @enum_value_type = load_constant(:EnumValueType).to_graphql
18
- @input_value_type = load_constant(:InputValueType).to_graphql
19
- @type_kind_enum = load_constant(:TypeKindEnum).to_graphql
20
- @directive_location_enum = load_constant(:DirectiveLocationEnum).to_graphql
11
+ @custom_namespace = if @class_based
12
+ schema.introspection || @built_in_namespace
13
+ else
14
+ schema.introspection_namespace || @built_in_namespace
15
+ end
16
+
17
+ type_defns = [
18
+ load_constant(:SchemaType),
19
+ load_constant(:TypeType),
20
+ load_constant(:FieldType),
21
+ load_constant(:DirectiveType),
22
+ load_constant(:EnumValueType),
23
+ load_constant(:InputValueType),
24
+ load_constant(:TypeKindEnum),
25
+ load_constant(:DirectiveLocationEnum)
26
+ ]
27
+ @types = {}
28
+ @possible_types = {}
29
+ type_defns.each do |t|
30
+ @types[t.graphql_name] = t
31
+ @possible_types[t.graphql_name] = [t]
32
+ end
21
33
  @entry_point_fields =
22
- if schema.disable_introspection_entry_points
34
+ if schema.disable_introspection_entry_points?
23
35
  {}
24
36
  else
25
- get_fields_from_class(class_sym: :EntryPoints)
37
+ entry_point_fields = get_fields_from_class(class_sym: :EntryPoints)
38
+ entry_point_fields.delete('__schema') if schema.disable_schema_introspection_entry_point?
39
+ entry_point_fields.delete('__type') if schema.disable_type_introspection_entry_point?
40
+ entry_point_fields
26
41
  end
27
42
  @dynamic_fields = get_fields_from_class(class_sym: :DynamicFields)
28
43
  end
29
44
 
30
- def object_types
31
- [
32
- @schema_type,
33
- @type_type,
34
- @field_type,
35
- @directive_type,
36
- @enum_value_type,
37
- @input_value_type,
38
- @type_kind_enum,
39
- @directive_location_enum,
40
- ]
41
- end
42
-
43
45
  def entry_points
44
46
  @entry_point_fields.values
45
47
  end
@@ -56,25 +58,94 @@ module GraphQL
56
58
  @dynamic_fields[name]
57
59
  end
58
60
 
61
+ # The introspection system is prepared with a bunch of LateBoundTypes.
62
+ # Replace those with the objects that they refer to, since LateBoundTypes
63
+ # aren't handled at runtime.
64
+ #
65
+ # @api private
66
+ # @return void
67
+ def resolve_late_bindings
68
+ @types.each do |name, t|
69
+ if t.kind.fields?
70
+ t.fields.each do |_name, field_defn|
71
+ field_defn.type = resolve_late_binding(field_defn.type)
72
+ end
73
+ end
74
+ end
75
+
76
+ @entry_point_fields.each do |name, f|
77
+ f.type = resolve_late_binding(f.type)
78
+ end
79
+
80
+ @dynamic_fields.each do |name, f|
81
+ f.type = resolve_late_binding(f.type)
82
+ end
83
+ nil
84
+ end
85
+
59
86
  private
60
87
 
88
+ def resolve_late_binding(late_bound_type)
89
+ case late_bound_type
90
+ when GraphQL::Schema::LateBoundType
91
+ @schema.get_type(late_bound_type.name)
92
+ when GraphQL::Schema::List, GraphQL::ListType
93
+ resolve_late_binding(late_bound_type.of_type).to_list_type
94
+ when GraphQL::Schema::NonNull, GraphQL::NonNullType
95
+ resolve_late_binding(late_bound_type.of_type).to_non_null_type
96
+ when Module
97
+ # It's a normal type -- no change required
98
+ late_bound_type
99
+ else
100
+ raise "Invariant: unexpected type: #{late_bound_type} (#{late_bound_type.class})"
101
+ end
102
+ end
103
+
61
104
  def load_constant(class_name)
62
- @custom_namespace.const_get(class_name)
105
+ const = @custom_namespace.const_get(class_name)
106
+ if @class_based
107
+ dup_type_class(const)
108
+ else
109
+ # Use `.to_graphql` to get a freshly-made version, not shared between schemas
110
+ const.to_graphql
111
+ end
63
112
  rescue NameError
64
113
  # Dup the built-in so that the cached fields aren't shared
65
- @built_in_namespace.const_get(class_name)
114
+ dup_type_class(@built_in_namespace.const_get(class_name))
66
115
  end
67
116
 
68
117
  def get_fields_from_class(class_sym:)
69
- object_class = load_constant(class_sym)
70
- object_type_defn = object_class.to_graphql
71
- extracted_field_defns = {}
72
- object_type_defn.all_fields.each do |field_defn|
73
- inner_resolve = field_defn.resolve_proc
74
- resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
75
- extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
118
+ object_type_defn = load_constant(class_sym)
119
+
120
+ if object_type_defn.is_a?(Module)
121
+ object_type_defn.fields
122
+ else
123
+ extracted_field_defns = {}
124
+ object_class = object_type_defn.metadata[:type_class]
125
+ object_type_defn.all_fields.each do |field_defn|
126
+ inner_resolve = field_defn.resolve_proc
127
+ resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
128
+ extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
129
+ end
130
+ extracted_field_defns
131
+ end
132
+ end
133
+
134
+ # This is probably not 100% robust -- but it has to be good enough to avoid modifying the built-in introspection types
135
+ def dup_type_class(type_class)
136
+ type_name = type_class.graphql_name
137
+ Class.new(type_class) do
138
+ # This won't be inherited like other things will
139
+ graphql_name(type_name)
140
+
141
+ if type_class.kind.fields?
142
+ type_class.fields.each do |_name, field_defn|
143
+ dup_field = field_defn.dup
144
+ dup_field.owner = self
145
+ add_field(dup_field)
146
+ end
147
+ end
76
148
  end
77
- extracted_field_defns
78
149
  end
79
150
 
80
151
  class PerFieldProxyResolve
@@ -6,6 +6,7 @@ module GraphQL
6
6
  # @api Private
7
7
  class LateBoundType
8
8
  attr_reader :name
9
+ alias :graphql_name :name
9
10
  def initialize(local_name)
10
11
  @name = local_name
11
12
  end
@@ -6,6 +6,8 @@ module GraphQL
6
6
  # Wraps a {Schema::Member} as a list type.
7
7
  # @see {Schema::Member::TypeSystemHelpers#to_list_type}
8
8
  class List < GraphQL::Schema::Wrapper
9
+ include Schema::Member::ValidatesInput
10
+
9
11
  def to_graphql
10
12
  @of_type.graphql_definition.to_list_type
11
13
  end
@@ -23,6 +25,45 @@ module GraphQL
23
25
  def to_type_signature
24
26
  "[#{@of_type.to_type_signature}]"
25
27
  end
28
+
29
+ # This is for introspection, where it's expected the name will be `null`
30
+ def graphql_name
31
+ nil
32
+ end
33
+
34
+ def coerce_result(value, ctx)
35
+ value.map { |i| i.nil? ? nil : of_type.coerce_result(i, ctx) }
36
+ end
37
+
38
+ def coerce_input(value, ctx)
39
+ if value.nil?
40
+ nil
41
+ else
42
+ ensure_array(value).map { |item| item.nil? ? item : of_type.coerce_input(item, ctx) }
43
+ end
44
+ end
45
+
46
+ def validate_non_null_input(value, ctx)
47
+ result = GraphQL::Query::InputValidationResult.new
48
+ ensure_array(value).each_with_index do |item, index|
49
+ item_result = of_type.validate_input(item, ctx)
50
+ if !item_result.valid?
51
+ result.merge_result!(index, item_result)
52
+ end
53
+ end
54
+ result
55
+ end
56
+
57
+ private
58
+
59
+ def ensure_array(value)
60
+ # `Array({ a: 1 })` makes `[[:a, 1]]`, so do it manually
61
+ if value.is_a?(Array)
62
+ value
63
+ else
64
+ [value]
65
+ end
66
+ end
26
67
  end
27
68
  end
28
69
  end
@@ -45,7 +45,13 @@ module GraphQL
45
45
  def resolve_type(types, type)
46
46
  case kind = type.fetch("kind")
47
47
  when "ENUM", "INTERFACE", "INPUT_OBJECT", "OBJECT", "SCALAR", "UNION"
48
- types.fetch(type.fetch("name"))
48
+ type_name = type.fetch("name")
49
+ type = types[type_name] || Schema::BUILT_IN_TYPES[type_name]
50
+ if type.nil?
51
+ raise "Type not found: #{type_name.inspect} among #{types.keys.sort}"
52
+ else
53
+ type.graphql_definition
54
+ end
49
55
  when "LIST"
50
56
  ListType.new(of_type: resolve_type(types, type.fetch("ofType")))
51
57
  when "NON_NULL"
@@ -118,14 +124,20 @@ module GraphQL
118
124
  }]
119
125
  )
120
126
  when "FIELD"
121
- GraphQL::Field.define(
127
+ defns = {
122
128
  name: type["name"],
123
129
  type: type_resolver.call(type["type"]),
124
130
  description: type["description"],
125
- arguments: Hash[type["args"].map { |arg|
131
+ }
132
+
133
+ # Avoid passing an empty hash, which warns on Ruby 2.7
134
+ if type["args"].any?
135
+ defns[:arguments] = Hash[type["args"].map { |arg|
126
136
  [arg["name"], define_type(arg.merge("kind" => "ARGUMENT"), type_resolver)]
127
137
  }]
128
- )
138
+ end
139
+
140
+ GraphQL::Field.define(**defns)
129
141
  when "ARGUMENT"
130
142
  kwargs = {}
131
143
  if type["defaultValue"]
@@ -18,16 +18,17 @@ module GraphQL
18
18
  # @param new_name [String]
19
19
  # @return [String]
20
20
  def graphql_name(new_name = nil)
21
- case
22
- when new_name
21
+ if new_name
23
22
  @graphql_name = new_name
24
- when overridden = overridden_graphql_name
25
- overridden
26
23
  else
27
- default_graphql_name
24
+ overridden_graphql_name || default_graphql_name
28
25
  end
29
26
  end
30
27
 
28
+ def overridden_graphql_name
29
+ @graphql_name
30
+ end
31
+
31
32
  # Just a convenience method to point out that people should use graphql_name instead
32
33
  def name(new_name = nil)
33
34
  return super() if new_name.nil?
@@ -46,7 +47,20 @@ module GraphQL
46
47
  if new_description
47
48
  @description = new_description
48
49
  else
49
- @description || find_inherited_value(:description)
50
+ @description
51
+ end
52
+ end
53
+
54
+ # This pushes some configurations _down_ the inheritance tree,
55
+ # in order to prevent repetitive lookups at runtime.
56
+ module ConfigurationExtension
57
+ def inherited(child_class)
58
+ child_class.introspection(introspection)
59
+ child_class.description(description)
60
+ if overridden_graphql_name
61
+ child_class.graphql_name(overridden_graphql_name)
62
+ end
63
+ super
50
64
  end
51
65
  end
52
66
 
@@ -55,7 +69,7 @@ module GraphQL
55
69
  if !new_introspection.nil?
56
70
  @introspection = new_introspection
57
71
  else
58
- @introspection || find_inherited_value(:introspection, false)
72
+ @introspection
59
73
  end
60
74
  end
61
75
 
@@ -79,10 +93,6 @@ module GraphQL
79
93
 
80
94
  alias :unwrap :itself
81
95
 
82
- def overridden_graphql_name
83
- @graphql_name || find_inherited_value(:overridden_graphql_name)
84
- end
85
-
86
96
  # Creates the default name for a schema member.
87
97
  # The default name is the Ruby constant name,
88
98
  # without any namespaces and with any `-Type` suffix removed
@@ -64,6 +64,8 @@ module GraphQL
64
64
  else
65
65
  raise ArgumentError, LIST_TYPE_ERROR
66
66
  end
67
+ when GraphQL::Schema::NonNull, GraphQL::Schema::List
68
+ type_expr
67
69
  when Module
68
70
  # This is a way to check that it's the right kind of module:
69
71
  if type_expr.respond_to?(:graphql_definition)
@@ -72,12 +74,14 @@ module GraphQL
72
74
  # Eg `String` => GraphQL::STRING_TYPE
73
75
  parse_type(type_expr.name, null: true)
74
76
  end
77
+ when Proc
78
+ parse_type(type_expr.call, null: true)
75
79
  when false
76
80
  raise ArgumentError, "Received `false` instead of a type, maybe a `!` should be replaced with `null: true` (for fields) or `required: true` (for arguments)"
77
81
  end
78
82
 
79
83
  if return_type.nil?
80
- raise "Unexpected type input: #{type_expr} (#{type_expr.class})"
84
+ raise "Unexpected type input: #{type_expr.inspect} (#{type_expr.class})"
81
85
  end
82
86
 
83
87
  # Apply list_type first, that way the
@@ -15,6 +15,11 @@ module GraphQL
15
15
  @graphql_definition ||= to_graphql
16
16
  end
17
17
 
18
+ # This is for a common interface with .define-based types
19
+ def type_class
20
+ self
21
+ end
22
+
18
23
  # Wipe out the cached graphql_definition so that `.to_graphql` will be called again.
19
24
  def initialize_copy(original)
20
25
  super
@@ -97,7 +97,7 @@ module GraphQL
97
97
  # (Don't want to allow arbitrary access to objects this way)
98
98
  resolved_application_object_type = context.schema.resolve_type(lookup_as_type, application_object, context)
99
99
  context.schema.after_lazy(resolved_application_object_type) do |application_object_type|
100
- possible_object_types = context.schema.possible_types(lookup_as_type)
100
+ possible_object_types = context.warden.possible_types(lookup_as_type)
101
101
  if !possible_object_types.include?(application_object_type)
102
102
  err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
103
103
  load_application_object_failed(err)
@@ -105,7 +105,7 @@ module GraphQL
105
105
  # This object was loaded successfully
106
106
  # and resolved to the right type,
107
107
  # now apply the `.authorized?` class method if there is one
108
- if (class_based_type = application_object_type.metadata[:type_class])
108
+ if (class_based_type = application_object_type.type_class)
109
109
  context.schema.after_lazy(class_based_type.authorized?(application_object, context)) do |authed|
110
110
  if authed
111
111
  application_object
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Member
5
+ module HasAstNode
6
+ # If this schema was parsed from a `.graphql` file (or other SDL),
7
+ # this is the AST node that defined this part of the schema.
8
+ def ast_node(new_ast_node = nil)
9
+ if new_ast_node
10
+ @ast_node = new_ast_node
11
+ end
12
+ @ast_node
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -7,11 +7,11 @@ module GraphQL
7
7
  module HasFields
8
8
  # Add a field to this object or interface with the given definition
9
9
  # @see {GraphQL::Schema::Field#initialize} for method signature
10
- # @return [void]
10
+ # @return [GraphQL::Schema::Field]
11
11
  def field(*args, **kwargs, &block)
12
12
  field_defn = field_class.from_options(*args, owner: self, **kwargs, &block)
13
13
  add_field(field_defn)
14
- nil
14
+ field_defn
15
15
  end
16
16
 
17
17
  # @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
@@ -59,8 +59,8 @@ module GraphQL
59
59
  # @param field_defn [GraphQL::Schema::Field]
60
60
  # @return [void]
61
61
  def add_field(field_defn)
62
- if CONFLICT_FIELD_NAMES.include?(field_defn.original_name) && field_defn.original_name == field_defn.resolver_method
63
- warn "#{self.graphql_name}'s `field :#{field_defn.original_name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.original_name}` and `def resolve_#{field_defn.original_name}`)"
62
+ if CONFLICT_FIELD_NAMES.include?(field_defn.resolver_method) && field_defn.original_name == field_defn.resolver_method && field_defn.method_conflict_warning?
63
+ warn "#{self.graphql_name}'s `field :#{field_defn.name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.resolver_method}` and `def resolve_#{field_defn.resolver_method}`). Or use `method_conflict_warning: false` to suppress this warning."
64
64
  end
65
65
  own_fields[field_defn.name] = field_defn
66
66
  nil
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ module ValidatesInput
7
+ def valid_input?(val, ctx)
8
+ validate_input(val, ctx).valid?
9
+ end
10
+
11
+ def validate_input(val, ctx)
12
+ if val.nil?
13
+ GraphQL::Query::InputValidationResult.new
14
+ else
15
+ validate_non_null_input(val, ctx)
16
+ end
17
+ end
18
+
19
+ def valid_isolated_input?(v)
20
+ valid_input?(v, GraphQL::Query::NullContext)
21
+ end
22
+
23
+ def coerce_isolated_input(v)
24
+ coerce_input(v, GraphQL::Query::NullContext)
25
+ end
26
+
27
+ def coerce_isolated_result(v)
28
+ coerce_result(v, GraphQL::Query::NullContext)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -3,10 +3,12 @@ require 'graphql/schema/member/accepts_definition'
3
3
  require 'graphql/schema/member/base_dsl_methods'
4
4
  require 'graphql/schema/member/cached_graphql_definition'
5
5
  require 'graphql/schema/member/graphql_type_names'
6
+ require 'graphql/schema/member/has_ast_node'
6
7
  require 'graphql/schema/member/has_path'
7
8
  require 'graphql/schema/member/relay_shortcuts'
8
9
  require 'graphql/schema/member/scoped'
9
10
  require 'graphql/schema/member/type_system_helpers'
11
+ require 'graphql/schema/member/validates_input'
10
12
  require "graphql/relay/type_extensions"
11
13
 
12
14
  module GraphQL
@@ -20,10 +22,13 @@ module GraphQL
20
22
  extend CachedGraphQLDefinition
21
23
  extend GraphQL::Relay::TypeExtensions
22
24
  extend BaseDSLMethods
25
+ extend BaseDSLMethods::ConfigurationExtension
26
+ introspection(false)
23
27
  extend TypeSystemHelpers
24
28
  extend Scoped
25
29
  extend RelayShortcuts
26
30
  extend HasPath
31
+ extend HasAstNode
27
32
  end
28
33
  end
29
34
  end
@@ -64,7 +64,7 @@ module GraphQL
64
64
 
65
65
  class << self
66
66
  # Override this method to handle legacy-style usages of `MyMutation.field`
67
- def field(*args, &block)
67
+ def field(*args, **kwargs, &block)
68
68
  if args.empty?
69
69
  raise ArgumentError, "#{name}.field is used for adding fields to this mutation. Use `mutation: #{name}` to attach this mutation instead."
70
70
  else
@@ -6,6 +6,8 @@ module GraphQL
6
6
  # Wraps a {Schema::Member} when it is required.
7
7
  # @see {Schema::Member::TypeSystemHelpers#to_non_null_type}
8
8
  class NonNull < GraphQL::Schema::Wrapper
9
+ include Schema::Member::ValidatesInput
10
+
9
11
  def to_graphql
10
12
  @of_type.graphql_definition.to_non_null_type
11
13
  end
@@ -32,6 +34,29 @@ module GraphQL
32
34
  def inspect
33
35
  "#<#{self.class.name} @of_type=#{@of_type.inspect}>"
34
36
  end
37
+
38
+ def validate_input(value, ctx)
39
+ if value.nil?
40
+ result = GraphQL::Query::InputValidationResult.new
41
+ result.add_problem("Expected value to not be null")
42
+ result
43
+ else
44
+ of_type.validate_input(value, ctx)
45
+ end
46
+ end
47
+
48
+ # This is for introspection, where it's expected the name will be `null`
49
+ def graphql_name
50
+ nil
51
+ end
52
+
53
+ def coerce_input(value, ctx)
54
+ of_type.coerce_input(value, ctx)
55
+ end
56
+
57
+ def coerce_result(value, ctx)
58
+ of_type.coerce_result(value, ctx)
59
+ end
35
60
  end
36
61
  end
37
62
  end
@@ -57,13 +57,9 @@ module GraphQL
57
57
  else
58
58
  nil
59
59
  end
60
- # rescue GraphQL::ExecutionError => err
61
- # err
62
60
  end
63
61
  end
64
62
  end
65
- # rescue GraphQL::ExecutionError => err
66
- # err
67
63
  end
68
64
  end
69
65
 
@@ -86,11 +82,24 @@ module GraphQL
86
82
  include(int)
87
83
  end
88
84
  end
85
+ # Remove any interfaces which are being replaced (late-bound types are updated in place this way)
86
+ own_interfaces.reject! { |i|
87
+ new_interfaces.any? { |new_i|
88
+ new_name = new_i.is_a?(String) ? new_i : new_i.graphql_name
89
+ old_name = i.is_a?(String) ? i : i.graphql_name
90
+ new_name == old_name
91
+ }
92
+ }
89
93
  own_interfaces.concat(new_interfaces)
90
94
  end
91
95
 
92
96
  def interfaces
93
- own_interfaces + (superclass <= GraphQL::Schema::Object ? superclass.interfaces : [])
97
+ inherited_interfaces = (superclass.respond_to?(:interfaces) ? superclass.interfaces : nil)
98
+ if inherited_interfaces && !inherited_interfaces.empty?
99
+ own_interfaces + inherited_interfaces
100
+ else
101
+ own_interfaces
102
+ end
94
103
  end
95
104
 
96
105
  def own_interfaces
@@ -120,6 +129,7 @@ module GraphQL
120
129
  obj_type.interfaces = interfaces
121
130
  obj_type.introspection = introspection
122
131
  obj_type.mutation = mutation
132
+ obj_type.ast_node = ast_node
123
133
  fields.each do |field_name, field_inst|
124
134
  field_defn = field_inst.to_graphql
125
135
  obj_type.fields[field_defn.name] = field_defn
@@ -54,7 +54,6 @@ module GraphQL
54
54
  )
55
55
 
56
56
  @document = @document_from_schema.document
57
-
58
57
  @schema = schema
59
58
  end
60
59
 
@@ -99,7 +98,7 @@ module GraphQL
99
98
  if directive.name == "deprecated"
100
99
  reason = directive.arguments.find { |arg| arg.name == "reason" }
101
100
 
102
- if reason.value == GraphQL::Directive::DEFAULT_DEPRECATION_REASON
101
+ if reason.value == GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON
103
102
  "@deprecated"
104
103
  else
105
104
  "@deprecated(reason: #{reason.value.to_s.inspect})"
@@ -61,7 +61,7 @@ module GraphQL
61
61
  end
62
62
 
63
63
  return_value = if input_kwargs.any?
64
- super(input_kwargs)
64
+ super(**input_kwargs)
65
65
  else
66
66
  super()
67
67
  end
@@ -76,7 +76,7 @@ module GraphQL
76
76
  context.schema.after_lazy(load_arguments_val) do |loaded_args|
77
77
  # Then call `authorized?`, which may raise or may return a lazy object
78
78
  authorized_val = if loaded_args.any?
79
- authorized?(loaded_args)
79
+ authorized?(**loaded_args)
80
80
  else
81
81
  authorized?
82
82
  end
@@ -135,20 +135,8 @@ module GraphQL
135
135
  def authorized?(**inputs)
136
136
  self.class.arguments.each_value do |argument|
137
137
  arg_keyword = argument.keyword
138
- if inputs.key?(arg_keyword) && !(value = inputs[arg_keyword]).nil? && (value != argument.default_value)
139
- loads_type = @arguments_loads_as_type[arg_keyword]
140
- # If this argument resulted in an object being loaded,
141
- # then authorize this loaded object with its own policy.
142
- #
143
- # But if this argument was "just" a plain argument, like
144
- # a boolean, then authorize it based on the mutation.
145
- authorization_value = if loads_type
146
- value
147
- else
148
- self
149
- end
150
-
151
- arg_auth, err = argument.authorized?(authorization_value, context)
138
+ if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
139
+ arg_auth, err = argument.authorized?(self, arg_value, context)
152
140
  if !arg_auth
153
141
  return arg_auth, err
154
142
  else