graphql 2.0.26 → 2.1.1
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/relay.rb +18 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +15 -11
- data/lib/graphql/dataloader/source.rb +7 -0
- data/lib/graphql/dataloader.rb +9 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +95 -254
- data/lib/graphql/execution/interpreter.rb +0 -6
- data/lib/graphql/execution/lookahead.rb +1 -1
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/language/block_string.rb +28 -16
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +36 -35
- data/lib/graphql/language/lexer.rb +86 -56
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/parser.rb +706 -691
- data/lib/graphql/language/parser.y +1 -0
- data/lib/graphql/language/printer.rb +294 -145
- data/lib/graphql/language/sanitized_printer.rb +20 -22
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +20 -81
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/pagination/connection.rb +23 -1
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +32 -98
- data/lib/graphql/query.rb +2 -19
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/addition.rb +6 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +7 -1
- data/lib/graphql/schema/field.rb +7 -4
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +14 -2
- data/lib/graphql/schema/member/has_fields.rb +4 -1
- data/lib/graphql/schema/member/has_interfaces.rb +21 -7
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +14 -8
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/warden.rb +23 -37
- data/lib/graphql/schema.rb +23 -22
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions.rb +11 -6
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +19 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -2
- metadata +7 -4
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/static_validation/type_stack.rb +0 -216
|
@@ -30,6 +30,10 @@ module GraphQL
|
|
|
30
30
|
# @see authorized_new to make instances
|
|
31
31
|
protected :new
|
|
32
32
|
|
|
33
|
+
def wrap_scoped(object, context)
|
|
34
|
+
scoped_new(object, context)
|
|
35
|
+
end
|
|
36
|
+
|
|
33
37
|
# This is called by the runtime to return an object to call methods on.
|
|
34
38
|
def wrap(object, context)
|
|
35
39
|
authorized_new(object, context)
|
|
@@ -91,6 +95,10 @@ module GraphQL
|
|
|
91
95
|
end
|
|
92
96
|
end
|
|
93
97
|
end
|
|
98
|
+
|
|
99
|
+
def scoped_new(object, context)
|
|
100
|
+
self.new(object, context)
|
|
101
|
+
end
|
|
94
102
|
end
|
|
95
103
|
|
|
96
104
|
def initialize(object, context)
|
|
@@ -36,15 +36,11 @@ module GraphQL
|
|
|
36
36
|
|
|
37
37
|
# @param schema [GraphQL::Schema]
|
|
38
38
|
# @param context [Hash]
|
|
39
|
-
# @param only [<#call(member, ctx)>]
|
|
40
|
-
# @param except [<#call(member, ctx)>]
|
|
41
39
|
# @param introspection [Boolean] Should include the introspection types in the string?
|
|
42
|
-
def initialize(schema, context: nil,
|
|
40
|
+
def initialize(schema, context: nil, introspection: false)
|
|
43
41
|
@document_from_schema = GraphQL::Language::DocumentFromSchemaDefinition.new(
|
|
44
42
|
schema,
|
|
45
43
|
context: context,
|
|
46
|
-
only: only,
|
|
47
|
-
except: except,
|
|
48
44
|
include_introspection_types: introspection,
|
|
49
45
|
)
|
|
50
46
|
|
|
@@ -61,7 +57,12 @@ module GraphQL
|
|
|
61
57
|
false
|
|
62
58
|
end
|
|
63
59
|
end
|
|
64
|
-
schema = Class.new(GraphQL::Schema) {
|
|
60
|
+
schema = Class.new(GraphQL::Schema) {
|
|
61
|
+
query(query_root)
|
|
62
|
+
def self.visible?(member, _ctx)
|
|
63
|
+
member.graphql_name != "Root"
|
|
64
|
+
end
|
|
65
|
+
}
|
|
65
66
|
|
|
66
67
|
introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
|
|
67
68
|
schema,
|
|
@@ -94,7 +95,7 @@ module GraphQL
|
|
|
94
95
|
|
|
95
96
|
class IntrospectionPrinter < GraphQL::Language::Printer
|
|
96
97
|
def print_schema_definition(schema)
|
|
97
|
-
"schema {\n query: Root\n}"
|
|
98
|
+
print_string("schema {\n query: Root\n}")
|
|
98
99
|
end
|
|
99
100
|
end
|
|
100
101
|
end
|
|
@@ -21,6 +21,10 @@ module GraphQL
|
|
|
21
21
|
# @see {GraphQL::Schema::Mutation} for an example, it's basically the same.
|
|
22
22
|
#
|
|
23
23
|
class RelayClassicMutation < GraphQL::Schema::Mutation
|
|
24
|
+
include GraphQL::Schema::HasSingleInputArgument
|
|
25
|
+
|
|
26
|
+
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
|
27
|
+
|
|
24
28
|
# The payload should always include this field
|
|
25
29
|
field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
|
|
26
30
|
# Relay classic default:
|
|
@@ -31,34 +35,14 @@ module GraphQL
|
|
|
31
35
|
def resolve_with_support(**inputs)
|
|
32
36
|
input = inputs[:input].to_kwargs
|
|
33
37
|
|
|
34
|
-
new_extras = field ? field.extras : []
|
|
35
|
-
all_extras = self.class.extras + new_extras
|
|
36
|
-
|
|
37
|
-
# Transfer these from the top-level hash to the
|
|
38
|
-
# shortcutted `input:` object
|
|
39
|
-
all_extras.each do |ext|
|
|
40
|
-
# It's possible that the `extra` was not passed along by this point,
|
|
41
|
-
# don't re-add it if it wasn't given here.
|
|
42
|
-
if inputs.key?(ext)
|
|
43
|
-
input[ext] = inputs[ext]
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
38
|
if input
|
|
48
39
|
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
|
49
40
|
input_kwargs = input.to_h
|
|
50
41
|
client_mutation_id = input_kwargs.delete(:client_mutation_id)
|
|
51
|
-
|
|
52
|
-
# Relay Classic Mutations with no `argument`s
|
|
53
|
-
# don't require `input:`
|
|
54
|
-
input_kwargs = {}
|
|
42
|
+
inputs[:input] = input_kwargs
|
|
55
43
|
end
|
|
56
44
|
|
|
57
|
-
return_value =
|
|
58
|
-
super(**input_kwargs)
|
|
59
|
-
else
|
|
60
|
-
super()
|
|
61
|
-
end
|
|
45
|
+
return_value = super(**inputs)
|
|
62
46
|
|
|
63
47
|
context.query.after_lazy(return_value) do |return_hash|
|
|
64
48
|
# It might be an error
|
|
@@ -68,112 +52,6 @@ module GraphQL
|
|
|
68
52
|
return_hash
|
|
69
53
|
end
|
|
70
54
|
end
|
|
71
|
-
|
|
72
|
-
class << self
|
|
73
|
-
def dummy
|
|
74
|
-
@dummy ||= begin
|
|
75
|
-
d = Class.new(GraphQL::Schema::Resolver)
|
|
76
|
-
d.argument_class(self.argument_class)
|
|
77
|
-
# TODO make this lazier?
|
|
78
|
-
d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
|
|
79
|
-
d
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def field_arguments(context = GraphQL::Query::NullContext)
|
|
84
|
-
dummy.arguments(context)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def get_field_argument(name, context = GraphQL::Query::NullContext)
|
|
88
|
-
dummy.get_argument(name, context)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def own_field_arguments
|
|
92
|
-
dummy.own_arguments
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def all_field_argument_definitions
|
|
96
|
-
dummy.all_argument_definitions
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Also apply this argument to the input type:
|
|
100
|
-
def argument(*args, own_argument: false, **kwargs, &block)
|
|
101
|
-
it = input_type # make sure any inherited arguments are already added to it
|
|
102
|
-
arg = super(*args, **kwargs, &block)
|
|
103
|
-
|
|
104
|
-
# This definition might be overriding something inherited;
|
|
105
|
-
# if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
|
|
106
|
-
prev_args = it.own_arguments[arg.graphql_name]
|
|
107
|
-
case prev_args
|
|
108
|
-
when GraphQL::Schema::Argument
|
|
109
|
-
if prev_args.owner != self
|
|
110
|
-
it.own_arguments.delete(arg.graphql_name)
|
|
111
|
-
end
|
|
112
|
-
when Array
|
|
113
|
-
prev_args.reject! { |a| a.owner != self }
|
|
114
|
-
if prev_args.empty?
|
|
115
|
-
it.own_arguments.delete(arg.graphql_name)
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
it.add_argument(arg)
|
|
120
|
-
arg
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# The base class for generated input object types
|
|
124
|
-
# @param new_class [Class] The base class to use for generating input object definitions
|
|
125
|
-
# @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
|
|
126
|
-
def input_object_class(new_class = nil)
|
|
127
|
-
if new_class
|
|
128
|
-
@input_object_class = new_class
|
|
129
|
-
end
|
|
130
|
-
@input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
|
|
134
|
-
# @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
|
|
135
|
-
def input_type(new_input_type = nil)
|
|
136
|
-
if new_input_type
|
|
137
|
-
@input_type = new_input_type
|
|
138
|
-
end
|
|
139
|
-
@input_type ||= generate_input_type
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
private
|
|
143
|
-
|
|
144
|
-
# Generate the input type for the `input:` argument
|
|
145
|
-
# To customize how input objects are generated, override this method
|
|
146
|
-
# @return [Class] a subclass of {.input_object_class}
|
|
147
|
-
def generate_input_type
|
|
148
|
-
mutation_args = all_argument_definitions
|
|
149
|
-
mutation_class = self
|
|
150
|
-
Class.new(input_object_class) do
|
|
151
|
-
class << self
|
|
152
|
-
def default_graphql_name
|
|
153
|
-
"#{self.mutation.graphql_name}Input"
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def description(new_desc = nil)
|
|
157
|
-
super || "Autogenerated input type of #{self.mutation.graphql_name}"
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
mutation(mutation_class)
|
|
161
|
-
# these might be inherited:
|
|
162
|
-
mutation_args.each do |arg|
|
|
163
|
-
add_argument(arg)
|
|
164
|
-
end
|
|
165
|
-
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
private
|
|
171
|
-
|
|
172
|
-
def authorize_arguments(args, values)
|
|
173
|
-
# remove the `input` wrapper to match values
|
|
174
|
-
input_args = args["input"].type.unwrap.arguments(context)
|
|
175
|
-
super(input_args, values)
|
|
176
|
-
end
|
|
177
55
|
end
|
|
178
56
|
end
|
|
179
57
|
end
|
|
@@ -65,19 +65,20 @@ module GraphQL
|
|
|
65
65
|
# @api private
|
|
66
66
|
def resolve_with_support(**args)
|
|
67
67
|
# First call the ready? hook which may raise
|
|
68
|
-
|
|
68
|
+
raw_ready_val = if args.any?
|
|
69
69
|
ready?(**args)
|
|
70
70
|
else
|
|
71
71
|
ready?
|
|
72
72
|
end
|
|
73
|
-
context.query.after_lazy(
|
|
74
|
-
if
|
|
73
|
+
context.query.after_lazy(raw_ready_val) do |ready_val|
|
|
74
|
+
if ready_val.is_a?(Array)
|
|
75
|
+
is_ready, ready_early_return = ready_val
|
|
75
76
|
if is_ready != false
|
|
76
77
|
raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{is_ready.inspect}, #{ready_early_return.inspect}]"
|
|
77
78
|
else
|
|
78
79
|
ready_early_return
|
|
79
80
|
end
|
|
80
|
-
elsif
|
|
81
|
+
elsif ready_val
|
|
81
82
|
# Then call each prepare hook, which may return a different value
|
|
82
83
|
# for that argument, or may return a lazy object
|
|
83
84
|
load_arguments_val = load_arguments(args)
|
|
@@ -85,21 +86,22 @@ module GraphQL
|
|
|
85
86
|
@prepared_arguments = loaded_args
|
|
86
87
|
Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
|
|
87
88
|
# Then call `authorized?`, which may raise or may return a lazy object
|
|
88
|
-
|
|
89
|
+
raw_authorized_val = if loaded_args.any?
|
|
89
90
|
authorized?(**loaded_args)
|
|
90
91
|
else
|
|
91
92
|
authorized?
|
|
92
93
|
end
|
|
93
|
-
context.query.after_lazy(
|
|
94
|
+
context.query.after_lazy(raw_authorized_val) do |authorized_val|
|
|
94
95
|
# If the `authorized?` returned two values, `false, early_return`,
|
|
95
96
|
# then use the early return value instead of continuing
|
|
96
|
-
if
|
|
97
|
+
if authorized_val.is_a?(Array)
|
|
98
|
+
authorized_result, early_return = authorized_val
|
|
97
99
|
if authorized_result == false
|
|
98
100
|
early_return
|
|
99
101
|
else
|
|
100
102
|
raise "Unexpected result from #authorized? (expected `true`, `false` or `[false, {...}]`): [#{authorized_result.inspect}, #{early_return.inspect}]"
|
|
101
103
|
end
|
|
102
|
-
elsif
|
|
104
|
+
elsif authorized_val
|
|
103
105
|
# Finally, all the hooks have passed, so resolve it
|
|
104
106
|
if loaded_args.any?
|
|
105
107
|
public_send(self.class.resolve_method, **loaded_args)
|
|
@@ -212,6 +214,10 @@ module GraphQL
|
|
|
212
214
|
arguments(context)
|
|
213
215
|
end
|
|
214
216
|
|
|
217
|
+
def any_field_arguments?
|
|
218
|
+
any_arguments?
|
|
219
|
+
end
|
|
220
|
+
|
|
215
221
|
def get_field_argument(name, context = GraphQL::Query::NullContext)
|
|
216
222
|
get_argument(name, context)
|
|
217
223
|
end
|
|
@@ -19,9 +19,9 @@ module GraphQL
|
|
|
19
19
|
|
|
20
20
|
def specified_by_url(new_url = nil)
|
|
21
21
|
if new_url
|
|
22
|
-
|
|
23
|
-
elsif
|
|
24
|
-
|
|
22
|
+
directive(GraphQL::Schema::Directive::SpecifiedBy, url: new_url)
|
|
23
|
+
elsif (directive = directives.find { |dir| dir.graphql_name == "specifiedBy" })
|
|
24
|
+
directive.arguments[:url] # rubocop:disable Development/ContextIsPassedCop
|
|
25
25
|
elsif superclass.respond_to?(:specified_by_url)
|
|
26
26
|
superclass.specified_by_url
|
|
27
27
|
else
|
|
@@ -28,14 +28,19 @@ module GraphQL
|
|
|
28
28
|
def resolve_with_support(**args)
|
|
29
29
|
result = nil
|
|
30
30
|
unsubscribed = true
|
|
31
|
-
catch :graphql_subscription_unsubscribed do
|
|
31
|
+
unsubscribed_result = catch :graphql_subscription_unsubscribed do
|
|
32
32
|
result = super
|
|
33
33
|
unsubscribed = false
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
if unsubscribed
|
|
38
|
-
|
|
38
|
+
if unsubscribed_result
|
|
39
|
+
context.namespace(:subscriptions)[:final_update] = true
|
|
40
|
+
unsubscribed_result
|
|
41
|
+
else
|
|
42
|
+
context.skip
|
|
43
|
+
end
|
|
39
44
|
else
|
|
40
45
|
result
|
|
41
46
|
end
|
|
@@ -94,9 +99,11 @@ module GraphQL
|
|
|
94
99
|
end
|
|
95
100
|
|
|
96
101
|
# Call this to halt execution and remove this subscription from the system
|
|
97
|
-
|
|
102
|
+
# @param update_value [Object] if present, deliver this update before unsubscribing
|
|
103
|
+
# @return [void]
|
|
104
|
+
def unsubscribe(update_value = nil)
|
|
98
105
|
context.namespace(:subscriptions)[:unsubscribed] = true
|
|
99
|
-
throw :graphql_subscription_unsubscribed
|
|
106
|
+
throw :graphql_subscription_unsubscribed, update_value
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
READING_SCOPE = ::Object.new
|
|
@@ -133,7 +133,7 @@ module GraphQL
|
|
|
133
133
|
if all_errors.frozen? # It's empty
|
|
134
134
|
all_errors = []
|
|
135
135
|
end
|
|
136
|
-
interpolation_vars = { validated: validated.graphql_name }
|
|
136
|
+
interpolation_vars = { validated: validated.graphql_name, value: value.inspect }
|
|
137
137
|
if errors.is_a?(String)
|
|
138
138
|
all_errors << (errors % interpolation_vars)
|
|
139
139
|
else
|
|
@@ -4,37 +4,12 @@ require 'set'
|
|
|
4
4
|
|
|
5
5
|
module GraphQL
|
|
6
6
|
class Schema
|
|
7
|
-
# Restrict access to a {GraphQL::Schema} with a user-defined
|
|
7
|
+
# Restrict access to a {GraphQL::Schema} with a user-defined `visible?` implementations.
|
|
8
8
|
#
|
|
9
9
|
# When validating and executing a query, all access to schema members
|
|
10
10
|
# should go through a warden. If you access the schema directly,
|
|
11
11
|
# you may show a client something that it shouldn't be allowed to see.
|
|
12
12
|
#
|
|
13
|
-
# @example Hiding private fields
|
|
14
|
-
# private_members = -> (member, ctx) { member.metadata[:private] }
|
|
15
|
-
# result = Schema.execute(query_string, except: private_members)
|
|
16
|
-
#
|
|
17
|
-
# @example Custom filter implementation
|
|
18
|
-
# # It must respond to `#call(member)`.
|
|
19
|
-
# class MissingRequiredFlags
|
|
20
|
-
# def initialize(user)
|
|
21
|
-
# @user = user
|
|
22
|
-
# end
|
|
23
|
-
#
|
|
24
|
-
# # Return `false` if any required flags are missing
|
|
25
|
-
# def call(member, ctx)
|
|
26
|
-
# member.metadata[:required_flags].any? do |flag|
|
|
27
|
-
# !@user.has_flag?(flag)
|
|
28
|
-
# end
|
|
29
|
-
# end
|
|
30
|
-
# end
|
|
31
|
-
#
|
|
32
|
-
# # Then, use the custom filter in query:
|
|
33
|
-
# missing_required_flags = MissingRequiredFlags.new(current_user)
|
|
34
|
-
#
|
|
35
|
-
# # This query can only access members which match the user's flags
|
|
36
|
-
# result = Schema.execute(query_string, except: missing_required_flags)
|
|
37
|
-
#
|
|
38
13
|
# @api private
|
|
39
14
|
class Warden
|
|
40
15
|
def self.from_context(context)
|
|
@@ -114,22 +89,16 @@ module GraphQL
|
|
|
114
89
|
def interfaces(obj_type); obj_type.interfaces; end
|
|
115
90
|
end
|
|
116
91
|
|
|
117
|
-
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
|
118
92
|
# @param context [GraphQL::Query::Context]
|
|
119
93
|
# @param schema [GraphQL::Schema]
|
|
120
|
-
def initialize(
|
|
94
|
+
def initialize(context:, schema:)
|
|
121
95
|
@schema = schema
|
|
122
96
|
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
|
123
97
|
@query = @schema.query
|
|
124
98
|
@mutation = @schema.mutation
|
|
125
99
|
@subscription = @schema.subscription
|
|
126
100
|
@context = context
|
|
127
|
-
@visibility_cache =
|
|
128
|
-
read_through { |m| filter.call(m, context) }
|
|
129
|
-
else
|
|
130
|
-
read_through { |m| schema.visible?(m, context) }
|
|
131
|
-
end
|
|
132
|
-
|
|
101
|
+
@visibility_cache = read_through { |m| schema.visible?(m, context) }
|
|
133
102
|
@visibility_cache.compare_by_identity
|
|
134
103
|
# Initialize all ivars to improve object shape consistency:
|
|
135
104
|
@types = @visible_types = @reachable_types = @visible_parent_fields =
|
|
@@ -219,7 +188,16 @@ module GraphQL
|
|
|
219
188
|
# @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
|
|
220
189
|
# @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
|
|
221
190
|
def arguments(argument_owner, ctx = nil)
|
|
222
|
-
@visible_arguments ||= read_through { |o|
|
|
191
|
+
@visible_arguments ||= read_through { |o|
|
|
192
|
+
args = o.arguments(@context)
|
|
193
|
+
if args.any?
|
|
194
|
+
args = args.values
|
|
195
|
+
args.select! { |a| visible_argument?(a, @context) }
|
|
196
|
+
args
|
|
197
|
+
else
|
|
198
|
+
EmptyObjects::EMPTY_ARRAY
|
|
199
|
+
end
|
|
200
|
+
}
|
|
223
201
|
@visible_arguments[argument_owner]
|
|
224
202
|
end
|
|
225
203
|
|
|
@@ -242,7 +220,13 @@ module GraphQL
|
|
|
242
220
|
|
|
243
221
|
# @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
|
|
244
222
|
def interfaces(obj_type)
|
|
245
|
-
@visible_interfaces ||= read_through { |t|
|
|
223
|
+
@visible_interfaces ||= read_through { |t|
|
|
224
|
+
ints = t.interfaces(@context)
|
|
225
|
+
if ints.any?
|
|
226
|
+
ints.select! { |i| visible_type?(i) }
|
|
227
|
+
end
|
|
228
|
+
ints
|
|
229
|
+
}
|
|
246
230
|
@visible_interfaces[obj_type]
|
|
247
231
|
end
|
|
248
232
|
|
|
@@ -385,7 +369,9 @@ module GraphQL
|
|
|
385
369
|
end
|
|
386
370
|
|
|
387
371
|
def read_through
|
|
388
|
-
Hash.new { |h, k| h[k] = yield(k) }
|
|
372
|
+
h = Hash.new { |h, k| h[k] = yield(k) }
|
|
373
|
+
h.compare_by_identity
|
|
374
|
+
h
|
|
389
375
|
end
|
|
390
376
|
|
|
391
377
|
def reachable_type_set
|
data/lib/graphql/schema.rb
CHANGED
|
@@ -37,10 +37,12 @@ require "graphql/schema/directive/skip"
|
|
|
37
37
|
require "graphql/schema/directive/feature"
|
|
38
38
|
require "graphql/schema/directive/flagged"
|
|
39
39
|
require "graphql/schema/directive/transform"
|
|
40
|
+
require "graphql/schema/directive/specified_by"
|
|
40
41
|
require "graphql/schema/type_membership"
|
|
41
42
|
|
|
42
43
|
require "graphql/schema/resolver"
|
|
43
44
|
require "graphql/schema/mutation"
|
|
45
|
+
require "graphql/schema/has_single_input_argument"
|
|
44
46
|
require "graphql/schema/relay_classic_mutation"
|
|
45
47
|
require "graphql/schema/subscription"
|
|
46
48
|
|
|
@@ -222,7 +224,7 @@ module GraphQL
|
|
|
222
224
|
# @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
|
|
223
225
|
# @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
|
|
224
226
|
# @return [Hash] GraphQL result
|
|
225
|
-
def as_json(
|
|
227
|
+
def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
|
226
228
|
introspection_query = Introspection.query(
|
|
227
229
|
include_deprecated_args: include_deprecated_args,
|
|
228
230
|
include_schema_description: include_schema_description,
|
|
@@ -231,16 +233,14 @@ module GraphQL
|
|
|
231
233
|
include_specified_by_url: include_specified_by_url,
|
|
232
234
|
)
|
|
233
235
|
|
|
234
|
-
execute(introspection_query,
|
|
236
|
+
execute(introspection_query, context: context).to_h
|
|
235
237
|
end
|
|
236
238
|
|
|
237
239
|
# Return the GraphQL IDL for the schema
|
|
238
240
|
# @param context [Hash]
|
|
239
|
-
# @param only [<#call(member, ctx)>]
|
|
240
|
-
# @param except [<#call(member, ctx)>]
|
|
241
241
|
# @return [String]
|
|
242
|
-
def to_definition(
|
|
243
|
-
GraphQL::Schema::Printer.print_schema(self,
|
|
242
|
+
def to_definition(context: {})
|
|
243
|
+
GraphQL::Schema::Printer.print_schema(self, context: context)
|
|
244
244
|
end
|
|
245
245
|
|
|
246
246
|
# Return the GraphQL::Language::Document IDL AST for the schema
|
|
@@ -268,20 +268,6 @@ module GraphQL
|
|
|
268
268
|
@find_cache[path] ||= @finder.find(path)
|
|
269
269
|
end
|
|
270
270
|
|
|
271
|
-
def default_filter
|
|
272
|
-
GraphQL::Filter.new(except: default_mask)
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def default_mask(new_mask = nil)
|
|
276
|
-
if new_mask
|
|
277
|
-
line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
|
|
278
|
-
GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
|
|
279
|
-
@own_default_mask = new_mask
|
|
280
|
-
else
|
|
281
|
-
@own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
|
|
285
271
|
def static_validator
|
|
286
272
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
|
287
273
|
end
|
|
@@ -776,7 +762,16 @@ module GraphQL
|
|
|
776
762
|
own_orphan_types.concat(new_orphan_types.flatten)
|
|
777
763
|
end
|
|
778
764
|
|
|
779
|
-
find_inherited_value(:orphan_types,
|
|
765
|
+
inherited_ot = find_inherited_value(:orphan_types, nil)
|
|
766
|
+
if inherited_ot
|
|
767
|
+
if own_orphan_types.any?
|
|
768
|
+
inherited_ot + own_orphan_types
|
|
769
|
+
else
|
|
770
|
+
inherited_ot
|
|
771
|
+
end
|
|
772
|
+
else
|
|
773
|
+
own_orphan_types
|
|
774
|
+
end
|
|
780
775
|
end
|
|
781
776
|
|
|
782
777
|
def default_execution_strategy
|
|
@@ -978,7 +973,12 @@ module GraphQL
|
|
|
978
973
|
new_directives.flatten.each { |d| directive(d) }
|
|
979
974
|
end
|
|
980
975
|
|
|
981
|
-
find_inherited_value(:directives, default_directives)
|
|
976
|
+
inherited_dirs = find_inherited_value(:directives, default_directives)
|
|
977
|
+
if own_directives.any?
|
|
978
|
+
inherited_dirs.merge(own_directives)
|
|
979
|
+
else
|
|
980
|
+
inherited_dirs
|
|
981
|
+
end
|
|
982
982
|
end
|
|
983
983
|
|
|
984
984
|
# Attach a single directive to this schema
|
|
@@ -994,6 +994,7 @@ module GraphQL
|
|
|
994
994
|
"skip" => GraphQL::Schema::Directive::Skip,
|
|
995
995
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
|
996
996
|
"oneOf" => GraphQL::Schema::Directive::OneOf,
|
|
997
|
+
"specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
|
|
997
998
|
}.freeze
|
|
998
999
|
end
|
|
999
1000
|
|
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
|
3
3
|
module StaticValidation
|
|
4
4
|
# Default rules for {GraphQL::StaticValidation::Validator}
|
|
5
5
|
#
|
|
6
|
-
# Order is important here. Some validators
|
|
6
|
+
# Order is important here. Some validators skip later hooks.
|
|
7
7
|
# which stops the visit on that node. That way it doesn't try to find fields on types that
|
|
8
8
|
# don't exist, etc.
|
|
9
9
|
ALL_RULES = [
|
|
@@ -111,7 +111,7 @@ module GraphQL
|
|
|
111
111
|
# that required fields are missing
|
|
112
112
|
required_field_names = @warden.arguments(type)
|
|
113
113
|
.select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
|
|
114
|
-
.map(&:name)
|
|
114
|
+
.map!(&:name)
|
|
115
115
|
|
|
116
116
|
present_field_names = ast_node.arguments.map(&:name)
|
|
117
117
|
missing_required_field_names = required_field_names - present_field_names
|
|
@@ -340,7 +340,7 @@ module GraphQL
|
|
|
340
340
|
selections.each do |node|
|
|
341
341
|
case node
|
|
342
342
|
when GraphQL::Language::Nodes::Field
|
|
343
|
-
definition = context.
|
|
343
|
+
definition = context.warden.get_field(owner_type, node.name)
|
|
344
344
|
fields << Field.new(node, definition, owner_type, parents)
|
|
345
345
|
when GraphQL::Language::Nodes::InlineFragment
|
|
346
346
|
fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
|
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
|
21
21
|
present_argument_names = ast_node.arguments.map(&:name)
|
|
22
22
|
required_argument_names = context.warden.arguments(defn)
|
|
23
23
|
.select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
|
|
24
|
-
.map(&:name)
|
|
24
|
+
.map!(&:name)
|
|
25
25
|
|
|
26
26
|
missing_names = required_argument_names - present_argument_names
|
|
27
27
|
if missing_names.any?
|
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
|
36
36
|
|
|
37
37
|
required_fields = context.warden.arguments(parent_type)
|
|
38
38
|
.select{|arg| arg.type.kind.non_null?}
|
|
39
|
-
.map(&:graphql_name)
|
|
39
|
+
.map!(&:graphql_name)
|
|
40
40
|
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
|
42
42
|
missing_fields = required_fields - present_fields
|
|
@@ -8,20 +8,20 @@ module GraphQL
|
|
|
8
8
|
# It provides access to the schema & fragments which validators may read from.
|
|
9
9
|
#
|
|
10
10
|
# It holds a list of errors which each validator may add to.
|
|
11
|
-
#
|
|
12
|
-
# It also provides limited access to the {TypeStack} instance,
|
|
13
|
-
# which tracks state as you climb in and out of different fields.
|
|
14
11
|
class ValidationContext
|
|
15
12
|
extend Forwardable
|
|
16
13
|
|
|
17
14
|
attr_reader :query, :errors, :visitor,
|
|
18
15
|
:on_dependency_resolve_handlers,
|
|
19
|
-
:max_errors
|
|
16
|
+
:max_errors, :warden, :schema
|
|
17
|
+
|
|
20
18
|
|
|
21
|
-
def_delegators :@query, :
|
|
19
|
+
def_delegators :@query, :document, :fragments, :operations
|
|
22
20
|
|
|
23
21
|
def initialize(query, visitor_class, max_errors)
|
|
24
22
|
@query = query
|
|
23
|
+
@warden = query.warden
|
|
24
|
+
@schema = query.schema
|
|
25
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
|
26
26
|
@errors = []
|
|
27
27
|
@max_errors = max_errors || Float::INFINITY
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "graphql/static_validation/error"
|
|
3
3
|
require "graphql/static_validation/definition_dependencies"
|
|
4
|
-
require "graphql/static_validation/type_stack"
|
|
5
4
|
require "graphql/static_validation/validator"
|
|
6
5
|
require "graphql/static_validation/validation_context"
|
|
7
6
|
require "graphql/static_validation/validation_timeout_error"
|
|
@@ -124,7 +124,8 @@ module GraphQL
|
|
|
124
124
|
# This subscription was re-evaluated.
|
|
125
125
|
# Send it to the specific stream where this client was waiting.
|
|
126
126
|
def deliver(subscription_id, result)
|
|
127
|
-
|
|
127
|
+
has_more = !result.context.namespace(:subscriptions)[:final_update]
|
|
128
|
+
payload = { result: result.to_h, more: has_more }
|
|
128
129
|
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
|
129
130
|
end
|
|
130
131
|
|