graphql 1.9.0.pre1 → 1.9.0.pre2
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/graphql.rb +5 -1
- data/lib/graphql/analysis/ast/analyzer.rb +1 -1
- data/lib/graphql/analysis/ast/visitor.rb +6 -1
- data/lib/graphql/backwards_compatibility.rb +1 -1
- data/lib/graphql/dig.rb +19 -0
- data/lib/graphql/directive.rb +13 -1
- data/lib/graphql/directive/include_directive.rb +1 -7
- data/lib/graphql/directive/skip_directive.rb +1 -8
- data/lib/graphql/execution/interpreter.rb +23 -13
- data/lib/graphql/execution/interpreter/resolve.rb +56 -0
- data/lib/graphql/execution/interpreter/runtime.rb +174 -74
- data/lib/graphql/execution/lazy.rb +7 -1
- data/lib/graphql/execution/lookahead.rb +71 -6
- data/lib/graphql/execution_error.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +5 -1
- data/lib/graphql/introspection/type_type.rb +4 -4
- data/lib/graphql/language.rb +0 -1
- data/lib/graphql/language/block_string.rb +37 -0
- data/lib/graphql/language/document_from_schema_definition.rb +1 -1
- data/lib/graphql/language/lexer.rb +55 -36
- data/lib/graphql/language/lexer.rl +8 -3
- data/lib/graphql/language/nodes.rb +27 -4
- data/lib/graphql/language/parser.rb +55 -55
- data/lib/graphql/language/parser.y +11 -11
- data/lib/graphql/language/printer.rb +1 -1
- data/lib/graphql/language/visitor.rb +22 -13
- data/lib/graphql/literal_validation_error.rb +6 -0
- data/lib/graphql/query.rb +13 -0
- data/lib/graphql/query/arguments.rb +2 -1
- data/lib/graphql/query/context.rb +3 -10
- data/lib/graphql/query/executor.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/relay/connection_resolve.rb +1 -1
- data/lib/graphql/relay/relation_connection.rb +1 -1
- data/lib/graphql/schema.rb +81 -11
- data/lib/graphql/schema/argument.rb +1 -1
- data/lib/graphql/schema/build_from_definition.rb +2 -4
- data/lib/graphql/schema/directive.rb +103 -0
- data/lib/graphql/schema/directive/feature.rb +66 -0
- data/lib/graphql/schema/directive/include.rb +25 -0
- data/lib/graphql/schema/directive/skip.rb +25 -0
- data/lib/graphql/schema/directive/transform.rb +48 -0
- data/lib/graphql/schema/enum_value.rb +2 -2
- data/lib/graphql/schema/field.rb +63 -17
- data/lib/graphql/schema/input_object.rb +1 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +4 -2
- data/lib/graphql/schema/member/build_type.rb +33 -1
- data/lib/graphql/schema/member/has_fields.rb +8 -73
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -1
- data/lib/graphql/schema/resolver.rb +1 -1
- data/lib/graphql/static_validation.rb +2 -1
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +25 -10
- data/lib/graphql/static_validation/definition_dependencies.rb +3 -3
- data/lib/graphql/static_validation/{message.rb → error.rb} +11 -11
- data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
- data/lib/graphql/static_validation/literal_validator.rb +54 -11
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +34 -5
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +31 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +2 -2
- data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +7 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +35 -0
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -1
- data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +11 -2
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +11 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +24 -6
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +32 -0
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +5 -1
- data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -1
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +5 -1
- data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -1
- data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +4 -1
- data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +5 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +13 -3
- data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +4 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +2 -2
- data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +9 -2
- data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +7 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +47 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +4 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +4 -3
- data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +20 -6
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +5 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +18 -2
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
- data/lib/graphql/static_validation/validator.rb +24 -14
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -2
- data/lib/graphql/tracing/skylight_tracing.rb +2 -2
- data/lib/graphql/unauthorized_field_error.rb +23 -0
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/analysis/ast_spec.rb +40 -0
- data/spec/graphql/authorization_spec.rb +93 -20
- data/spec/graphql/base_type_spec.rb +3 -1
- data/spec/graphql/execution/interpreter_spec.rb +127 -4
- data/spec/graphql/execution/lazy_spec.rb +49 -0
- data/spec/graphql/execution/lookahead_spec.rb +113 -21
- data/spec/graphql/execution/multiplex_spec.rb +2 -1
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/lexer_spec.rb +72 -3
- data/spec/graphql/language/printer_spec.rb +18 -6
- data/spec/graphql/query/arguments_spec.rb +21 -0
- data/spec/graphql/query/context_spec.rb +10 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +144 -29
- data/spec/graphql/schema/directive/feature_spec.rb +81 -0
- data/spec/graphql/schema/directive/transform_spec.rb +39 -0
- data/spec/graphql/schema/enum_spec.rb +5 -3
- data/spec/graphql/schema/field_extension_spec.rb +3 -3
- data/spec/graphql/schema/field_spec.rb +19 -0
- data/spec/graphql/schema/input_object_spec.rb +81 -0
- data/spec/graphql/schema/member/build_type_spec.rb +46 -0
- data/spec/graphql/schema/member/scoped_spec.rb +3 -3
- data/spec/graphql/schema/printer_spec.rb +244 -96
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +26 -0
- data/spec/graphql/schema/resolver_spec.rb +1 -1
- data/spec/graphql/schema/warden_spec.rb +35 -11
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +212 -72
- data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +72 -29
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +22 -2
- data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +13 -4
- data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +58 -0
- data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +14 -7
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +14 -7
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
- data/spec/graphql/static_validation/validator_spec.rb +6 -4
- data/spec/graphql/tracing/new_relic_tracing_spec.rb +10 -0
- data/spec/graphql/tracing/skylight_tracing_spec.rb +10 -0
- data/spec/graphql/types/iso_8601_date_time_spec.rb +1 -2
- data/spec/integration/mongoid/star_trek/schema.rb +5 -5
- data/spec/integration/rails/graphql/relay/relation_connection_spec.rb +37 -8
- data/spec/integration/rails/graphql/schema_spec.rb +2 -2
- data/spec/integration/rails/spec_helper.rb +10 -0
- data/spec/integration/tmp/app/graphql/types/bird_type.rb +7 -0
- data/spec/integration/tmp/dummy/Gemfile +45 -0
- data/spec/integration/tmp/dummy/README.rdoc +28 -0
- data/spec/integration/tmp/dummy/Rakefile +6 -0
- data/spec/integration/tmp/dummy/app/assets/javascripts/application.js +16 -0
- data/spec/integration/tmp/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/integration/tmp/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/integration/tmp/dummy/app/controllers/graphql_controller.rb +43 -0
- data/spec/integration/tmp/dummy/app/graphql/dummy_schema.rb +34 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_enum.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_input_object.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_interface.rb +5 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_object.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_scalar.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_union.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/mutation_type.rb +10 -0
- data/spec/integration/tmp/dummy/app/graphql/types/query_type.rb +15 -0
- data/spec/integration/tmp/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/integration/tmp/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/integration/tmp/dummy/bin/bundle +3 -0
- data/spec/integration/tmp/dummy/bin/rails +4 -0
- data/spec/integration/tmp/dummy/bin/rake +4 -0
- data/spec/integration/tmp/dummy/bin/setup +29 -0
- data/spec/integration/tmp/dummy/config.ru +4 -0
- data/spec/integration/tmp/dummy/config/application.rb +32 -0
- data/spec/integration/tmp/dummy/config/boot.rb +3 -0
- data/spec/integration/tmp/dummy/config/environment.rb +5 -0
- data/spec/integration/tmp/dummy/config/environments/development.rb +38 -0
- data/spec/integration/tmp/dummy/config/environments/production.rb +76 -0
- data/spec/integration/tmp/dummy/config/environments/test.rb +42 -0
- data/spec/integration/tmp/dummy/config/initializers/assets.rb +11 -0
- data/spec/integration/tmp/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/integration/tmp/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/integration/tmp/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/integration/tmp/dummy/config/initializers/inflections.rb +16 -0
- data/spec/integration/tmp/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/integration/tmp/dummy/config/initializers/session_store.rb +3 -0
- data/spec/integration/tmp/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
- data/spec/integration/tmp/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/spec/integration/tmp/dummy/config/locales/en.yml +23 -0
- data/spec/integration/tmp/dummy/config/routes.rb +61 -0
- data/spec/integration/tmp/dummy/config/secrets.yml +22 -0
- data/spec/integration/tmp/dummy/db/seeds.rb +7 -0
- data/spec/integration/tmp/dummy/public/404.html +67 -0
- data/spec/integration/tmp/dummy/public/422.html +67 -0
- data/spec/integration/tmp/dummy/public/500.html +66 -0
- data/spec/integration/tmp/dummy/public/favicon.ico +0 -0
- data/spec/integration/tmp/dummy/public/robots.txt +5 -0
- data/spec/support/dummy/schema.rb +2 -2
- data/spec/support/error_bubbling_helpers.rb +23 -0
- data/spec/support/jazz.rb +53 -6
- data/spec/support/lazy_helpers.rb +26 -8
- data/spec/support/new_relic.rb +3 -0
- data/spec/support/skylight.rb +3 -0
- data/spec/support/star_wars/schema.rb +13 -9
- data/spec/support/static_validation_helpers.rb +3 -1
- metadata +145 -22
- data/lib/graphql/language/comments.rb +0 -45
- data/spec/graphql/schema/member/has_fields_spec.rb +0 -132
- data/spec/integration/tmp/app/graphql/types/family_type.rb +0 -9
@@ -17,8 +17,8 @@ module GraphQL
|
|
17
17
|
# @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
|
18
18
|
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
|
19
19
|
# It can also be specified per-query with `context[:set_new_relic_transaction_name]`.
|
20
|
-
def initialize(
|
21
|
-
@set_transaction_name = set_transaction_name
|
20
|
+
def initialize(options = {})
|
21
|
+
@set_transaction_name = options.fetch(:set_transaction_name, false)
|
22
22
|
super
|
23
23
|
end
|
24
24
|
|
@@ -17,8 +17,8 @@ module GraphQL
|
|
17
17
|
# @param set_endpoint_name [Boolean] If true, the GraphQL operation name will be used as the endpoint name.
|
18
18
|
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
|
19
19
|
# It can also be specified per-query with `context[:set_skylight_endpoint_name]`.
|
20
|
-
def initialize(
|
21
|
-
@set_endpoint_name = set_endpoint_name
|
20
|
+
def initialize(options = {})
|
21
|
+
@set_endpoint_name = options.fetch(:set_endpoint_name, false)
|
22
22
|
super
|
23
23
|
end
|
24
24
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class UnauthorizedFieldError < GraphQL::UnauthorizedError
|
4
|
+
# @return [Field] the field that failed the authorization check
|
5
|
+
attr_reader :field
|
6
|
+
|
7
|
+
def initialize(message = nil, object: nil, type: nil, context: nil, field: nil)
|
8
|
+
if message.nil? && [field, type].any?(&:nil?)
|
9
|
+
raise ArgumentError, "#{self.class.name} requires either a message or keywords"
|
10
|
+
end
|
11
|
+
|
12
|
+
@field = field
|
13
|
+
message ||= begin
|
14
|
+
if object
|
15
|
+
"An instance of #{object.class} failed #{type.name}'s authorization check on field #{field.name}"
|
16
|
+
else
|
17
|
+
"Failed #{type.name}'s authorization check on field #{field.name}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
super(message, object: object, type: type, context: context)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/graphql/version.rb
CHANGED
@@ -61,6 +61,16 @@ describe GraphQL::Analysis::AST do
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
class AstPreviousField < GraphQL::Analysis::AST::Analyzer
|
65
|
+
def on_enter_field(node, parent, visitor)
|
66
|
+
@previous_field = visitor.previous_field_definition
|
67
|
+
end
|
68
|
+
|
69
|
+
def result
|
70
|
+
@previous_field
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
64
74
|
describe "using the AST analysis engine" do
|
65
75
|
let(:schema) do
|
66
76
|
query_type = Class.new(GraphQL::Schema::Object) do
|
@@ -77,6 +87,7 @@ describe GraphQL::Analysis::AST do
|
|
77
87
|
query query_type
|
78
88
|
use GraphQL::Analysis::AST
|
79
89
|
query_analyzer AstErrorAnalyzer
|
90
|
+
use GraphQL::Execution::Interpreter
|
80
91
|
end
|
81
92
|
end
|
82
93
|
|
@@ -89,7 +100,26 @@ describe GraphQL::Analysis::AST do
|
|
89
100
|
let(:query) { GraphQL::Query.new(schema, query_string, variables: {}) }
|
90
101
|
|
91
102
|
it "runs the AST analyzers correctly" do
|
103
|
+
res = query.result
|
104
|
+
refute res.key?("data")
|
105
|
+
assert_equal ["An Error!"], res["errors"].map { |e| e["message"] }
|
106
|
+
end
|
107
|
+
|
108
|
+
it "skips rewrite" do
|
109
|
+
# Try running the query:
|
92
110
|
query.result
|
111
|
+
# But the validation step doesn't build an irep_node tree
|
112
|
+
assert_nil query.irep_selection
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "when validate: false" do
|
116
|
+
let(:query) { GraphQL::Query.new(schema, query_string, validate: false) }
|
117
|
+
it "Skips rewrite" do
|
118
|
+
# Try running the query:
|
119
|
+
query.result
|
120
|
+
# But the validation step doesn't build an irep_node tree
|
121
|
+
assert_nil query.irep_selection
|
122
|
+
end
|
93
123
|
end
|
94
124
|
end
|
95
125
|
|
@@ -127,6 +157,16 @@ describe GraphQL::Analysis::AST do
|
|
127
157
|
assert_equal 2, reduce_result.size
|
128
158
|
end
|
129
159
|
end
|
160
|
+
|
161
|
+
describe "Visitor#previous_field_definition" do
|
162
|
+
let(:analyzers) { [AstPreviousField] }
|
163
|
+
let(:query) { GraphQL::Query.new(Dummy::Schema, "{ __schema { types { name } } }") }
|
164
|
+
|
165
|
+
it "it runs the analyzer" do
|
166
|
+
prev_field = reduce_result.first
|
167
|
+
assert_equal "__Schema.types", prev_field.metadata[:type_class].path
|
168
|
+
end
|
169
|
+
end
|
130
170
|
end
|
131
171
|
|
132
172
|
it "calls the defined analyzers" do
|
@@ -48,7 +48,7 @@ describe GraphQL::Authorization do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def authorized?(object, context)
|
51
|
-
super && object != :hide
|
51
|
+
super && object != :hide && object != :replace
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -159,7 +159,7 @@ describe GraphQL::Authorization do
|
|
159
159
|
super && value != "a"
|
160
160
|
end
|
161
161
|
|
162
|
-
field :value, String, null: false, method: :
|
162
|
+
field :value, String, null: false, method: :itself
|
163
163
|
end
|
164
164
|
|
165
165
|
module UnauthorizedInterface
|
@@ -186,7 +186,7 @@ describe GraphQL::Authorization do
|
|
186
186
|
Box.new(value: Box.new(value: Box.new(value: Box.new(value: is_authed))))
|
187
187
|
end
|
188
188
|
|
189
|
-
field :value, String, null: false, method: :
|
189
|
+
field :value, String, null: false, method: :itself
|
190
190
|
end
|
191
191
|
|
192
192
|
class IntegerObject < BaseObject
|
@@ -197,7 +197,7 @@ describe GraphQL::Authorization do
|
|
197
197
|
is_allowed = !(ctx[:unauthorized_relay] || obj == ctx[:exclude_integer])
|
198
198
|
Box.new(value: Box.new(value: is_allowed))
|
199
199
|
end
|
200
|
-
field :value, Integer, null: false, method: :
|
200
|
+
field :value, Integer, null: false, method: :itself
|
201
201
|
end
|
202
202
|
|
203
203
|
class IntegerObjectEdge < GraphQL::Types::Relay::BaseEdge
|
@@ -237,7 +237,7 @@ describe GraphQL::Authorization do
|
|
237
237
|
|
238
238
|
class Query < BaseObject
|
239
239
|
field :hidden, Integer, null: false
|
240
|
-
field :unauthorized, Integer, null: true, method: :
|
240
|
+
field :unauthorized, Integer, null: true, method: :itself
|
241
241
|
field :int2, Integer, null: true do
|
242
242
|
argument :int, Integer, required: false
|
243
243
|
argument :hidden, Integer, required: false
|
@@ -268,22 +268,22 @@ describe GraphQL::Authorization do
|
|
268
268
|
end
|
269
269
|
|
270
270
|
def empty_array; []; end
|
271
|
-
field :hidden_object, HiddenObject, null: false,
|
272
|
-
field :hidden_interface, HiddenInterface, null: false,
|
273
|
-
field :hidden_default_interface, HiddenDefaultInterface, null: false,
|
274
|
-
field :hidden_connection, RelayObject.connection_type, null: :false,
|
275
|
-
field :hidden_edge, RelayObject.edge_type, null: :false,
|
271
|
+
field :hidden_object, HiddenObject, null: false, resolver_method: :itself
|
272
|
+
field :hidden_interface, HiddenInterface, null: false, resolver_method: :itself
|
273
|
+
field :hidden_default_interface, HiddenDefaultInterface, null: false, resolver_method: :itself
|
274
|
+
field :hidden_connection, RelayObject.connection_type, null: :false, resolver_method: :empty_array
|
275
|
+
field :hidden_edge, RelayObject.edge_type, null: :false, resolver_method: :edge_object
|
276
276
|
|
277
277
|
field :inaccessible, Integer, null: false, method: :object_id
|
278
|
-
field :inaccessible_object, InaccessibleObject, null: false,
|
279
|
-
field :inaccessible_interface, InaccessibleInterface, null: false,
|
280
|
-
field :inaccessible_default_interface, InaccessibleDefaultInterface, null: false,
|
281
|
-
field :inaccessible_connection, RelayObject.connection_type, null: :false,
|
282
|
-
field :inaccessible_edge, RelayObject.edge_type, null: :false,
|
278
|
+
field :inaccessible_object, InaccessibleObject, null: false, resolver_method: :itself
|
279
|
+
field :inaccessible_interface, InaccessibleInterface, null: false, resolver_method: :itself
|
280
|
+
field :inaccessible_default_interface, InaccessibleDefaultInterface, null: false, resolver_method: :itself
|
281
|
+
field :inaccessible_connection, RelayObject.connection_type, null: :false, resolver_method: :empty_array
|
282
|
+
field :inaccessible_edge, RelayObject.edge_type, null: :false, resolver_method: :edge_object
|
283
283
|
|
284
|
-
field :unauthorized_object, UnauthorizedObject, null: true,
|
285
|
-
field :unauthorized_connection, RelayObject.connection_type, null: false,
|
286
|
-
field :unauthorized_edge, RelayObject.edge_type, null: false,
|
284
|
+
field :unauthorized_object, UnauthorizedObject, null: true, resolver_method: :itself
|
285
|
+
field :unauthorized_connection, RelayObject.connection_type, null: false, resolver_method: :array_with_item
|
286
|
+
field :unauthorized_edge, RelayObject.edge_type, null: false, resolver_method: :edge_object
|
287
287
|
|
288
288
|
def edge_object
|
289
289
|
OpenStruct.new(node: 100)
|
@@ -305,11 +305,11 @@ describe GraphQL::Authorization do
|
|
305
305
|
[self, self]
|
306
306
|
end
|
307
307
|
|
308
|
-
field :unauthorized_lazy_check_box, UnauthorizedCheckBox, null: true,
|
308
|
+
field :unauthorized_lazy_check_box, UnauthorizedCheckBox, null: true, resolver_method: :unauthorized_lazy_box do
|
309
309
|
argument :value, String, required: true
|
310
310
|
end
|
311
311
|
|
312
|
-
field :unauthorized_interface, UnauthorizedInterface, null: true,
|
312
|
+
field :unauthorized_interface, UnauthorizedInterface, null: true, resolver_method: :unauthorized_lazy_box do
|
313
313
|
argument :value, String, required: true
|
314
314
|
end
|
315
315
|
|
@@ -383,6 +383,8 @@ describe GraphQL::Authorization do
|
|
383
383
|
def self.unauthorized_object(err)
|
384
384
|
if err.object.respond_to?(:replacement)
|
385
385
|
err.object.replacement
|
386
|
+
elsif err.object == :replace
|
387
|
+
33
|
386
388
|
else
|
387
389
|
raise GraphQL::ExecutionError, "Unauthorized #{err.type.graphql_name}: #{err.object}"
|
388
390
|
end
|
@@ -390,6 +392,18 @@ describe GraphQL::Authorization do
|
|
390
392
|
|
391
393
|
# use GraphQL::Backtrace
|
392
394
|
end
|
395
|
+
|
396
|
+
class SchemaWithFieldHook < GraphQL::Schema
|
397
|
+
query(Query)
|
398
|
+
|
399
|
+
def self.unauthorized_field(err)
|
400
|
+
if err.object == :replace
|
401
|
+
42
|
402
|
+
else
|
403
|
+
raise GraphQL::ExecutionError, "Unauthorized field #{err.field.graphql_name} on #{err.type.graphql_name}: #{err.object}"
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
393
407
|
end
|
394
408
|
|
395
409
|
def auth_execute(*args)
|
@@ -621,6 +635,65 @@ describe GraphQL::Authorization do
|
|
621
635
|
end
|
622
636
|
end
|
623
637
|
|
638
|
+
describe "field level authorization" do
|
639
|
+
describe "unauthorized field" do
|
640
|
+
describe "with an unauthorized field hook configured" do
|
641
|
+
describe "when the hook returns a value" do
|
642
|
+
it "replaces the response with the return value of the unauthorized field hook" do
|
643
|
+
query = "{ unauthorized }"
|
644
|
+
response = AuthTest::SchemaWithFieldHook.execute(query, root_value: :replace)
|
645
|
+
assert_equal 42, response["data"].fetch("unauthorized")
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
describe "when the field hook raises an error" do
|
650
|
+
it "returns nil" do
|
651
|
+
query = "{ unauthorized }"
|
652
|
+
response = AuthTest::SchemaWithFieldHook.execute(query, root_value: :hide)
|
653
|
+
assert_nil response["data"].fetch("unauthorized")
|
654
|
+
end
|
655
|
+
|
656
|
+
it "adds the error to the errors key" do
|
657
|
+
query = "{ unauthorized }"
|
658
|
+
response = AuthTest::SchemaWithFieldHook.execute(query, root_value: :hide)
|
659
|
+
assert_equal ["Unauthorized field unauthorized on Query: hide"], response["errors"].map { |e| e["message"] }
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
describe "with an unauthorized field hook not configured" do
|
665
|
+
describe "When the object hook replaces the field" do
|
666
|
+
it "delegates to the unauthorized object hook, which replaces the object" do
|
667
|
+
query = "{ unauthorized }"
|
668
|
+
response = AuthTest::Schema.execute(query, root_value: :replace)
|
669
|
+
assert_equal 33, response["data"].fetch("unauthorized")
|
670
|
+
end
|
671
|
+
end
|
672
|
+
describe "When the object hook raises an error" do
|
673
|
+
it "returns nil" do
|
674
|
+
query = "{ unauthorized }"
|
675
|
+
response = AuthTest::Schema.execute(query, root_value: :hide)
|
676
|
+
assert_nil response["data"].fetch("unauthorized")
|
677
|
+
end
|
678
|
+
|
679
|
+
it "adds the error to the errors key" do
|
680
|
+
query = "{ unauthorized }"
|
681
|
+
response = AuthTest::Schema.execute(query, root_value: :hide)
|
682
|
+
assert_equal ["Unauthorized Query: hide"], response["errors"].map { |e| e["message"] }
|
683
|
+
end
|
684
|
+
end
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
describe "authorized field" do
|
689
|
+
it "returns the field data" do
|
690
|
+
query = "{ unauthorized }"
|
691
|
+
response = AuthTest::SchemaWithFieldHook.execute(query, root_value: 1)
|
692
|
+
assert_equal 1, response["data"].fetch("unauthorized")
|
693
|
+
end
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
624
697
|
it "halts on unauthorized fields, using the parent object" do
|
625
698
|
query = "{ unauthorized }"
|
626
699
|
hidden_response = auth_execute(query, root_value: :hide)
|
@@ -4,10 +4,17 @@ require "spec_helper"
|
|
4
4
|
describe GraphQL::Execution::Interpreter do
|
5
5
|
module InterpreterTest
|
6
6
|
class Box
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(value:)
|
7
|
+
def initialize(value: nil, &block)
|
10
8
|
@value = value
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def value
|
13
|
+
if @block
|
14
|
+
@value = @block.call
|
15
|
+
@block = nil
|
16
|
+
end
|
17
|
+
@value
|
11
18
|
end
|
12
19
|
end
|
13
20
|
|
@@ -17,12 +24,25 @@ describe GraphQL::Execution::Interpreter do
|
|
17
24
|
field :name, String, null: false
|
18
25
|
field :cards, ["InterpreterTest::Card"], null: false
|
19
26
|
|
27
|
+
def self.authorized?(expansion, ctx)
|
28
|
+
if expansion.sym == "NOPE"
|
29
|
+
false
|
30
|
+
else
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
20
35
|
def cards
|
21
36
|
Query::CARDS.select { |c| c.expansion_sym == @object.sym }
|
22
37
|
end
|
23
38
|
|
24
39
|
def lazy_sym
|
25
|
-
Box.new(value: sym)
|
40
|
+
Box.new(value: object.sym)
|
41
|
+
end
|
42
|
+
|
43
|
+
field :null_union_field_test, Integer, null: false
|
44
|
+
def null_union_field_test
|
45
|
+
1
|
26
46
|
end
|
27
47
|
end
|
28
48
|
|
@@ -34,6 +54,11 @@ describe GraphQL::Execution::Interpreter do
|
|
34
54
|
def expansion
|
35
55
|
Query::EXPANSIONS.find { |e| e.sym == @object.expansion_sym }
|
36
56
|
end
|
57
|
+
|
58
|
+
field :null_union_field_test, Integer, null: true
|
59
|
+
def null_union_field_test
|
60
|
+
nil
|
61
|
+
end
|
37
62
|
end
|
38
63
|
|
39
64
|
class Color < GraphQL::Schema::Enum
|
@@ -59,6 +84,7 @@ describe GraphQL::Execution::Interpreter do
|
|
59
84
|
field :calls, Integer, null: false do
|
60
85
|
argument :expected, Integer, required: true
|
61
86
|
end
|
87
|
+
|
62
88
|
def calls(expected:)
|
63
89
|
c = context[:calls] += 1
|
64
90
|
if c != expected
|
@@ -67,9 +93,26 @@ describe GraphQL::Execution::Interpreter do
|
|
67
93
|
c
|
68
94
|
end
|
69
95
|
end
|
96
|
+
|
97
|
+
field :runtime_info, String, null: false
|
98
|
+
def runtime_info
|
99
|
+
"#{context.namespace(:interpreter)[:current_path]} -> #{context.namespace(:interpreter)[:current_field].path}"
|
100
|
+
end
|
101
|
+
|
102
|
+
field :lazy_runtime_info, String, null: false
|
103
|
+
def lazy_runtime_info
|
104
|
+
Box.new {
|
105
|
+
"#{context.namespace(:interpreter)[:current_path]} -> #{context.namespace(:interpreter)[:current_field].path}"
|
106
|
+
}
|
107
|
+
end
|
70
108
|
end
|
71
109
|
|
72
110
|
class Query < GraphQL::Schema::Object
|
111
|
+
# Try a root-level authorized hook that returns a lazy value
|
112
|
+
def self.authorized?(obj, ctx)
|
113
|
+
Box.new(value: true)
|
114
|
+
end
|
115
|
+
|
73
116
|
field :card, Card, null: true do
|
74
117
|
argument :name, String, required: true
|
75
118
|
end
|
@@ -99,6 +142,8 @@ describe GraphQL::Execution::Interpreter do
|
|
99
142
|
OpenStruct.new(name: "Ravnica, City of Guilds", sym: "RAV"),
|
100
143
|
# This data has an error, for testing null propagation
|
101
144
|
OpenStruct.new(name: nil, sym: "XYZ"),
|
145
|
+
# This is not allowed by .authorized?,
|
146
|
+
OpenStruct.new(name: nil, sym: "NOPE"),
|
102
147
|
]
|
103
148
|
|
104
149
|
field :find, [Entity], null: false do
|
@@ -112,12 +157,21 @@ describe GraphQL::Execution::Interpreter do
|
|
112
157
|
end
|
113
158
|
end
|
114
159
|
|
160
|
+
field :findMany, [Entity, null: true], null: false do
|
161
|
+
argument :ids, [ID], required: true
|
162
|
+
end
|
163
|
+
|
164
|
+
def find_many(ids:)
|
165
|
+
find(id: ids).map { |e| Box.new(value: e) }
|
166
|
+
end
|
167
|
+
|
115
168
|
field :field_counter, FieldCounter, null: false
|
116
169
|
def field_counter; :field_counter; end
|
117
170
|
end
|
118
171
|
|
119
172
|
class Schema < GraphQL::Schema
|
120
173
|
use GraphQL::Execution::Interpreter
|
174
|
+
use GraphQL::Analysis::AST
|
121
175
|
query(Query)
|
122
176
|
lazy_resolve(Box, :value)
|
123
177
|
end
|
@@ -201,6 +255,26 @@ describe GraphQL::Execution::Interpreter do
|
|
201
255
|
end
|
202
256
|
end
|
203
257
|
|
258
|
+
describe "runtime info in context" do
|
259
|
+
it "is available" do
|
260
|
+
res = InterpreterTest::Schema.execute <<-GRAPHQL
|
261
|
+
{
|
262
|
+
fieldCounter {
|
263
|
+
runtimeInfo
|
264
|
+
fieldCounter {
|
265
|
+
runtimeInfo
|
266
|
+
lazyRuntimeInfo
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
GRAPHQL
|
271
|
+
|
272
|
+
assert_equal '["fieldCounter", "runtimeInfo"] -> FieldCounter.runtimeInfo', res["data"]["fieldCounter"]["runtimeInfo"]
|
273
|
+
assert_equal '["fieldCounter", "fieldCounter", "runtimeInfo"] -> FieldCounter.runtimeInfo', res["data"]["fieldCounter"]["fieldCounter"]["runtimeInfo"]
|
274
|
+
assert_equal '["fieldCounter", "fieldCounter", "lazyRuntimeInfo"] -> FieldCounter.lazyRuntimeInfo', res["data"]["fieldCounter"]["fieldCounter"]["lazyRuntimeInfo"]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
204
278
|
describe "CI setup" do
|
205
279
|
it "sets interpreter based on a constant" do
|
206
280
|
if TESTING_INTERPRETER
|
@@ -228,6 +302,7 @@ describe GraphQL::Execution::Interpreter do
|
|
228
302
|
# Although the expansion was found, its name of `nil`
|
229
303
|
# propagated to here
|
230
304
|
assert_nil res["data"].fetch("expansion")
|
305
|
+
assert_equal ["Cannot return null for non-nullable field Expansion.name"], res["errors"].map { |e| e["message"] }
|
231
306
|
end
|
232
307
|
|
233
308
|
it "propagates nulls in lists" do
|
@@ -245,6 +320,54 @@ describe GraphQL::Execution::Interpreter do
|
|
245
320
|
# A null in one of the list items removed the whole list
|
246
321
|
assert_nil(res["data"])
|
247
322
|
end
|
323
|
+
|
324
|
+
it "works with unions that fail .authorized?" do
|
325
|
+
res = InterpreterTest::Schema.execute <<-GRAPHQL
|
326
|
+
{
|
327
|
+
find(id: "NOPE") {
|
328
|
+
... on Expansion {
|
329
|
+
sym
|
330
|
+
}
|
331
|
+
}
|
332
|
+
}
|
333
|
+
GRAPHQL
|
334
|
+
assert_equal ["Cannot return null for non-nullable field Query.find"], res["errors"].map { |e| e["message"] }
|
335
|
+
end
|
336
|
+
|
337
|
+
it "works with lists of unions" do
|
338
|
+
res = InterpreterTest::Schema.execute <<-GRAPHQL
|
339
|
+
{
|
340
|
+
findMany(ids: ["RAV", "NOPE", "BOGUS"]) {
|
341
|
+
... on Expansion {
|
342
|
+
sym
|
343
|
+
}
|
344
|
+
}
|
345
|
+
}
|
346
|
+
GRAPHQL
|
347
|
+
|
348
|
+
assert_equal 3, res["data"]["findMany"].size
|
349
|
+
assert_equal "RAV", res["data"]["findMany"][0]["sym"]
|
350
|
+
assert_equal nil, res["data"]["findMany"][1]
|
351
|
+
assert_equal nil, res["data"]["findMany"][2]
|
352
|
+
assert_equal false, res.key?("errors")
|
353
|
+
end
|
354
|
+
|
355
|
+
it "works with union lists that have members of different kinds, with different nullabilities" do
|
356
|
+
res = InterpreterTest::Schema.execute <<-GRAPHQL
|
357
|
+
{
|
358
|
+
findMany(ids: ["RAV", "Dark Confidant"]) {
|
359
|
+
... on Expansion {
|
360
|
+
nullUnionFieldTest
|
361
|
+
}
|
362
|
+
... on Card {
|
363
|
+
nullUnionFieldTest
|
364
|
+
}
|
365
|
+
}
|
366
|
+
}
|
367
|
+
GRAPHQL
|
368
|
+
|
369
|
+
assert_equal [1, nil], res["data"]["findMany"].map { |f| f["nullUnionFieldTest"] }
|
370
|
+
end
|
248
371
|
end
|
249
372
|
|
250
373
|
describe "duplicated fields" do
|