graphql 2.0.27 → 2.1.3
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/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- 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/query_depth.rb +7 -2
- 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 +38 -10
- 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/nodes.rb +2 -2
- 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/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 +19 -4
- 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 +4 -0
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/warden.rb +87 -89
- data/lib/graphql/schema.rb +125 -55
- 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 +23 -20
- 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
|
@@ -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
|
@@ -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)
|
@@ -85,6 +60,7 @@ module GraphQL
|
|
85
60
|
def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
|
86
61
|
def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
|
87
62
|
def arguments(owner, ctx); owner.arguments(ctx); end
|
63
|
+
def loadable?(type, ctx); type.visible?(ctx); end
|
88
64
|
end
|
89
65
|
end
|
90
66
|
|
@@ -109,27 +85,22 @@ module GraphQL
|
|
109
85
|
def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable Development/ContextIsPassedCop
|
110
86
|
def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
|
111
87
|
def reachable_type?(type_name); true; end
|
88
|
+
def loadable?(type, _ctx); true; end
|
112
89
|
def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
|
113
90
|
def possible_types(type_defn); @schema.possible_types(type_defn); end
|
114
91
|
def interfaces(obj_type); obj_type.interfaces; end
|
115
92
|
end
|
116
93
|
|
117
|
-
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
118
94
|
# @param context [GraphQL::Query::Context]
|
119
95
|
# @param schema [GraphQL::Schema]
|
120
|
-
def initialize(
|
96
|
+
def initialize(context:, schema:)
|
121
97
|
@schema = schema
|
122
98
|
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
123
99
|
@query = @schema.query
|
124
100
|
@mutation = @schema.mutation
|
125
101
|
@subscription = @schema.subscription
|
126
102
|
@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
|
-
|
103
|
+
@visibility_cache = read_through { |m| schema.visible?(m, context) }
|
133
104
|
@visibility_cache.compare_by_identity
|
134
105
|
# Initialize all ivars to improve object shape consistency:
|
135
106
|
@types = @visible_types = @reachable_types = @visible_parent_fields =
|
@@ -153,6 +124,11 @@ module GraphQL
|
|
153
124
|
end
|
154
125
|
end
|
155
126
|
|
127
|
+
# @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
|
128
|
+
def loadable?(type, _ctx)
|
129
|
+
!reachable_type_set.include?(type) && visible_type?(type)
|
130
|
+
end
|
131
|
+
|
156
132
|
# @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
|
157
133
|
def get_type(type_name)
|
158
134
|
@visible_types ||= read_through do |name|
|
@@ -219,7 +195,16 @@ module GraphQL
|
|
219
195
|
# @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
|
220
196
|
# @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
|
221
197
|
def arguments(argument_owner, ctx = nil)
|
222
|
-
@visible_arguments ||= read_through { |o|
|
198
|
+
@visible_arguments ||= read_through { |o|
|
199
|
+
args = o.arguments(@context)
|
200
|
+
if args.any?
|
201
|
+
args = args.values
|
202
|
+
args.select! { |a| visible_argument?(a, @context) }
|
203
|
+
args
|
204
|
+
else
|
205
|
+
EmptyObjects::EMPTY_ARRAY
|
206
|
+
end
|
207
|
+
}
|
223
208
|
@visible_arguments[argument_owner]
|
224
209
|
end
|
225
210
|
|
@@ -242,7 +227,13 @@ module GraphQL
|
|
242
227
|
|
243
228
|
# @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
|
244
229
|
def interfaces(obj_type)
|
245
|
-
@visible_interfaces ||= read_through { |t|
|
230
|
+
@visible_interfaces ||= read_through { |t|
|
231
|
+
ints = t.interfaces(@context)
|
232
|
+
if ints.any?
|
233
|
+
ints.select! { |i| visible_type?(i) }
|
234
|
+
end
|
235
|
+
ints
|
236
|
+
}
|
246
237
|
@visible_interfaces[obj_type]
|
247
238
|
end
|
248
239
|
|
@@ -298,11 +289,19 @@ module GraphQL
|
|
298
289
|
next true if root_type?(type_defn) || type_defn.introspection?
|
299
290
|
|
300
291
|
if type_defn.kind.union?
|
301
|
-
|
292
|
+
possible_types(type_defn).any? && (referenced?(type_defn) || orphan_type?(type_defn))
|
302
293
|
elsif type_defn.kind.interface?
|
303
|
-
|
294
|
+
possible_types(type_defn).any?
|
304
295
|
else
|
305
|
-
referenced?(type_defn)
|
296
|
+
if referenced?(type_defn)
|
297
|
+
true
|
298
|
+
elsif type_defn.kind.object?
|
299
|
+
# Show this object if it belongs to ...
|
300
|
+
interfaces(type_defn).any? { |t| referenced?(t) } || # an interface which is referenced in the schema
|
301
|
+
union_memberships(type_defn).any? { |t| referenced?(t) || orphan_type?(t) } # or a union which is referenced or added via orphan_types
|
302
|
+
else
|
303
|
+
false
|
304
|
+
end
|
306
305
|
end
|
307
306
|
end
|
308
307
|
|
@@ -369,23 +368,14 @@ module GraphQL
|
|
369
368
|
@schema.orphan_types.include?(type_defn)
|
370
369
|
end
|
371
370
|
|
372
|
-
def visible_abstract_type?(type_defn)
|
373
|
-
type_defn.kind.object? && (
|
374
|
-
interfaces(type_defn).any? ||
|
375
|
-
union_memberships(type_defn).any?
|
376
|
-
)
|
377
|
-
end
|
378
|
-
|
379
|
-
def visible_possible_types?(type_defn)
|
380
|
-
possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
|
381
|
-
end
|
382
|
-
|
383
371
|
def visible?(member)
|
384
372
|
@visibility_cache[member]
|
385
373
|
end
|
386
374
|
|
387
375
|
def read_through
|
388
|
-
Hash.new { |h, k| h[k] = yield(k) }
|
376
|
+
h = Hash.new { |h, k| h[k] = yield(k) }
|
377
|
+
h.compare_by_identity
|
378
|
+
h
|
389
379
|
end
|
390
380
|
|
391
381
|
def reachable_type_set
|
@@ -416,54 +406,62 @@ module GraphQL
|
|
416
406
|
end
|
417
407
|
end
|
418
408
|
|
409
|
+
included_interface_possible_types_set = Set.new
|
410
|
+
|
419
411
|
until unvisited_types.empty?
|
420
412
|
type = unvisited_types.pop
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
413
|
+
visit_type(type, unvisited_types, @reachable_type_set, rt_hash, included_interface_possible_types_set, include_interface_possible_types: false)
|
414
|
+
end
|
415
|
+
|
416
|
+
@reachable_type_set
|
417
|
+
end
|
418
|
+
|
419
|
+
def visit_type(type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types:)
|
420
|
+
if visited_type_set.add?(type) || (include_interface_possible_types && type.kind.interface? && included_interface_possible_types_set.add?(type))
|
421
|
+
type_by_name = type_by_name_hash[type.graphql_name] ||= type
|
422
|
+
if type_by_name != type
|
423
|
+
name_1, name_2 = [type.inspect, type_by_name.inspect].sort
|
424
|
+
raise DuplicateNamesError.new(
|
425
|
+
duplicated_name: type.graphql_name, duplicated_definition_1: name_1, duplicated_definition_2: name_2
|
426
|
+
)
|
427
|
+
end
|
428
|
+
if type.kind.input_object?
|
429
|
+
# recurse into visible arguments
|
430
|
+
arguments(type).each do |argument|
|
431
|
+
argument_type = argument.type.unwrap
|
432
|
+
unvisited_types << argument_type
|
427
433
|
end
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
# recurse into visible
|
436
|
-
|
437
|
-
unvisited_types <<
|
434
|
+
elsif type.kind.union?
|
435
|
+
# recurse into visible possible types
|
436
|
+
possible_types(type).each do |possible_type|
|
437
|
+
unvisited_types << possible_type
|
438
|
+
end
|
439
|
+
elsif type.kind.fields?
|
440
|
+
if type.kind.object?
|
441
|
+
# recurse into visible implemented interfaces
|
442
|
+
interfaces(type).each do |interface|
|
443
|
+
unvisited_types << interface
|
438
444
|
end
|
439
|
-
elsif
|
440
|
-
|
441
|
-
|
442
|
-
possible_types(type).each do |possible_type|
|
443
|
-
unvisited_types << possible_type
|
444
|
-
end
|
445
|
-
elsif type.kind.object?
|
446
|
-
# recurse into visible implemented interfaces
|
447
|
-
interfaces(type).each do |interface|
|
448
|
-
unvisited_types << interface
|
449
|
-
end
|
445
|
+
elsif include_interface_possible_types
|
446
|
+
possible_types(type).each do |pt|
|
447
|
+
unvisited_types << pt
|
450
448
|
end
|
449
|
+
end
|
450
|
+
# Don't visit interface possible types -- it's not enough to justify visibility
|
451
451
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
452
|
+
# recurse into visible fields
|
453
|
+
fields(type).each do |field|
|
454
|
+
field_type = field.type.unwrap
|
455
|
+
# In this case, if it's an interface, we want to include
|
456
|
+
visit_type(field_type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types: true)
|
457
|
+
# recurse into visible arguments
|
458
|
+
arguments(field).each do |argument|
|
459
|
+
argument_type = argument.type.unwrap
|
460
|
+
unvisited_types << argument_type
|
461
461
|
end
|
462
462
|
end
|
463
463
|
end
|
464
464
|
end
|
465
|
-
|
466
|
-
@reachable_type_set
|
467
465
|
end
|
468
466
|
end
|
469
467
|
end
|