graphql 1.8.0.pre6 → 1.8.0.pre7
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 +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
|