graphql 1.8.0.pre6 → 1.8.0.pre7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +14 -0
- data/lib/graphql/internal_representation/node.rb +3 -20
- data/lib/graphql/language/parser.rb +590 -598
- data/lib/graphql/language/parser.y +1 -2
- data/lib/graphql/query/context.rb +26 -0
- data/lib/graphql/schema.rb +77 -22
- data/lib/graphql/schema/argument.rb +11 -3
- data/lib/graphql/schema/field.rb +50 -3
- data/lib/graphql/schema/input_object.rb +6 -0
- data/lib/graphql/schema/interface.rb +0 -1
- data/lib/graphql/schema/member.rb +2 -16
- data/lib/graphql/schema/member/has_fields.rb +4 -0
- data/lib/graphql/schema/object.rb +5 -4
- data/lib/graphql/schema/union.rb +0 -12
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -1
- data/lib/graphql/upgrader/member.rb +97 -10
- data/lib/graphql/version.rb +1 -1
- data/spec/fixtures/upgrader/starrable.original.rb +46 -0
- data/spec/fixtures/upgrader/starrable.transformed.rb +43 -0
- data/spec/fixtures/upgrader/subscribable.transformed.rb +32 -30
- data/spec/fixtures/upgrader/type_x.original.rb +14 -2
- data/spec/fixtures/upgrader/type_x.transformed.rb +12 -0
- data/spec/graphql/query/executor_spec.rb +2 -1
- data/spec/graphql/rake_task_spec.rb +3 -1
- data/spec/graphql/schema/field_spec.rb +94 -1
- data/spec/graphql/schema/input_object_spec.rb +6 -0
- data/spec/graphql/schema/interface_spec.rb +15 -0
- data/spec/graphql/schema/object_spec.rb +5 -3
- data/spec/graphql/schema/printer_spec.rb +19 -0
- data/spec/graphql/schema_spec.rb +10 -3
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -2
- data/spec/graphql/upgrader/member_spec.rb +2 -2
- data/spec/support/jazz.rb +21 -14
- data/spec/support/star_wars/schema.rb +1 -1
- metadata +7 -3
@@ -70,6 +70,32 @@ module GraphQL
|
|
70
70
|
def backtrace
|
71
71
|
GraphQL::Backtrace.new(self)
|
72
72
|
end
|
73
|
+
|
74
|
+
def execution_errors
|
75
|
+
@execution_errors ||= ExecutionErrors.new(self)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ExecutionErrors
|
80
|
+
def initialize(ctx)
|
81
|
+
@context = ctx
|
82
|
+
end
|
83
|
+
|
84
|
+
def add(err_or_msg)
|
85
|
+
err = case err_or_msg
|
86
|
+
when String
|
87
|
+
GraphQL::ExecutionError.new(err_or_msg)
|
88
|
+
when GraphQL::ExecutionError
|
89
|
+
err_or_msg
|
90
|
+
else
|
91
|
+
raise ArgumentError, "expected String or GraphQL::ExecutionError, not #{err_or_msg.class} (#{err_or_msg.inspect})"
|
92
|
+
end
|
93
|
+
# This will assign ast_node and path
|
94
|
+
@context.add_error(err)
|
95
|
+
end
|
96
|
+
|
97
|
+
alias :>> :add
|
98
|
+
alias :push :add
|
73
99
|
end
|
74
100
|
|
75
101
|
include SharedMethods
|
data/lib/graphql/schema.rb
CHANGED
@@ -613,7 +613,7 @@ module GraphQL
|
|
613
613
|
# @param except [<#call(member, ctx)>]
|
614
614
|
# @return [Hash] GraphQL result
|
615
615
|
def as_json(only: nil, except: nil, context: {})
|
616
|
-
execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context)
|
616
|
+
execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
|
617
617
|
end
|
618
618
|
|
619
619
|
# Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
|
@@ -625,19 +625,32 @@ module GraphQL
|
|
625
625
|
|
626
626
|
class << self
|
627
627
|
extend GraphQL::Delegate
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
628
|
+
# For compatibility, these methods all:
|
629
|
+
# - Cause the Schema instance to be created, if it hasn't been created yet
|
630
|
+
# - Delegate to that instance
|
631
|
+
# Eventually, the methods will be moved into this class, removing the need for the singleton.
|
632
|
+
def_delegators :graphql_definition,
|
633
|
+
# Schema structure
|
634
|
+
:as_json, :to_json, :to_document, :to_definition,
|
635
|
+
# Execution
|
636
|
+
:execute, :multiplex,
|
637
|
+
:static_validator, :introspection_system,
|
638
|
+
:query_analyzers, :middleware, :tracers, :instrumenters,
|
639
|
+
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
|
640
|
+
:validate, :multiplex_analyzers, :lazy?, :lazy_method_name,
|
641
|
+
# Configuration
|
642
|
+
:max_complexity=, :max_depth=,
|
643
|
+
:metadata,
|
644
|
+
:default_filter, :redefine,
|
645
|
+
:id_from_object_proc, :object_from_id_proc,
|
646
|
+
:id_from_object=, :object_from_id=, :type_error,
|
647
|
+
:remove_handler,
|
648
|
+
# Members
|
649
|
+
:types, :get_fields, :find,
|
650
|
+
:root_type_for_operation,
|
651
|
+
:union_memberships,
|
652
|
+
:get_field, :root_types, :references_to, :type_from_ast,
|
653
|
+
:possible_types, :get_field
|
641
654
|
|
642
655
|
def graphql_definition
|
643
656
|
@graphql_definition ||= to_graphql
|
@@ -653,6 +666,7 @@ module GraphQL
|
|
653
666
|
|
654
667
|
def to_graphql
|
655
668
|
schema_defn = self.new
|
669
|
+
schema_defn.raise_definition_error = true
|
656
670
|
schema_defn.query = query
|
657
671
|
schema_defn.mutation = mutation
|
658
672
|
schema_defn.subscription = subscription
|
@@ -660,16 +674,17 @@ module GraphQL
|
|
660
674
|
schema_defn.max_depth = max_depth
|
661
675
|
schema_defn.default_max_page_size = default_max_page_size
|
662
676
|
schema_defn.orphan_types = orphan_types
|
663
|
-
if !directives
|
664
|
-
directives(DIRECTIVES)
|
665
|
-
end
|
666
677
|
schema_defn.directives = directives
|
667
678
|
schema_defn.introspection_namespace = introspection
|
668
679
|
schema_defn.resolve_type = method(:resolve_type)
|
669
680
|
schema_defn.object_from_id = method(:object_from_id)
|
670
681
|
schema_defn.id_from_object = method(:id_from_object)
|
682
|
+
schema_defn.type_error = method(:type_error)
|
671
683
|
schema_defn.context_class = context_class
|
672
|
-
|
684
|
+
schema_defn.tracers.concat(defined_tracers)
|
685
|
+
schema_defn.query_analyzers.concat(defined_query_analyzers)
|
686
|
+
schema_defn.multiplex_analyzers.concat(defined_multiplex_analyzers)
|
687
|
+
defined_instrumenters.each do |step, insts|
|
673
688
|
insts.each do |inst|
|
674
689
|
schema_defn.instrumenters[step] << inst
|
675
690
|
end
|
@@ -678,6 +693,12 @@ module GraphQL
|
|
678
693
|
lazy_classes.each do |lazy_class, value_method|
|
679
694
|
schema_defn.lazy_methods.set(lazy_class, value_method)
|
680
695
|
end
|
696
|
+
if @rescues
|
697
|
+
@rescues.each do |err_class, handler|
|
698
|
+
schema_defn.rescue_from(err_class, &handler)
|
699
|
+
end
|
700
|
+
end
|
701
|
+
|
681
702
|
if plugins.any?
|
682
703
|
schema_plugins = plugins
|
683
704
|
# TODO don't depend on .define
|
@@ -776,6 +797,11 @@ module GraphQL
|
|
776
797
|
end
|
777
798
|
end
|
778
799
|
|
800
|
+
def rescue_from(err_class, &handler_block)
|
801
|
+
@rescues ||= {}
|
802
|
+
@rescues[err_class] = handler_block
|
803
|
+
end
|
804
|
+
|
779
805
|
def resolve_type(type, obj, ctx)
|
780
806
|
raise NotImplementedError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
|
781
807
|
end
|
@@ -788,6 +814,10 @@ module GraphQL
|
|
788
814
|
raise NotImplementedError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
|
789
815
|
end
|
790
816
|
|
817
|
+
def type_error(type_err, ctx)
|
818
|
+
DefaultTypeError.call(type_err, ctx)
|
819
|
+
end
|
820
|
+
|
791
821
|
def lazy_resolve(lazy_class, value_method)
|
792
822
|
lazy_classes[lazy_class] = value_method
|
793
823
|
end
|
@@ -798,14 +828,27 @@ module GraphQL
|
|
798
828
|
else
|
799
829
|
instrument_step
|
800
830
|
end
|
801
|
-
|
831
|
+
defined_instrumenters[step] << instrumenter
|
802
832
|
end
|
803
833
|
|
804
834
|
def directives(new_directives = nil)
|
805
835
|
if new_directives
|
806
836
|
@directives = new_directives.reduce({}) { |m, d| m[d.name] = d; m }
|
807
837
|
end
|
808
|
-
|
838
|
+
|
839
|
+
@directives ||= directives(DIRECTIVES)
|
840
|
+
end
|
841
|
+
|
842
|
+
def tracer(new_tracer)
|
843
|
+
defined_tracers << new_tracer
|
844
|
+
end
|
845
|
+
|
846
|
+
def query_analyzer(new_analyzer)
|
847
|
+
defined_query_analyzers << new_analyzer
|
848
|
+
end
|
849
|
+
|
850
|
+
def multiplex_analyzer(new_analyzer)
|
851
|
+
defined_multiplex_analyzers << new_analyzer
|
809
852
|
end
|
810
853
|
|
811
854
|
private
|
@@ -814,8 +857,20 @@ module GraphQL
|
|
814
857
|
@lazy_classes ||= {}
|
815
858
|
end
|
816
859
|
|
817
|
-
def
|
818
|
-
@
|
860
|
+
def defined_instrumenters
|
861
|
+
@defined_instrumenters ||= Hash.new { |h,k| h[k] = [] }
|
862
|
+
end
|
863
|
+
|
864
|
+
def defined_tracers
|
865
|
+
@defined_tracers ||= []
|
866
|
+
end
|
867
|
+
|
868
|
+
def defined_query_analyzers
|
869
|
+
@defined_query_analyzers ||= []
|
870
|
+
end
|
871
|
+
|
872
|
+
def defined_multiplex_analyzers
|
873
|
+
@defined_multiplex_analyzers ||= []
|
819
874
|
end
|
820
875
|
end
|
821
876
|
|
@@ -8,17 +8,25 @@ module GraphQL
|
|
8
8
|
|
9
9
|
attr_reader :name
|
10
10
|
|
11
|
-
|
11
|
+
# @param arg_name [Symbol]
|
12
|
+
# @param type_expr
|
13
|
+
# @param desc [String]
|
14
|
+
# @param required [Boolean] if true, this argument is non-null; if false, this argument is nullable
|
15
|
+
# @param description [String]
|
16
|
+
# @param default_value [Object]
|
17
|
+
# @param camelize [Boolean] if true, the name will be camelized when building the schema
|
18
|
+
def initialize(arg_name, type_expr, desc = nil, required:, description: nil, default_value: NO_DEFAULT, camelize: true)
|
12
19
|
@name = arg_name.to_s
|
13
20
|
@type_expr = type_expr
|
14
|
-
@description = desc
|
21
|
+
@description = desc || description
|
15
22
|
@null = !required
|
16
23
|
@default_value = default_value
|
24
|
+
@camelize = camelize
|
17
25
|
end
|
18
26
|
|
19
27
|
def to_graphql
|
20
28
|
argument = GraphQL::Argument.new
|
21
|
-
argument.name = Member::BuildType.camelize(@name)
|
29
|
+
argument.name = @camelize ? Member::BuildType.camelize(@name) : @name
|
22
30
|
argument.type = -> {
|
23
31
|
Member::BuildType.parse_type(@type_expr, null: @null)
|
24
32
|
}
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -19,7 +19,28 @@ module GraphQL
|
|
19
19
|
# @return [Symbol]
|
20
20
|
attr_reader :method
|
21
21
|
|
22
|
-
|
22
|
+
# @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
|
23
|
+
# @param return_type_expr [Class, GraphQL::BaseType, Array] The return type of this field
|
24
|
+
# @param desc [String] Field description
|
25
|
+
# @param null [Boolean] `true` if this field may return `null`, `false` if it is never `null`
|
26
|
+
# @param description [String] Field description
|
27
|
+
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
28
|
+
# @param method [Symbol] The method to call to resolve this field (defaults to `name`)
|
29
|
+
# @param hash_key [Object] The hash key to lookup to resolve this field (defaults to `name` or `name.to_s`)
|
30
|
+
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
31
|
+
# @param max_page_size [Integer] For connections, the maximum number of items to return from this field
|
32
|
+
# @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
|
33
|
+
# @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
|
34
|
+
# @param field [GraphQL::Field] **deprecated** for compatibility with <1.8.0
|
35
|
+
# @param function [GraphQL::Function] **deprecated** for compatibility with <1.8.0
|
36
|
+
# @param camelize [Boolean] If true, the field name will be camelized when building the schema
|
37
|
+
# @param complexity [Numeric] When provided, set the complexity for this field
|
38
|
+
def initialize(name, return_type_expr = nil, desc = nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, connection: nil, max_page_size: nil, resolve: nil, introspection: false, hash_key: nil, camelize: true, complexity: 1, extras: [], &definition_block)
|
39
|
+
if (field || function) && desc.nil? && return_type_expr.is_a?(String)
|
40
|
+
# The return type should be copied from `field` or `function`, and the second positional argument is the description
|
41
|
+
desc = return_type_expr
|
42
|
+
return_type_expr = nil
|
43
|
+
end
|
23
44
|
if !(field || function)
|
24
45
|
if return_type_expr.nil?
|
25
46
|
raise ArgumentError, "missing positional argument `type`"
|
@@ -40,7 +61,12 @@ module GraphQL
|
|
40
61
|
@function = function
|
41
62
|
@resolve = resolve
|
42
63
|
@deprecation_reason = deprecation_reason
|
64
|
+
if method && hash_key
|
65
|
+
raise ArgumentError, "Provide `method:` _or_ `hash_key:`, not both. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}`)"
|
66
|
+
end
|
43
67
|
@method = method
|
68
|
+
@hash_key = hash_key
|
69
|
+
@complexity = complexity
|
44
70
|
@return_type_expr = return_type_expr
|
45
71
|
@return_type_null = null
|
46
72
|
@connection = connection
|
@@ -48,6 +74,7 @@ module GraphQL
|
|
48
74
|
@introspection = introspection
|
49
75
|
@extras = extras
|
50
76
|
@arguments = {}
|
77
|
+
@camelize = camelize
|
51
78
|
|
52
79
|
if definition_block
|
53
80
|
instance_eval(&definition_block)
|
@@ -68,9 +95,28 @@ module GraphQL
|
|
68
95
|
end
|
69
96
|
end
|
70
97
|
|
98
|
+
def complexity(new_complexity)
|
99
|
+
case new_complexity
|
100
|
+
when Proc
|
101
|
+
if new_complexity.parameters.size != 3
|
102
|
+
fail(
|
103
|
+
"A complexity proc should always accept 3 parameters: ctx, args, child_complexity. "\
|
104
|
+
"E.g.: complexity ->(ctx, args, child_complexity) { child_complexity * args[:limit] }"
|
105
|
+
)
|
106
|
+
else
|
107
|
+
@complexity = new_complexity
|
108
|
+
end
|
109
|
+
when Numeric
|
110
|
+
@complexity = new_complexity
|
111
|
+
else
|
112
|
+
raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
71
117
|
# @return [GraphQL::Field]
|
72
118
|
def to_graphql
|
73
|
-
method_name = @method || Member::BuildType.underscore(@name)
|
119
|
+
method_name = @method || @hash_key || Member::BuildType.underscore(@name)
|
74
120
|
|
75
121
|
field_defn = if @field
|
76
122
|
@field.dup
|
@@ -80,7 +126,7 @@ module GraphQL
|
|
80
126
|
GraphQL::Field.new
|
81
127
|
end
|
82
128
|
|
83
|
-
field_defn.name = Member::BuildType.camelize(name)
|
129
|
+
field_defn.name = @camelize ? Member::BuildType.camelize(name) : name
|
84
130
|
if @return_type_expr
|
85
131
|
return_type_name = Member::BuildType.to_type_name(@return_type_expr)
|
86
132
|
connection = @connection.nil? ? return_type_name.end_with?("Connection") : @connection
|
@@ -116,6 +162,7 @@ module GraphQL
|
|
116
162
|
field_defn.connection = connection
|
117
163
|
field_defn.connection_max_page_size = @max_page_size
|
118
164
|
field_defn.introspection = @introspection
|
165
|
+
field_defn.complexity = @complexity
|
119
166
|
|
120
167
|
# apply this first, so it can be overriden below
|
121
168
|
if connection
|
@@ -9,6 +9,12 @@ module GraphQL
|
|
9
9
|
@context = context
|
10
10
|
end
|
11
11
|
|
12
|
+
# @return [GraphQL::Query::Context] The context for this query
|
13
|
+
attr_reader :context
|
14
|
+
|
15
|
+
# @return [GraphQL::Query::Arguments] The underlying arguments instance
|
16
|
+
attr_reader :arguments
|
17
|
+
|
12
18
|
# A lot of methods work just like GraphQL::Arguments
|
13
19
|
def_delegators :@arguments, :[], :key?, :to_h
|
14
20
|
def_delegators :to_h, :keys, :values, :each, :any?
|
@@ -29,31 +29,17 @@ module GraphQL
|
|
29
29
|
# @example
|
30
30
|
# field :isDraft, Boolean, null: false
|
31
31
|
# field :id, ID, null: false
|
32
|
+
# field :score, Int, null: false
|
32
33
|
module GraphQLTypeNames
|
33
34
|
Boolean = "Boolean"
|
34
35
|
ID = "ID"
|
36
|
+
Int = "Int"
|
35
37
|
end
|
36
38
|
|
37
39
|
include GraphQLTypeNames
|
38
40
|
class << self
|
39
41
|
include CachedGraphQLDefinition
|
40
42
|
include GraphQL::Relay::TypeExtensions
|
41
|
-
# Delegate to the derived type definition if possible.
|
42
|
-
# This is tricky because missing methods cause the definition to be built & cached.
|
43
|
-
def method_missing(method_name, *args, &block)
|
44
|
-
if graphql_definition.respond_to?(method_name)
|
45
|
-
graphql_definition.public_send(method_name, *args, &block)
|
46
|
-
else
|
47
|
-
super
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# Check if the derived type definition responds to the method
|
52
|
-
# @return [Boolean]
|
53
|
-
def respond_to_missing?(method_name, incl_private = false)
|
54
|
-
graphql_definition.respond_to?(method_name, incl_private) || super
|
55
|
-
end
|
56
|
-
|
57
43
|
# Call this with a new name to override the default name for this schema member; OR
|
58
44
|
# call it without an argument to get the name of this schema member
|
59
45
|
#
|
@@ -37,6 +37,10 @@ module GraphQL
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
+
def global_id_field(field_name)
|
41
|
+
field field_name, "ID", null: false, resolve: GraphQL::Relay::GlobalIdResolve.new(type: self)
|
42
|
+
end
|
43
|
+
|
40
44
|
private
|
41
45
|
|
42
46
|
# @return [Array<GraphQL::Schema::Field>] Fields defined on this class _specifically_, not parent classes
|
@@ -3,12 +3,17 @@
|
|
3
3
|
module GraphQL
|
4
4
|
class Schema
|
5
5
|
class Object < GraphQL::Schema::Member
|
6
|
+
# @return [Object] the application object this type is wrapping
|
6
7
|
attr_reader :object
|
7
8
|
|
9
|
+
# @return [GraphQL::Query::Context] the context instance for this query
|
10
|
+
attr_reader :context
|
11
|
+
|
8
12
|
def initialize(object, context)
|
9
13
|
@object = object
|
10
14
|
@context = context
|
11
15
|
end
|
16
|
+
|
12
17
|
extend GraphQL::Schema::Member::HasFields
|
13
18
|
field_class GraphQL::Schema::Field
|
14
19
|
|
@@ -56,10 +61,6 @@ module GraphQL
|
|
56
61
|
|
57
62
|
obj_type
|
58
63
|
end
|
59
|
-
|
60
|
-
def global_id_field(field_name)
|
61
|
-
field field_name, "ID", null: false, resolve: GraphQL::Relay::GlobalIdResolve.new(type: self)
|
62
|
-
end
|
63
64
|
end
|
64
65
|
end
|
65
66
|
end
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -2,11 +2,6 @@
|
|
2
2
|
module GraphQL
|
3
3
|
class Schema
|
4
4
|
class Union < GraphQL::Schema::Member
|
5
|
-
def initialize(obj, ctx)
|
6
|
-
@object = obj
|
7
|
-
@context = ctx
|
8
|
-
end
|
9
|
-
|
10
5
|
class << self
|
11
6
|
def possible_types(*types)
|
12
7
|
if types.any?
|
@@ -23,13 +18,6 @@ module GraphQL
|
|
23
18
|
@own_possible_types ||= []
|
24
19
|
end
|
25
20
|
|
26
|
-
# The class resolves type by:
|
27
|
-
# - make an instance
|
28
|
-
# - call the instance method
|
29
|
-
def resolve_type(value, ctx)
|
30
|
-
self.new(value, ctx).resolve_type
|
31
|
-
end
|
32
|
-
|
33
21
|
def to_graphql
|
34
22
|
type_defn = GraphQL::UnionType.new
|
35
23
|
type_defn.name = graphql_name
|