graphql 0.3.0 → 0.4.0
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/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
|
|