Skip to content

Commit 8a85957

Browse files
authored
Schema warden (#116)
1 parent 8d23998 commit 8a85957

File tree

4 files changed

+34
-17
lines changed

4 files changed

+34
-17
lines changed

lib/graphql/stitching/planner.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ def expand_interface_selections(current_location, parent_type, input_selections)
324324
end
325325

326326
if expanded_selections
327-
@supergraph.memoized_schema_possible_types(parent_type.graphql_name).each do |possible_type|
327+
@request.warden.possible_types(parent_type).each do |possible_type|
328328
next unless @supergraph.locations_by_type[possible_type.graphql_name].include?(current_location)
329329

330330
type_name = GraphQL::Language::Nodes::TypeName.new(name: possible_type.graphql_name)

lib/graphql/stitching/request.rb

+12-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class Request
2121
# @return [Hash] contextual object passed through resolver flows.
2222
attr_reader :context
2323

24+
# @return [GraphQL::Schema::Warden] a visibility warden for this request.
25+
attr_reader :warden
26+
2427
# Creates a new supergraph request.
2528
# @param supergraph [Supergraph] supergraph instance that resolves the request.
2629
# @param document [String, GraphQL::Language::Nodes::Document] the request string or parsed AST.
@@ -48,7 +51,11 @@ def initialize(supergraph, document, operation_name: nil, variables: nil, contex
4851

4952
@operation_name = operation_name
5053
@variables = variables || {}
51-
@context = context || GraphQL::Stitching::EMPTY_OBJECT
54+
55+
@query = GraphQL::Query.new(@supergraph.schema, document: @document, context: context)
56+
@warden = @query.warden
57+
@context = @query.context
58+
@context[:request] = self
5259
end
5360

5461
# @return [String] the original document string, or a print of the parsed AST document.
@@ -98,14 +105,14 @@ def operation_directives
98105
end
99106
end
100107

101-
# @return [Hash<String, GraphQL::Language::Nodes::AbstractNode>]
108+
# @return [Hash<String, GraphQL::Language::Nodes::AbstractNode>] map of variable names to AST type definitions.
102109
def variable_definitions
103110
@variable_definitions ||= operation.variables.each_with_object({}) do |v, memo|
104111
memo[v.name] = v.type
105112
end
106113
end
107114

108-
# @return [Hash<String, GraphQL::Language::Nodes::FragmentDefinition>]
115+
# @return [Hash<String, GraphQL::Language::Nodes::FragmentDefinition>] map of fragment names to their AST definitions.
109116
def fragment_definitions
110117
@fragment_definitions ||= @document.definitions.each_with_object({}) do |d, memo|
111118
memo[d.name] = d if d.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
@@ -114,7 +121,8 @@ def fragment_definitions
114121

115122
# Validates the request using the combined supergraph schema.
116123
def validate
117-
@supergraph.schema.validate(@document, context: @context)
124+
result = @supergraph.static_validator.validate(@query)
125+
result[:errors]
118126
end
119127

120128
# Prepares the request for stitching by rendering variable defaults and applying @skip/@include conditionals.

lib/graphql/stitching/shaper.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def introspection_field?(parent_type, node)
118118
def typename_in_type?(typename, type)
119119
return true if type.graphql_name == typename
120120

121-
type.kind.abstract? && @supergraph.memoized_schema_possible_types(type.graphql_name).any? do |t|
121+
type.kind.abstract? && @request.warden.possible_types(type).any? do |t|
122122
t.graphql_name == typename
123123
end
124124
end

lib/graphql/stitching/supergraph.rb

+20-11
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,27 @@ def from_definition(schema, executables:)
7272
end
7373
end
7474

75-
attr_reader :schema, :boundaries, :locations_by_type_and_field, :executables
75+
# @return [GraphQL::Schema] the composed schema for the supergraph.
76+
attr_reader :schema
77+
78+
# @return [Hash<String, Executable>] a map of executable resources by location.
79+
attr_reader :executables
80+
81+
attr_reader :boundaries, :locations_by_type_and_field
7682

7783
def initialize(schema:, fields: {}, boundaries: {}, executables: {})
7884
@schema = schema
85+
@schema.use(GraphQL::Schema::AlwaysVisible)
86+
7987
@boundaries = boundaries
80-
@possible_keys_by_type = {}
81-
@possible_keys_by_type_and_location = {}
82-
@memoized_schema_possible_types = {}
83-
@memoized_schema_fields = {}
84-
@memoized_introspection_types = nil
85-
@memoized_schema_types = nil
8688
@fields_by_type_and_location = nil
8789
@locations_by_type = nil
90+
@memoized_introspection_types = nil
91+
@memoized_schema_fields = {}
92+
@memoized_schema_types = nil
93+
@possible_keys_by_type = {}
94+
@possible_keys_by_type_and_location = {}
95+
@static_validator = nil
8896

8997
# add introspection types into the fields mapping
9098
@locations_by_type_and_field = memoized_introspection_types.each_with_object(fields) do |(type_name, type), memo|
@@ -156,6 +164,11 @@ def to_definition
156164
@schema.to_definition
157165
end
158166

167+
# @return [GraphQL::StaticValidation::Validator] static validator for the supergraph schema.
168+
def static_validator
169+
@static_validator ||= @schema.static_validator
170+
end
171+
159172
def fields
160173
@locations_by_type_and_field.reject { |k, _v| memoized_introspection_types[k] }
161174
end
@@ -172,10 +185,6 @@ def memoized_schema_types
172185
@memoized_schema_types ||= @schema.types
173186
end
174187

175-
def memoized_schema_possible_types(type_name)
176-
@memoized_schema_possible_types[type_name] ||= @schema.possible_types(memoized_schema_types[type_name])
177-
end
178-
179188
def memoized_schema_fields(type_name)
180189
@memoized_schema_fields[type_name] ||= begin
181190
fields = memoized_schema_types[type_name].fields

0 commit comments

Comments
 (0)