Skip to content
This repository was archived by the owner on Jul 25, 2024. It is now read-only.

Commit 7bc41a5

Browse files
aweikerJosh Price
authored andcommitted
Validate operation name matches an operation (#93)
Validate operation name matches an operation Refactored for clarity. Paired with @eoinkelly
1 parent 804a9bd commit 7bc41a5

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed

lib/graphql/execution/execution_context.ex

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,48 @@ defmodule GraphQL.Execution.ExecutionContext do
1313

1414
@spec new(GraphQL.Schema.t, GraphQL.Document.t, map, map, String.t) :: __MODULE__.t
1515
def new(schema, document, root_value, variable_values, operation_name) do
16-
Enum.reduce document.definitions, %__MODULE__{
16+
17+
initial_context = %__MODULE__{
1718
schema: schema,
1819
fragments: %{},
1920
root_value: root_value,
2021
operation: nil,
2122
variable_values: variable_values || %{},
2223
errors: []
23-
}, fn(definition, context) ->
24-
25-
case definition do
26-
%{kind: :OperationDefinition} ->
27-
cond do
28-
!operation_name && context.operation ->
29-
report_error(context, "Must provide operation name if query contains multiple operations.")
30-
!operation_name || definition.name.value === operation_name ->
31-
context = %{context | operation: definition}
32-
%{context | variable_values: GraphQL.Execution.Variables.extract(context) }
33-
true -> context
34-
end
35-
%{kind: :FragmentDefinition} ->
36-
put_in(context.fragments[definition.name.value], definition)
37-
end
24+
}
25+
26+
document.definitions
27+
|> Enum.reduce(initial_context, build_definition_handler(operation_name))
28+
|> validate_operation_exists(operation_name)
29+
end
30+
31+
defp build_definition_handler(operation_name) do
32+
fn(definition, context) -> handle_definition(operation_name, definition, context) end
33+
end
34+
35+
defp handle_definition(operation_name, definition = %{kind: :OperationDefinition}, context) do
36+
multiple_operations_no_operation_name = !operation_name && context.operation
37+
should_set_operation = !operation_name || definition.name.value === operation_name
38+
cond do
39+
multiple_operations_no_operation_name ->
40+
report_error(context, "Must provide operation name if query contains multiple operations.")
41+
should_set_operation ->
42+
context = %{context | operation: definition}
43+
%{context | variable_values: GraphQL.Execution.Variables.extract(context) }
44+
true -> context
3845
end
3946
end
4047

48+
defp handle_definition(_, definition = %{kind: :FragmentDefinition}, context) do
49+
put_in(context.fragments[definition.name.value], definition)
50+
end
51+
52+
defp validate_operation_exists(context, nil), do: context
53+
defp validate_operation_exists(context = %{operation: nil}, operation_name) do
54+
report_error(context, "Operation `#{operation_name}` not found in query.")
55+
end
56+
defp validate_operation_exists(context, _operation_name), do: context
57+
4158
@spec report_error(__MODULE__.t, String.t) :: __MODULE__.t
4259
def report_error(context, msg) do
4360
put_in(context.errors, [%{"message" => msg} | context.errors])

lib/graphql/execution/executor.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ defmodule GraphQL.Execution.Executor do
1616
"""
1717
@spec execute(GraphQL.Schema.t, GraphQL.Document.t, list) :: result_data | {:error, %{errors: list}}
1818
def execute(schema, document, opts \\ []) do
19-
schema = Schema.with_type_cache(schema)
19+
schema = Schema.with_type_cache(schema)
2020
{root_value, variable_values, operation_name} = expand_options(opts)
2121
context = ExecutionContext.new(schema, document, root_value, variable_values, operation_name)
22+
2223
case context.errors do
2324
[] -> execute_operation(context, context.operation, root_value)
2425
_ -> {:error, %{errors: Enum.dedup(context.errors)}}

test/graphql/execution/executor_test.exs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ defmodule GraphQL.Execution.Executor.ExecutorTest do
158158
assert_has_error(result, %{message: "Must provide operation name if query contains multiple operations."})
159159
end
160160

161+
test "operation name must match an operation defined in the query" do
162+
{_, result} = execute(TestSchema.schema, "query a {greeting}", operation_name: "b")
163+
assert_has_error(result, %{message: "Operation `b` not found in query."})
164+
end
165+
161166
test "do not include illegal fields in output" do
162167
{:ok, result} = execute(TestSchema.schema, ~S[query Q {g, h(name:"Joe")}], validate: false)
163168
assert_data(result, %{})

0 commit comments

Comments
 (0)