graphql 1.13.5 → 1.13.9
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +0 -7
- data/lib/generators/graphql/enum_generator.rb +4 -10
- data/lib/generators/graphql/field_extractor.rb +31 -0
- data/lib/generators/graphql/input_generator.rb +50 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
- data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
- data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
- data/lib/generators/graphql/install_generator.rb +1 -1
- data/lib/generators/graphql/interface_generator.rb +7 -7
- data/lib/generators/graphql/mutation_create_generator.rb +22 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
- data/lib/generators/graphql/mutation_generator.rb +5 -30
- data/lib/generators/graphql/mutation_update_generator.rb +22 -0
- data/lib/generators/graphql/object_generator.rb +8 -37
- data/lib/generators/graphql/orm_mutations_base.rb +40 -0
- data/lib/generators/graphql/scalar_generator.rb +4 -2
- data/lib/generators/graphql/templates/enum.erb +5 -1
- data/lib/generators/graphql/templates/input.erb +9 -0
- data/lib/generators/graphql/templates/interface.erb +4 -2
- data/lib/generators/graphql/templates/mutation.erb +1 -1
- data/lib/generators/graphql/templates/mutation_create.erb +20 -0
- data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
- data/lib/generators/graphql/templates/mutation_update.erb +21 -0
- data/lib/generators/graphql/templates/object.erb +4 -2
- data/lib/generators/graphql/templates/scalar.erb +3 -1
- data/lib/generators/graphql/templates/union.erb +4 -2
- data/lib/generators/graphql/type_generator.rb +46 -9
- data/lib/generators/graphql/union_generator.rb +5 -5
- data/lib/graphql/analysis/ast/visitor.rb +2 -1
- data/lib/graphql/dataloader/source.rb +2 -2
- data/lib/graphql/date_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/runtime.rb +29 -14
- data/lib/graphql/introspection/directive_location_enum.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +2 -0
- data/lib/graphql/introspection/schema_type.rb +5 -0
- data/lib/graphql/introspection/type_type.rb +9 -3
- data/lib/graphql/introspection.rb +4 -1
- data/lib/graphql/pagination/relation_connection.rb +4 -4
- data/lib/graphql/rubocop/graphql/default_required_true.rb +4 -4
- data/lib/graphql/schema/argument.rb +18 -1
- data/lib/graphql/schema/directive.rb +11 -0
- data/lib/graphql/schema/field.rb +21 -12
- data/lib/graphql/schema/input_object.rb +1 -1
- data/lib/graphql/schema/loader.rb +3 -0
- data/lib/graphql/schema/scalar.rb +12 -0
- data/lib/graphql/schema.rb +12 -5
- data/lib/graphql/types/iso_8601_date.rb +13 -5
- data/lib/graphql/types/iso_8601_date_time.rb +8 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +2 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +12 -0
- 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::
|
11
|
+
class TypeGeneratorBase < Rails::Generators::NamedBase
|
12
12
|
include Core
|
13
13
|
|
14
|
-
|
15
|
-
type: :
|
16
|
-
|
17
|
-
|
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(
|
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(
|
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
|
93
|
-
"field :#{@name}, #{@type_expr}, 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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -207,7 +207,7 @@ module GraphQL
|
|
207
207
|
# Root .authorized? returned false.
|
208
208
|
@response = nil
|
209
209
|
else
|
210
|
-
|
210
|
+
call_method_on_directives(:resolve, object_proxy, root_operation.directives) do # execute query level directives
|
211
211
|
gathered_selections = gather_selections(object_proxy, root_type, root_operation.selections)
|
212
212
|
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
213
213
|
# require isolation during execution (because of runtime directives). In that case,
|
@@ -227,7 +227,7 @@ module GraphQL
|
|
227
227
|
|
228
228
|
@dataloader.append_job {
|
229
229
|
set_all_interpreter_context(query.root_value, nil, nil, path)
|
230
|
-
|
230
|
+
call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
|
231
231
|
evaluate_selections(
|
232
232
|
path,
|
233
233
|
context.scoped_context,
|
@@ -503,7 +503,7 @@ module GraphQL
|
|
503
503
|
}
|
504
504
|
end
|
505
505
|
|
506
|
-
field_result =
|
506
|
+
field_result = call_method_on_directives(:resolve, object, directives) do
|
507
507
|
# Actually call the field resolver and capture the result
|
508
508
|
app_result = begin
|
509
509
|
query.with_error_handling do
|
@@ -735,7 +735,7 @@ module GraphQL
|
|
735
735
|
final_result = nil
|
736
736
|
end
|
737
737
|
set_all_interpreter_context(continue_value, nil, nil, path) # reset this mutable state
|
738
|
-
|
738
|
+
call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
|
739
739
|
evaluate_selections(
|
740
740
|
path,
|
741
741
|
context.scoped_context,
|
@@ -754,6 +754,8 @@ module GraphQL
|
|
754
754
|
end
|
755
755
|
when "LIST"
|
756
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?
|
757
759
|
response_list = GraphQLResultArray.new(result_name, selection_result)
|
758
760
|
response_list.graphql_non_null_list_items = inner_type.non_null?
|
759
761
|
set_result(selection_result, result_name, response_list)
|
@@ -768,12 +770,12 @@ module GraphQL
|
|
768
770
|
this_idx = idx
|
769
771
|
next_path.freeze
|
770
772
|
idx += 1
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
if HALT != continue_value
|
775
|
-
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)
|
776
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)
|
777
779
|
end
|
778
780
|
end
|
779
781
|
rescue NoMethodError => err
|
@@ -793,12 +795,25 @@ module GraphQL
|
|
793
795
|
end
|
794
796
|
end
|
795
797
|
|
796
|
-
def
|
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)
|
797
812
|
return yield if directives.nil? || directives.empty?
|
798
|
-
run_directive(object, directives, 0, &block)
|
813
|
+
run_directive(method_name, object, directives, 0, &block)
|
799
814
|
end
|
800
815
|
|
801
|
-
def run_directive(object, directives, idx, &block)
|
816
|
+
def run_directive(method_name, object, directives, idx, &block)
|
802
817
|
dir_node = directives[idx]
|
803
818
|
if !dir_node
|
804
819
|
yield
|
@@ -822,8 +837,8 @@ module GraphQL
|
|
822
837
|
if dir_args == HALT
|
823
838
|
nil
|
824
839
|
else
|
825
|
-
dir_defn.
|
826
|
-
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)
|
827
842
|
end
|
828
843
|
end
|
829
844
|
end
|
@@ -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
|
-
|
31
|
-
|
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
|
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module Introspection
|
4
|
-
def self.query(include_deprecated_args: false)
|
4
|
+
def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false)
|
5
5
|
# The introspection query to end all introspection queries, copied from
|
6
6
|
# https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js
|
7
7
|
<<-QUERY
|
8
8
|
query IntrospectionQuery {
|
9
9
|
__schema {
|
10
|
+
#{include_schema_description ? "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
|
+
#{include_is_repeatable ? "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
|
+
#{include_specified_by_url ? "specifiedByUrl" : ""}
|
30
33
|
fields(includeDeprecated: true) {
|
31
34
|
name
|
32
35
|
description
|
@@ -47,7 +47,7 @@ module GraphQL
|
|
47
47
|
def cursor_for(item)
|
48
48
|
load_nodes
|
49
49
|
# index in nodes + existing offset + 1 (because it's offset, not index)
|
50
|
-
offset = nodes.index(item) + 1 + (@paged_nodes_offset || 0)
|
50
|
+
offset = nodes.index(item) + 1 + (@paged_nodes_offset || 0) - (relation_offset(items) || 0)
|
51
51
|
encode(offset.to_s)
|
52
52
|
end
|
53
53
|
|
@@ -116,9 +116,9 @@ module GraphQL
|
|
116
116
|
if defined?(@sliced_nodes_limit)
|
117
117
|
return
|
118
118
|
else
|
119
|
+
next_offset = relation_offset(items) || 0
|
119
120
|
if after_offset
|
120
|
-
|
121
|
-
relation_offset = previous_offset + after_offset
|
121
|
+
next_offset += after_offset
|
122
122
|
end
|
123
123
|
|
124
124
|
if before_offset && after_offset
|
@@ -136,7 +136,7 @@ module GraphQL
|
|
136
136
|
end
|
137
137
|
|
138
138
|
@sliced_nodes_limit = relation_limit
|
139
|
-
@sliced_nodes_offset =
|
139
|
+
@sliced_nodes_offset = next_offset
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
@@ -25,16 +25,16 @@ module GraphQL
|
|
25
25
|
|
26
26
|
def_node_matcher :argument_config_with_required_true?, <<-Pattern
|
27
27
|
(
|
28
|
-
send nil? :argument ... (hash <$(pair (sym :required) (true)) ...>)
|
28
|
+
send {nil? _} :argument ... (hash <$(pair (sym :required) (true)) ...>)
|
29
29
|
)
|
30
30
|
Pattern
|
31
31
|
|
32
32
|
def on_send(node)
|
33
33
|
argument_config_with_required_true?(node) do |required_config|
|
34
34
|
add_offense(required_config) do |corrector|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
cleaned_node_source = source_without_keyword_argument(node, required_config)
|
36
|
+
corrector.replace(node, cleaned_node_source)
|
37
|
+
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -48,13 +48,21 @@ module GraphQL
|
|
48
48
|
# @param directives [Hash{Class => Hash}]
|
49
49
|
# @param deprecation_reason [String]
|
50
50
|
# @param validates [Hash, nil] Options for building validators, if any should be applied
|
51
|
-
|
51
|
+
# @param replace_null_with_default [Boolean] if `true`, incoming values of `null` will be replaced with the configured `default_value`
|
52
|
+
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: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
|
52
53
|
arg_name ||= name
|
53
54
|
@name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
|
54
55
|
@type_expr = type_expr || type
|
55
56
|
@description = desc || description
|
56
57
|
@null = required != true
|
57
58
|
@default_value = default_value
|
59
|
+
if replace_null_with_default
|
60
|
+
if !default_value?
|
61
|
+
raise ArgumentError, "`replace_null_with_default: true` requires a default value, please provide one with `default_value: ...`"
|
62
|
+
end
|
63
|
+
@replace_null_with_default = true
|
64
|
+
end
|
65
|
+
|
58
66
|
@owner = owner
|
59
67
|
@as = as
|
60
68
|
@loads = loads
|
@@ -97,6 +105,10 @@ module GraphQL
|
|
97
105
|
@default_value != NO_DEFAULT
|
98
106
|
end
|
99
107
|
|
108
|
+
def replace_null_with_default?
|
109
|
+
@replace_null_with_default
|
110
|
+
end
|
111
|
+
|
100
112
|
attr_writer :description
|
101
113
|
|
102
114
|
# @return [String] Documentation for this argument
|
@@ -253,6 +265,11 @@ module GraphQL
|
|
253
265
|
return
|
254
266
|
end
|
255
267
|
|
268
|
+
if value.nil? && replace_null_with_default?
|
269
|
+
value = default_value
|
270
|
+
default_used = true
|
271
|
+
end
|
272
|
+
|
256
273
|
loaded_value = nil
|
257
274
|
coerced_value = context.schema.error_handler.with_error_handling(context) do
|
258
275
|
type.coerce_input(value, context)
|
@@ -90,6 +90,11 @@ module GraphQL
|
|
90
90
|
yield
|
91
91
|
end
|
92
92
|
|
93
|
+
# Continuing is passed as a block, yield to continue.
|
94
|
+
def resolve_each(object, arguments, context)
|
95
|
+
yield
|
96
|
+
end
|
97
|
+
|
93
98
|
def on_field?
|
94
99
|
locations.include?(FIELD)
|
95
100
|
end
|
@@ -128,6 +133,10 @@ module GraphQL
|
|
128
133
|
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
|
129
134
|
end
|
130
135
|
|
136
|
+
def graphql_name
|
137
|
+
self.class.graphql_name
|
138
|
+
end
|
139
|
+
|
131
140
|
LOCATIONS = [
|
132
141
|
QUERY = :QUERY,
|
133
142
|
MUTATION = :MUTATION,
|
@@ -147,6 +156,7 @@ module GraphQL
|
|
147
156
|
ENUM_VALUE = :ENUM_VALUE,
|
148
157
|
INPUT_OBJECT = :INPUT_OBJECT,
|
149
158
|
INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION,
|
159
|
+
VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
|
150
160
|
]
|
151
161
|
|
152
162
|
DEFAULT_DEPRECATION_REASON = 'No longer supported'
|
@@ -169,6 +179,7 @@ module GraphQL
|
|
169
179
|
ENUM_VALUE: 'Location adjacent to an enum value definition.',
|
170
180
|
INPUT_OBJECT: 'Location adjacent to an input object type definition.',
|
171
181
|
INPUT_FIELD_DEFINITION: 'Location adjacent to an input object field definition.',
|
182
|
+
VARIABLE_DEFINITION: 'Location adjacent to a variable definition.',
|
172
183
|
}
|
173
184
|
|
174
185
|
private
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -38,7 +38,9 @@ module GraphQL
|
|
38
38
|
|
39
39
|
# @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
|
40
40
|
def owner_type
|
41
|
-
@owner_type ||= if owner
|
41
|
+
@owner_type ||= if owner.nil?
|
42
|
+
raise GraphQL::InvariantError, "Field #{original_name.inspect} (graphql name: #{graphql_name.inspect}) has no owner, but all fields should have an owner. How did this happen?!"
|
43
|
+
elsif owner < GraphQL::Schema::Mutation
|
42
44
|
owner.payload_type
|
43
45
|
else
|
44
46
|
owner
|
@@ -188,6 +190,7 @@ module GraphQL
|
|
188
190
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
189
191
|
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
190
192
|
# @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`)
|
193
|
+
# @param dig [Array<String, Symbol>] The nested hash keys to lookup on the underlying hash to resolve this field using dig
|
191
194
|
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
192
195
|
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
193
196
|
# @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
|
@@ -210,7 +213,7 @@ module GraphQL
|
|
210
213
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
211
214
|
# @param validates [Array<Hash>] Configurations for validating this field
|
212
215
|
# @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
|
213
|
-
def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, 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: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
|
216
|
+
def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, 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: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
|
214
217
|
if name.nil?
|
215
218
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
216
219
|
end
|
@@ -236,8 +239,8 @@ module GraphQL
|
|
236
239
|
@resolve = resolve
|
237
240
|
self.deprecation_reason = deprecation_reason
|
238
241
|
|
239
|
-
if method && hash_key
|
240
|
-
raise ArgumentError, "Provide `method:` _or_ `
|
242
|
+
if method && hash_key && dig
|
243
|
+
raise ArgumentError, "Provide `method:`, `hash_key:` _or_ `dig:`, not multiple. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}, dig: #{dig.inspect}`)"
|
241
244
|
end
|
242
245
|
|
243
246
|
if resolver_method
|
@@ -245,13 +248,14 @@ module GraphQL
|
|
245
248
|
raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
246
249
|
end
|
247
250
|
|
248
|
-
if hash_key
|
249
|
-
raise ArgumentError, "Provide `hash_key
|
251
|
+
if hash_key || dig
|
252
|
+
raise ArgumentError, "Provide `hash_key:`, `dig:`, _or_ `resolver_method:`, not multiple. (called with: `hash_key: #{hash_key.inspect}, dig: #{dig.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
250
253
|
end
|
251
254
|
end
|
252
255
|
|
253
256
|
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
254
257
|
method_name = method || hash_key || name_s
|
258
|
+
@dig_keys = dig
|
255
259
|
resolver_method ||= name_s.to_sym
|
256
260
|
|
257
261
|
@method_str = -method_name.to_s
|
@@ -424,11 +428,14 @@ module GraphQL
|
|
424
428
|
elsif connection?
|
425
429
|
arguments = query.arguments_for(nodes.first, self)
|
426
430
|
max_possible_page_size = nil
|
427
|
-
if arguments[
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
431
|
+
if arguments.respond_to?(:[]) # It might have been an error
|
432
|
+
if arguments[:first]
|
433
|
+
max_possible_page_size = arguments[:first]
|
434
|
+
end
|
435
|
+
|
436
|
+
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
437
|
+
max_possible_page_size = arguments[:last]
|
438
|
+
end
|
432
439
|
end
|
433
440
|
|
434
441
|
if max_possible_page_size.nil?
|
@@ -822,7 +829,9 @@ module GraphQL
|
|
822
829
|
end
|
823
830
|
elsif obj.object.is_a?(Hash)
|
824
831
|
inner_object = obj.object
|
825
|
-
if
|
832
|
+
if @dig_keys
|
833
|
+
inner_object.dig(*@dig_keys)
|
834
|
+
elsif inner_object.key?(@method_sym)
|
826
835
|
inner_object[@method_sym]
|
827
836
|
else
|
828
837
|
inner_object[@method_str]
|
@@ -81,7 +81,7 @@ module GraphQL
|
|
81
81
|
|
82
82
|
def self.authorized?(obj, value, ctx)
|
83
83
|
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
84
|
-
if value.
|
84
|
+
if value.respond_to?(:key?)
|
85
85
|
arguments(ctx).each do |_name, input_obj_arg|
|
86
86
|
input_obj_arg = input_obj_arg.type_class
|
87
87
|
if value.key?(input_obj_arg.keyword) &&
|
@@ -34,6 +34,7 @@ module GraphQL
|
|
34
34
|
Class.new(GraphQL::Schema) do
|
35
35
|
orphan_types(types.values)
|
36
36
|
directives(directives)
|
37
|
+
description(schema["description"])
|
37
38
|
|
38
39
|
def self.resolve_type(*)
|
39
40
|
raise(GraphQL::RequiredImplementationMissingError, "This schema was loaded from string, so it can't resolve types for objects")
|
@@ -141,6 +142,7 @@ module GraphQL
|
|
141
142
|
Class.new(GraphQL::Schema::Scalar) do
|
142
143
|
graphql_name(type["name"])
|
143
144
|
description(type["description"])
|
145
|
+
specified_by_url(type["specifiedByUrl"])
|
144
146
|
end
|
145
147
|
end
|
146
148
|
when "UNION"
|
@@ -160,6 +162,7 @@ module GraphQL
|
|
160
162
|
graphql_name(directive["name"])
|
161
163
|
description(directive["description"])
|
162
164
|
locations(*directive["locations"].map(&:to_sym))
|
165
|
+
repeatable(directive["isRepeatable"])
|
163
166
|
loader.build_arguments(self, directive["args"], type_resolver)
|
164
167
|
end
|
165
168
|
end
|
@@ -32,6 +32,18 @@ module GraphQL
|
|
32
32
|
GraphQL::TypeKinds::SCALAR
|
33
33
|
end
|
34
34
|
|
35
|
+
def specified_by_url(new_url = nil)
|
36
|
+
if new_url
|
37
|
+
@specified_by_url = new_url
|
38
|
+
elsif defined?(@specified_by_url)
|
39
|
+
@specified_by_url
|
40
|
+
elsif superclass.respond_to?(:specified_by_url)
|
41
|
+
superclass.specified_by_url
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
35
47
|
def default_scalar(is_default = nil)
|
36
48
|
if !is_default.nil?
|
37
49
|
@default_scalar = is_default
|
data/lib/graphql/schema.rb
CHANGED
@@ -892,6 +892,17 @@ module GraphQL
|
|
892
892
|
GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
|
893
893
|
end
|
894
894
|
|
895
|
+
# @return [String, nil]
|
896
|
+
def description(new_description = nil)
|
897
|
+
if new_description
|
898
|
+
@description = new_description
|
899
|
+
elsif defined?(@description)
|
900
|
+
@description
|
901
|
+
else
|
902
|
+
find_inherited_value(:description, nil)
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
895
906
|
def find(path)
|
896
907
|
if !@finder
|
897
908
|
@find_cache = {}
|
@@ -1247,11 +1258,7 @@ module GraphQL
|
|
1247
1258
|
when Module
|
1248
1259
|
type_or_name
|
1249
1260
|
else
|
1250
|
-
raise
|
1251
|
-
Invariant: unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})
|
1252
|
-
|
1253
|
-
This is probably a bug in GraphQL-Ruby, please report this error on GitHub: https://github.com/rmosolgo/graphql-ruby/issues/new?template=bug_report.md
|
1254
|
-
ERR
|
1261
|
+
raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
1255
1262
|
end
|
1256
1263
|
|
1257
1264
|
if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
|