graphql 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graph_ql/{types/input_value.rb → argument.rb} +4 -1
- data/lib/graph_ql/{scalars/boolean_type.rb → boolean_type.rb} +0 -0
- data/lib/graph_ql/definition_helpers.rb +10 -0
- data/lib/graph_ql/definition_helpers/argument_definer.rb +5 -2
- data/lib/graph_ql/definition_helpers/definable.rb +1 -1
- data/lib/graph_ql/definition_helpers/field_definer.rb +4 -1
- data/lib/graph_ql/definition_helpers/non_null_with_bang.rb +3 -1
- data/lib/graph_ql/definition_helpers/string_named_hash.rb +7 -1
- data/lib/graph_ql/definition_helpers/type_definer.rb +13 -1
- data/lib/graph_ql/directive.rb +14 -6
- data/lib/graph_ql/{directives → directive}/include_directive.rb +1 -1
- data/lib/graph_ql/{directives → directive}/skip_directive.rb +1 -1
- data/lib/graph_ql/enum_type.rb +66 -0
- data/lib/graph_ql/field.rb +49 -13
- data/lib/graph_ql/{scalars/float_type.rb → float_type.rb} +0 -0
- data/lib/graph_ql/{scalars/id_type.rb → id_type.rb} +0 -0
- data/lib/graph_ql/input_object_type.rb +33 -0
- data/lib/graph_ql/{scalars/int_type.rb → int_type.rb} +0 -0
- data/lib/graph_ql/interface_type.rb +33 -0
- data/lib/graph_ql/introspection/field_type.rb +1 -0
- data/lib/graph_ql/introspection/fields_field.rb +1 -0
- data/lib/graph_ql/introspection/input_value_type.rb +1 -1
- data/lib/graph_ql/introspection/introspection_query.rb +77 -0
- data/lib/graph_ql/introspection/schema_field.rb +13 -0
- data/lib/graph_ql/introspection/type_by_name_field.rb +14 -0
- data/lib/graph_ql/introspection/type_kind_enum.rb +1 -1
- data/lib/graph_ql/{types/list_type.rb → list_type.rb} +3 -0
- data/lib/graph_ql/{parser/nodes.rb → nodes.rb} +8 -1
- data/lib/graph_ql/{types/non_null_type.rb → non_null_type.rb} +3 -0
- data/lib/graph_ql/object_type.rb +92 -0
- data/lib/graph_ql/parser.rb +107 -6
- data/lib/graph_ql/query.rb +8 -0
- data/lib/graph_ql/query/arguments.rb +13 -4
- data/lib/graph_ql/{directives → query}/directive_chain.rb +2 -2
- data/lib/graph_ql/query/field_resolution_strategy.rb +2 -2
- data/lib/graph_ql/query/operation_resolver.rb +3 -3
- data/lib/graph_ql/query/selection_resolver.rb +1 -1
- data/lib/graph_ql/scalar_type.rb +9 -0
- data/lib/graph_ql/schema.rb +26 -17
- data/lib/graph_ql/schema/type_reducer.rb +7 -0
- data/lib/graph_ql/static_validation/arguments_validator.rb +1 -0
- data/lib/graph_ql/static_validation/message.rb +4 -1
- data/lib/graph_ql/static_validation/rules/fields_are_defined_on_type.rb +1 -0
- data/lib/graph_ql/static_validation/rules/fields_have_appropriate_selections.rb +1 -0
- data/lib/graph_ql/static_validation/type_stack.rb +24 -4
- data/lib/graph_ql/static_validation/validator.rb +27 -0
- data/lib/graph_ql/{scalars/string_type.rb → string_type.rb} +0 -0
- data/lib/graph_ql/transform.rb +87 -0
- data/lib/graph_ql/type_kinds.rb +9 -0
- data/lib/graph_ql/{types/union.rb → union_type.rb} +8 -4
- data/lib/graph_ql/version.rb +1 -1
- data/lib/graph_ql/{parser/visitor.rb → visitor.rb} +29 -4
- data/lib/graphql.rb +28 -11
- data/readme.md +11 -1
- data/spec/graph_ql/{types/enum_spec.rb → enum_type_spec.rb} +1 -1
- data/spec/graph_ql/{fields/field_spec.rb → field_spec.rb} +0 -0
- data/spec/graph_ql/{scalars/id_type_spec.rb → id_type_spec.rb} +0 -0
- data/spec/graph_ql/{types/input_object_type_spec.rb → input_object_type_spec.rb} +0 -0
- data/spec/graph_ql/{types/interface_spec.rb → interface_type_spec.rb} +1 -1
- data/spec/graph_ql/introspection/introspection_query_spec.rb +10 -0
- data/spec/graph_ql/introspection/schema_type_spec.rb +0 -4
- data/spec/graph_ql/introspection/type_type_spec.rb +1 -5
- data/spec/graph_ql/{types/object_type_spec.rb → object_type_spec.rb} +0 -0
- data/spec/graph_ql/{parser/parser_spec.rb → parser_spec.rb} +0 -0
- data/spec/graph_ql/query/operation_resolver_spec.rb +1 -1
- data/spec/graph_ql/query_spec.rb +6 -2
- data/spec/graph_ql/schema/type_validator_spec.rb +1 -1
- data/spec/graph_ql/{parser/transform_spec.rb → transform_spec.rb} +0 -0
- data/spec/graph_ql/{types/union_spec.rb → union_type_spec.rb} +2 -2
- data/spec/graph_ql/{parser/visitor_spec.rb → visitor_spec.rb} +0 -0
- data/spec/support/{dummy_app.rb → dairy_app.rb} +8 -8
- data/spec/support/{dummy_data.rb → dairy_data.rb} +0 -0
- data/spec/support/star_wars_data.rb +71 -0
- data/spec/support/star_wars_schema.rb +87 -0
- metadata +59 -50
- data/lib/graph_ql/definition_helpers/forwardable.rb +0 -10
- data/lib/graph_ql/parser/parser.rb +0 -108
- data/lib/graph_ql/parser/transform.rb +0 -87
- data/lib/graph_ql/scalars/scalar_type.rb +0 -5
- data/lib/graph_ql/types/enum.rb +0 -32
- data/lib/graph_ql/types/input_object_type.rb +0 -14
- data/lib/graph_ql/types/interface.rb +0 -14
- data/lib/graph_ql/types/object_type.rb +0 -66
@@ -0,0 +1,13 @@
|
|
1
|
+
# A wrapper to implement `__schema`
|
2
|
+
class GraphQL::Introspection::SchemaField
|
3
|
+
DEFINITION = Proc.new { |f, wrapped_type|
|
4
|
+
f.name("__schema")
|
5
|
+
f.description("This GraphQL schema")
|
6
|
+
f.type(!GraphQL::Introspection::SchemaType)
|
7
|
+
f.resolve -> (o, a, c) { wrapped_type }
|
8
|
+
}
|
9
|
+
|
10
|
+
def self.create(wrapped_type)
|
11
|
+
GraphQL::Field.new { |f| DEFINITION.call(f, wrapped_type) }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# A wrapper to create `__type(name: )` dynamically.
|
2
|
+
class GraphQL::Introspection::TypeByNameField
|
3
|
+
DEFINITION = Proc.new { |f, type, field, arg, type_hash|
|
4
|
+
f.name("__type")
|
5
|
+
f.description("A type in the GraphQL system")
|
6
|
+
f.arguments({name: arg.build(type: !type.String)})
|
7
|
+
f.type(!GraphQL::Introspection::TypeType)
|
8
|
+
f.resolve -> (o, args, c) { type_hash[args["name"]] }
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.create(type_hash)
|
12
|
+
GraphQL::Field.new { |f, type, field, arg| DEFINITION.call(f, type, field, arg, type_hash) }
|
13
|
+
end
|
14
|
+
end
|
@@ -4,6 +4,8 @@ module GraphQL::Nodes
|
|
4
4
|
# - expose accessors for keyword arguments
|
5
5
|
class AbstractNode
|
6
6
|
attr_accessor :line, :col
|
7
|
+
|
8
|
+
# @param options [Hash] Must contain all attributes defined by {required_attrs}, may also include `position_source`
|
7
9
|
def initialize(options)
|
8
10
|
required_keys = self.class.required_attrs
|
9
11
|
allowed_keys = required_keys + [:line, :col]
|
@@ -31,15 +33,17 @@ module GraphQL::Nodes
|
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
36
|
+
# Test all attributes, checking for any other nodes below this one
|
34
37
|
def children
|
35
38
|
self.class.required_attrs
|
36
39
|
.map { |attr| send(attr) }
|
37
|
-
.flatten
|
40
|
+
.flatten
|
38
41
|
.select { |val| val.is_a?(GraphQL::Nodes::AbstractNode) }
|
39
42
|
end
|
40
43
|
|
41
44
|
class << self
|
42
45
|
attr_reader :required_attrs
|
46
|
+
# Defines attributes which are required at initialization.
|
43
47
|
def attr_required(*attr_names)
|
44
48
|
@required_attrs ||= []
|
45
49
|
@required_attrs += attr_names
|
@@ -48,6 +52,9 @@ module GraphQL::Nodes
|
|
48
52
|
|
49
53
|
# Create a new AbstractNode child which
|
50
54
|
# requires and exposes {attr_names}.
|
55
|
+
# @param attr_names [Array<Symbol>] Attributes this node class will have
|
56
|
+
# @param block [Block] Block passed to `Class.new`
|
57
|
+
# @return [Class] A new node class
|
51
58
|
def create(*attr_names, &block)
|
52
59
|
cls = Class.new(self, &block)
|
53
60
|
cls.attr_required(*attr_names)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# This type exposes fields on an object.
|
2
|
+
#
|
3
|
+
#
|
4
|
+
class GraphQL::ObjectType
|
5
|
+
include GraphQL::DefinitionHelpers::NonNullWithBang
|
6
|
+
extend GraphQL::DefinitionHelpers::Definable
|
7
|
+
attr_definable :name, :description, :interfaces, :fields
|
8
|
+
|
9
|
+
def initialize(&block)
|
10
|
+
self.fields = []
|
11
|
+
self.interfaces = []
|
12
|
+
yield(
|
13
|
+
self,
|
14
|
+
GraphQL::DefinitionHelpers::TypeDefiner.instance,
|
15
|
+
GraphQL::DefinitionHelpers::FieldDefiner.instance,
|
16
|
+
GraphQL::DefinitionHelpers::ArgumentDefiner.instance
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @overload fields(new_fields)
|
21
|
+
# Define `new_fields` as the fields this type exposes, uses {#fields=}
|
22
|
+
#
|
23
|
+
# @overload fields()
|
24
|
+
# @return [Hash] fields this type exposes
|
25
|
+
def fields(new_fields=nil)
|
26
|
+
if !new_fields.nil?
|
27
|
+
self.fields = new_fields
|
28
|
+
end
|
29
|
+
@fields
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Define fields to be `new_fields`, normalize with {StringNamedHash}
|
34
|
+
# @param new_fields [Hash] The fields exposed by this type
|
35
|
+
def fields=(new_fields)
|
36
|
+
@fields = GraphQL::DefinitionHelpers::StringNamedHash.new(new_fields).to_h
|
37
|
+
end
|
38
|
+
|
39
|
+
# @overload interfaces(new_interfaces)
|
40
|
+
# Declare that this type implements `new_interfaces`.
|
41
|
+
# Shovel this type into each interface's `possible_types` array.
|
42
|
+
#
|
43
|
+
# (There's a bug here: if you define interfaces twice, it won't remove previous definitions.)
|
44
|
+
#
|
45
|
+
# @param new_interfaces [Array<GraphQL::Interface>] interfaces that this type implements
|
46
|
+
#
|
47
|
+
# @overload interfaces
|
48
|
+
# @return [Array<GraphQL::Interface>] interfaces that this type implements
|
49
|
+
#
|
50
|
+
def interfaces(new_interfaces=nil)
|
51
|
+
if !new_interfaces.nil?
|
52
|
+
@interfaces = new_interfaces
|
53
|
+
new_interfaces.each {|i| i.possible_types << self }
|
54
|
+
end
|
55
|
+
@interfaces
|
56
|
+
end
|
57
|
+
|
58
|
+
def kind
|
59
|
+
GraphQL::TypeKinds::OBJECT
|
60
|
+
end
|
61
|
+
|
62
|
+
# Print the human-readable name of this type
|
63
|
+
def to_s
|
64
|
+
Printer.instance.print(self)
|
65
|
+
end
|
66
|
+
|
67
|
+
alias :inspect :to_s
|
68
|
+
|
69
|
+
# @param other [GraphQL::ObjectType] compare to this object
|
70
|
+
# @return [Boolean] are these types equivalent? (incl. non-null, list)
|
71
|
+
def ==(other)
|
72
|
+
if other.is_a?(GraphQL::ObjectType)
|
73
|
+
self.to_s == other.to_s
|
74
|
+
else
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Print a type, using the query-style naming pattern
|
80
|
+
class Printer
|
81
|
+
include Singleton
|
82
|
+
def print(type)
|
83
|
+
if type.kind.non_null?
|
84
|
+
"#{print(type.of_type)}!"
|
85
|
+
elsif type.kind.list?
|
86
|
+
"[#{print(type.of_type)}]"
|
87
|
+
else
|
88
|
+
type.name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/graph_ql/parser.rb
CHANGED
@@ -1,9 +1,110 @@
|
|
1
|
-
require 'graph_ql/parser/nodes'
|
2
|
-
require 'graph_ql/parser/parser'
|
3
|
-
require 'graph_ql/parser/transform'
|
4
|
-
require 'graph_ql/parser/visitor'
|
5
|
-
|
6
1
|
module GraphQL
|
2
|
+
# Parser is a [parslet](http://kschiess.github.io/parslet/) parser for parsing queries.
|
3
|
+
#
|
4
|
+
class Parser < Parslet::Parser
|
5
|
+
root(:document)
|
6
|
+
rule(:document) { (
|
7
|
+
space |
|
8
|
+
operation_definition |
|
9
|
+
fragment_definition
|
10
|
+
).repeat(1).as(:document_parts)
|
11
|
+
}
|
12
|
+
|
13
|
+
# TODO: whitespace sensitive regarding `on`, eg `onFood`, see lookahead note in spec
|
14
|
+
rule(:fragment_definition) {
|
15
|
+
str("fragment").as(:fragment_keyword) >>
|
16
|
+
space? >> name.as(:fragment_name) >>
|
17
|
+
space? >> str("on") >> space? >> name.as(:type_condition) >>
|
18
|
+
space? >> directives.maybe.as(:optional_directives).as(:directives) >>
|
19
|
+
space? >> selections.as(:selections)
|
20
|
+
}
|
21
|
+
|
22
|
+
rule(:fragment_spread) {
|
23
|
+
spread.as(:fragment_spread_keyword) >> space? >>
|
24
|
+
name.as(:fragment_spread_name) >> space? >>
|
25
|
+
directives.maybe.as(:optional_directives).as(:directives)
|
26
|
+
}
|
27
|
+
rule(:spread) { str("...") }
|
28
|
+
# TODO: `on` bug, see spec
|
29
|
+
rule(:inline_fragment) {
|
30
|
+
spread.as(:fragment_spread_keyword) >> space? >>
|
31
|
+
str("on ") >> name.as(:inline_fragment_type) >> space? >>
|
32
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
33
|
+
selections.as(:selections)
|
34
|
+
}
|
35
|
+
|
36
|
+
rule(:operation_definition) { (unnamed_selections | named_operation_definition) }
|
37
|
+
rule(:unnamed_selections) { selections.as(:unnamed_selections)}
|
38
|
+
rule(:named_operation_definition) {
|
39
|
+
operation_type.as(:operation_type) >> space? >>
|
40
|
+
name.as(:name) >> space? >>
|
41
|
+
operation_variable_definitions.maybe.as(:optional_variables).as(:variables) >> space? >>
|
42
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
43
|
+
selections.as(:selections)
|
44
|
+
}
|
45
|
+
rule(:operation_type) { (str("query") | str("mutation")) }
|
46
|
+
rule(:operation_variable_definitions) { str("(") >> space? >> (operation_variable_definition >> separator?).repeat(1) >> space? >> str(")") }
|
47
|
+
rule(:operation_variable_definition) {
|
48
|
+
value_variable.as(:variable_name) >> space? >>
|
49
|
+
str(":") >> space? >>
|
50
|
+
type.as(:variable_type) >> space? >>
|
51
|
+
(str("=") >> space? >> value.as(:variable_default_value)).maybe.as(:variable_optional_default_value)}
|
52
|
+
|
53
|
+
rule(:selection) { (inline_fragment | fragment_spread | field) >> space? >> separator? }
|
54
|
+
rule(:selections) { str("{") >> space? >> selection.repeat(1) >> space? >> str("}")}
|
55
|
+
|
56
|
+
rule(:field) {
|
57
|
+
field_alias.maybe.as(:alias) >>
|
58
|
+
name.as(:field_name) >>
|
59
|
+
field_arguments.maybe.as(:optional_field_arguments).as(:field_arguments) >> space? >>
|
60
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
61
|
+
selections.maybe.as(:optional_selections).as(:selections)
|
62
|
+
}
|
63
|
+
|
64
|
+
rule(:field_alias) { name.as(:alias_name) >> space? >> str(":") >> space? }
|
65
|
+
rule(:field_arguments) { str("(") >> field_argument.repeat(1) >> str(")") }
|
66
|
+
rule(:field_argument) { name.as(:field_argument_name) >> str(":") >> space? >> value.as(:field_argument_value) >> separator? }
|
67
|
+
|
68
|
+
rule(:directives) { (directive >> separator?).repeat(1) }
|
69
|
+
rule(:directive) {
|
70
|
+
str("@") >> name.as(:directive_name) >>
|
71
|
+
directive_arguments.maybe.as(:optional_directive_arguments).as(:directive_arguments)
|
72
|
+
}
|
73
|
+
rule(:directive_arguments) { str("(") >> directive_argument.repeat(1) >> str(")") }
|
74
|
+
rule(:directive_argument) { name.as(:directive_argument_name) >> str(":") >> space? >> value.as(:directive_argument_value) >> separator? }
|
75
|
+
|
76
|
+
rule(:type) { (non_null_type | list_type | type_name)}
|
77
|
+
rule(:list_type) { str("[") >> type.as(:list_type) >> str("]")}
|
78
|
+
rule(:non_null_type) { (list_type | type_name).as(:non_null_type) >> str("!") }
|
79
|
+
rule(:type_name) { name.as(:type_name) }
|
80
|
+
|
81
|
+
rule(:value) {(
|
82
|
+
value_input_object |
|
83
|
+
value_float |
|
84
|
+
value_int |
|
85
|
+
value_string |
|
86
|
+
value_boolean |
|
87
|
+
value_array |
|
88
|
+
value_variable |
|
89
|
+
value_enum
|
90
|
+
)}
|
91
|
+
rule(:value_sign?) { match('[\-\+]').maybe }
|
92
|
+
rule(:value_array) { (str("[") >> (value >> separator?).repeat(0) >> str("]")).as(:array) }
|
93
|
+
rule(:value_boolean) { (str("true") | str("false")).as(:boolean) }
|
94
|
+
rule(:value_float) { (value_sign? >> match('\d').repeat(1) >> str(".") >> match('\d').repeat(1) >> (match("[eE]") >> value_sign? >> match('\d').repeat(1)).maybe).as(:float) }
|
95
|
+
rule(:value_input_object) { str("{") >> value_input_object_pair.repeat(1).as(:input_object) >> str("}") }
|
96
|
+
rule(:value_input_object_pair) { space? >> name.as(:input_object_name) >> space? >> str(":") >> space? >> value.as(:input_object_value) >> separator? }
|
97
|
+
rule(:value_int) { (value_sign? >> match('\d').repeat(1)).as(:int) }
|
98
|
+
# TODO: support unicode, escaped chars (match the spec)
|
99
|
+
rule(:value_string) { str('"') >> match('[^\"]').repeat(1).as(:string) >> str('"')}
|
100
|
+
rule(:value_enum) { name.as(:enum) }
|
101
|
+
rule(:value_variable) { str("$") >> name.as(:variable) }
|
102
|
+
|
103
|
+
rule(:separator?) { (space? >> str(",") >> space?).maybe }
|
104
|
+
rule(:name) { match('[_A-Za-z]') >> match('[_0-9A-Za-z]').repeat(0) }
|
105
|
+
rule(:comment) { str("#") >> match('[^\r\n]').repeat(0) }
|
106
|
+
rule(:space) { (match('[\s\n]+') | comment).repeat(1) }
|
107
|
+
rule(:space?) { space.maybe }
|
108
|
+
end
|
7
109
|
PARSER = GraphQL::Parser.new
|
8
|
-
TRANSFORM = GraphQL::Transform.new
|
9
110
|
end
|
data/lib/graph_ql/query.rb
CHANGED
@@ -5,6 +5,13 @@ class GraphQL::Query
|
|
5
5
|
DEFAULT_RESOLVE = :__default_resolve
|
6
6
|
attr_reader :schema, :document, :context, :fragments, :params
|
7
7
|
|
8
|
+
# Prepare query `query_string` on {GraphQL::Schema} `schema`
|
9
|
+
# @param schema [GraphQL::Schema]
|
10
|
+
# @param query_string [String]
|
11
|
+
# @param context [#[]] (default: `nil`) an arbitrary hash of values which you can access in {GraphQL::Field#resolve}
|
12
|
+
# @param params [Hash] (default: `{}`) values for `$variables` in the query
|
13
|
+
# @param debug [Boolean] (default: `true`) if true, errors are raised, if false, errors are put in the `errors` key
|
14
|
+
# @param validate [Boolean] (default: `true`) if true, `query_string` will be validated with {StaticValidation::Validator}
|
8
15
|
def initialize(schema, query_string, context: nil, params: {}, debug: true, validate: true)
|
9
16
|
@schema = schema
|
10
17
|
@debug = debug
|
@@ -82,3 +89,4 @@ require 'graph_ql/query/inline_fragment_resolution_strategy'
|
|
82
89
|
require 'graph_ql/query/operation_resolver'
|
83
90
|
require 'graph_ql/query/selection_resolver'
|
84
91
|
require 'graph_ql/query/type_resolver'
|
92
|
+
require 'graph_ql/query/directive_chain'
|
@@ -1,8 +1,11 @@
|
|
1
|
-
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
# Provide read-only access to arguments by string or symbol names.
|
2
4
|
class GraphQL::Query::Arguments
|
3
|
-
|
5
|
+
extend Forwardable
|
6
|
+
|
4
7
|
def initialize(ast_arguments, argument_hash, variables)
|
5
|
-
@
|
8
|
+
@hash = ast_arguments.reduce({}) do |memo, arg|
|
6
9
|
arg_defn = argument_hash[arg.name]
|
7
10
|
value = reduce_value(arg.value, arg_defn, variables)
|
8
11
|
memo[arg.name] = value
|
@@ -10,6 +13,12 @@ class GraphQL::Query::Arguments
|
|
10
13
|
end
|
11
14
|
end
|
12
15
|
|
16
|
+
def_delegators :@hash, :keys, :values
|
17
|
+
|
18
|
+
def [](key)
|
19
|
+
@hash[key.to_s]
|
20
|
+
end
|
21
|
+
|
13
22
|
private
|
14
23
|
|
15
24
|
def reduce_value(value, arg_defn, variables)
|
@@ -18,7 +27,7 @@ class GraphQL::Query::Arguments
|
|
18
27
|
elsif value.is_a?(GraphQL::Nodes::Enum)
|
19
28
|
value = arg_defn.type.coerce(value.name)
|
20
29
|
elsif value.is_a?(GraphQL::Nodes::InputObject)
|
21
|
-
value = self.class.new(value.pairs, arg_defn.type.input_fields, variables)
|
30
|
+
value = self.class.new(value.pairs, arg_defn.type.input_fields, variables)
|
22
31
|
else
|
23
32
|
value
|
24
33
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class GraphQL::DirectiveChain
|
1
|
+
class GraphQL::Query::DirectiveChain
|
2
2
|
DIRECTIVE_ON = {
|
3
3
|
GraphQL::Nodes::Field => GraphQL::Directive::ON_FIELD,
|
4
4
|
GraphQL::Nodes::InlineFragment => GraphQL::Directive::ON_FRAGMENT,
|
@@ -24,7 +24,7 @@ class GraphQL::DirectiveChain
|
|
24
24
|
@result = block.call
|
25
25
|
else
|
26
26
|
applicable_directives.map do |(ast_directive, directive)|
|
27
|
-
args = GraphQL::Query::Arguments.new(ast_directive.arguments, directive.arguments, operation_resolver.variables)
|
27
|
+
args = GraphQL::Query::Arguments.new(ast_directive.arguments, directive.arguments, operation_resolver.variables)
|
28
28
|
@result = directive.resolve(args, block)
|
29
29
|
end
|
30
30
|
@result ||= {}
|
@@ -3,8 +3,8 @@ class GraphQL::Query::FieldResolutionStrategy
|
|
3
3
|
|
4
4
|
def initialize(ast_field, parent_type, target, operation_resolver)
|
5
5
|
field_name = ast_field.name
|
6
|
-
field = parent_type
|
7
|
-
arguments = GraphQL::Query::Arguments.new(ast_field.arguments, field.arguments, operation_resolver.variables)
|
6
|
+
field = operation_resolver.query.schema.get_field(parent_type, field_name) || raise("No field found on #{parent_type.name} '#{parent_type}' for '#{field_name}'")
|
7
|
+
arguments = GraphQL::Query::Arguments.new(ast_field.arguments, field.arguments, operation_resolver.variables)
|
8
8
|
value = field.resolve(target, arguments, operation_resolver.context)
|
9
9
|
if value.nil?
|
10
10
|
@result_value = value
|
@@ -1,14 +1,14 @@
|
|
1
1
|
class GraphQL::Query::OperationResolver
|
2
|
-
|
3
|
-
attr_reader :variables, :query
|
2
|
+
attr_reader :variables, :query, :context
|
4
3
|
|
5
4
|
def initialize(operation_definition, query)
|
6
5
|
@operation_definition = operation_definition
|
7
6
|
@variables = query.params
|
8
7
|
@query = query
|
8
|
+
@context = query.context
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
|
12
12
|
|
13
13
|
def result
|
14
14
|
@result ||= execute(@operation_definition, query)
|
@@ -9,7 +9,7 @@ class GraphQL::Query::SelectionResolver
|
|
9
9
|
|
10
10
|
def initialize(target, type, selections, operation_resolver)
|
11
11
|
@result = selections.reduce({}) do |memo, ast_field|
|
12
|
-
chain = GraphQL::DirectiveChain.new(ast_field, operation_resolver) {
|
12
|
+
chain = GraphQL::Query::DirectiveChain.new(ast_field, operation_resolver) {
|
13
13
|
strategy_class = RESOLUTION_STRATEGIES[ast_field.class]
|
14
14
|
strategy = strategy_class.new(ast_field, type, target, operation_resolver)
|
15
15
|
strategy.result
|
data/lib/graph_ql/schema.rb
CHANGED
@@ -1,23 +1,13 @@
|
|
1
|
+
# A GraphQL schema which may be queried with {GraphQL::Query}.
|
1
2
|
class GraphQL::Schema
|
2
|
-
DIRECTIVES = [GraphQL::SkipDirective, GraphQL::IncludeDirective]
|
3
|
+
DIRECTIVES = [GraphQL::Directive::SkipDirective, GraphQL::Directive::IncludeDirective]
|
4
|
+
DYNAMIC_FIELDS = ["__type", "__typename", "__schema"]
|
3
5
|
|
4
6
|
attr_reader :query, :mutation, :directives, :static_validator
|
5
|
-
def initialize(query:, mutation:)
|
6
|
-
# Add fields to this query root for introspection:
|
7
|
-
query.fields = query.fields.merge({
|
8
|
-
"__type" => GraphQL::Field.new do |f, type, field, arg|
|
9
|
-
f.description("A type in the GraphQL system")
|
10
|
-
f.arguments({name: arg.build(type: !type.String)})
|
11
|
-
f.type(!GraphQL::Introspection::TypeType)
|
12
|
-
f.resolve -> (o, a, c) { self.types[a["name"]] }
|
13
|
-
end,
|
14
|
-
"__schema" => GraphQL::Field.new do |f|
|
15
|
-
f.description("This GraphQL schema")
|
16
|
-
f.type(!GraphQL::Introspection::SchemaType)
|
17
|
-
f.resolve -> (o, a, c) { self }
|
18
|
-
end
|
19
|
-
})
|
20
7
|
|
8
|
+
# @param query [GraphQL::ObjectType] the query root for the schema
|
9
|
+
# @param mutation [GraphQL::ObjectType, nil] the mutation root for the schema
|
10
|
+
def initialize(query:, mutation:)
|
21
11
|
@query = query
|
22
12
|
@mutation = mutation
|
23
13
|
@directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
|
@@ -29,8 +19,27 @@ class GraphQL::Schema
|
|
29
19
|
end
|
30
20
|
end
|
31
21
|
|
22
|
+
# A `{ name => type }` hash of types in this schema
|
23
|
+
# @returns Hash
|
32
24
|
def types
|
33
|
-
@types ||= TypeReducer.
|
25
|
+
@types ||= TypeReducer.find_all([query, mutation, GraphQL::Introspection::SchemaType].compact)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Resolve field named `field_name` for type `parent_type`.
|
29
|
+
# Handles dynamic fields `__typename`, `__type` and `__schema`, too
|
30
|
+
def get_field(parent_type, field_name)
|
31
|
+
defined_field = parent_type.fields[field_name]
|
32
|
+
if defined_field
|
33
|
+
defined_field
|
34
|
+
elsif field_name == "__typename"
|
35
|
+
GraphQL::Introspection::TypenameField.create(parent_type)
|
36
|
+
elsif field_name == "__schema" && parent_type == query
|
37
|
+
GraphQL::Introspection::SchemaField.create(self)
|
38
|
+
elsif field_name == "__type" && parent_type == query
|
39
|
+
GraphQL::Introspection::TypeByNameField.create(self.types)
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
34
43
|
end
|
35
44
|
end
|
36
45
|
|