graphql 1.13.5 → 1.13.9
Sign up to get free protection for your applications and to get access to all the features.
- 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))
|