Skip to content

Commit 4ea88d6

Browse files
committed
fix(Query) handle comment-only queries by returning nothing
1 parent ccb5868 commit 4ea88d6

File tree

3 files changed

+65
-49
lines changed

3 files changed

+65
-49
lines changed

lib/graphql/query.rb

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -63,30 +63,33 @@ def initialize(schema, query_string = nil, document: nil, context: nil, variable
6363
end
6464

6565
@arguments_cache = Hash.new { |h, k| h[k] = {} }
66+
@validation_errors = []
67+
@analysis_errors = []
68+
@internal_representation = {}
69+
valid?
6670
end
6771

6872
# Get the result for this query, executing it once
6973
def result
7074
@result ||= begin
71-
if @validate && (validation_errors.any? || analysis_errors.any?)
72-
{ "errors" => validation_errors + analysis_errors}
75+
if !valid?
76+
all_errors = validation_errors + analysis_errors
77+
if all_errors.any?
78+
{ "errors" => all_errors }
79+
else
80+
nil
81+
end
7382
else
7483
Executor.new(self).result
7584
end
7685
end
77-
7886
end
7987

8088

8189
# This is the operation to run for this query.
8290
# If more than one operation is present, it must be named at runtime.
8391
# @return [GraphQL::Language::Nodes::OperationDefinition, nil]
84-
def selected_operation
85-
@selected_operation ||= begin
86-
perform_validation
87-
@selected_operation
88-
end
89-
end
92+
attr_reader :selected_operation
9093

9194
# Determine the values for variables of this query, using default values
9295
# if a value isn't provided at runtime.
@@ -101,19 +104,16 @@ def variables
101104
)
102105
end
103106

104-
def internal_representation
105-
@internal_representation ||= begin
106-
perform_validation
107-
@internal_representation
108-
end
109-
end
107+
# @return [Hash<String, nil => GraphQL::InternalRepresentation::Node] Operation name -> Irep node pairs
108+
attr_reader :internal_representation
110109

111-
def validation_errors
112-
@validation_errors ||= begin
113-
perform_validation
114-
@validation_errors
115-
end
116-
end
110+
# TODO this should probably contain error instances, not hashes
111+
# @return [Array<Hash>] Static validation errors for the query string
112+
attr_reader :validation_errors
113+
114+
# TODO this should probably contain error instances, not hashes
115+
# @return [Array<Hash>] Errors for this particular query run (eg, exceeds max complexity)
116+
attr_reader :analysis_errors
117117

118118
# Node-level cache for calculating arguments. Used during execution and query analysis.
119119
# @return [GraphQL::Query::Arguments] Arguments for this node, merging default values, literal values and query variables
@@ -127,24 +127,55 @@ def arguments_for(irep_node, definition)
127127
end
128128
end
129129

130+
# @return [GraphQL::Language::Nodes::Document, nil]
131+
def selected_operation
132+
@selected_operation ||= find_operation(@operations, @operation_name)
133+
end
134+
135+
def valid?
136+
@valid ||= if @validate
137+
document_valid? && query_possible? && query_valid?
138+
else
139+
true
140+
end
141+
end
142+
130143
private
131144

132-
def perform_validation
133-
@selected_operation = find_operation(@operations, @operation_name)
145+
146+
# Assert that the passed-in query string is internally consistent
147+
def document_valid?
134148
validation_result = schema.static_validator.validate(self)
135149
@validation_errors = validation_result[:errors]
136150
@internal_representation = validation_result[:irep]
137-
if @validation_errors.none?
138-
# Accessing variables will raise errors if there are any :S
139-
variables
140-
end
141-
nil
151+
@validation_errors.none?
152+
end
153+
154+
# Given that the document is valid, do we have what we need to
155+
# execute the document this time?
156+
# - Is there an operation to run?
157+
# - Are all variables accounted for?
158+
def query_possible?
159+
!selected_operation.nil? && variables
160+
true
142161
rescue GraphQL::Query::OperationNameMissingError, GraphQL::Query::VariableValidationError => err
143-
@validation_errors = [err.to_h]
144-
@internal_representation = nil
145-
nil
162+
@validation_errors << err.to_h
163+
false
146164
end
147165

166+
# Given that we _could_ execute this query, _should_ we?
167+
# - Does it violate any query analyzers?
168+
def query_valid?
169+
@analysis_errors = begin
170+
if @query_analyzers.any?
171+
reduce_results = GraphQL::Analysis.analyze_query(self, @query_analyzers)
172+
reduce_results.select { |r| r.is_a?(GraphQL::AnalysisError) }.map(&:to_h)
173+
else
174+
[]
175+
end
176+
end
177+
@analysis_errors.none?
178+
end
148179

149180
def find_operation(operations, operation_name)
150181
if operations.length == 1
@@ -157,19 +188,5 @@ def find_operation(operations, operation_name)
157188
operations[operation_name]
158189
end
159190
end
160-
161-
def analysis_errors
162-
@analysis_errors ||= begin
163-
if validation_errors.any?
164-
# Can't reduce an invalid query
165-
[]
166-
elsif @query_analyzers.any?
167-
reduce_results = GraphQL::Analysis.analyze_query(self, @query_analyzers)
168-
reduce_results.select { |r| r.is_a?(GraphQL::AnalysisError) }.map(&:to_h)
169-
else
170-
[]
171-
end
172-
end
173-
end
174191
end
175192
end

spec/graphql/static_validation/rules/fragment_types_exist_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"}
2121

2222
let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, rules: [GraphQL::StaticValidation::FragmentTypesExist]) }
23-
let(:query) { GraphQL::Query.new(DummySchema, query_string) }
23+
let(:query) { GraphQL::Query.new(DummySchema, query_string, validate: false) }
2424
let(:errors) { validator.validate(query)[:errors] }
2525

2626
it "finds non-existent types on fragments" do

spec/graphql/static_validation/rules/fragments_are_used_spec.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@
3535
let(:query_string) {%|
3636
# I am a comment.
3737
|}
38-
let(:document) { GraphQL.parse(query_string) }
39-
let(:result) { DummySchema.execute(document: document) }
38+
let(:result) { DummySchema.execute(query_string) }
4039
it "handles them gracefully" do
41-
assert_equal nil, result
40+
assert_equal({}, result)
4241
end
4342
end
4443
end

0 commit comments

Comments
 (0)