graphql 0.11.1 → 0.12.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/graphql.rb +5 -2
- data/lib/graphql/argument.rb +8 -3
- data/lib/graphql/base_type.rb +5 -3
- data/lib/graphql/boolean_type.rb +6 -1
- data/lib/graphql/define.rb +8 -0
- data/lib/graphql/define/assign_argument.rb +19 -0
- data/lib/graphql/define/assign_enum_value.rb +16 -0
- data/lib/graphql/define/assign_object_field.rb +19 -0
- data/lib/graphql/define/assignment_dictionary.rb +26 -0
- data/lib/graphql/define/defined_object_proxy.rb +32 -0
- data/lib/graphql/define/instance_definable.rb +79 -0
- data/lib/graphql/{definition_helpers → define}/non_null_with_bang.rb +1 -1
- data/lib/graphql/{definition_helpers → define}/type_definer.rb +1 -1
- data/lib/graphql/directive.rb +7 -2
- data/lib/graphql/enum_type.rb +12 -6
- data/lib/graphql/execution_error.rb +1 -1
- data/lib/graphql/field.rb +26 -23
- data/lib/graphql/float_type.rb +2 -3
- data/lib/graphql/id_type.rb +9 -1
- data/lib/graphql/input_object_type.rb +11 -8
- data/lib/graphql/int_type.rb +2 -1
- data/lib/graphql/interface_type.rb +7 -2
- data/lib/graphql/introspection/input_value_type.rb +10 -1
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/object_type.rb +27 -25
- data/lib/graphql/query.rb +5 -8
- data/lib/graphql/query/serial_execution/field_resolution.rb +0 -10
- data/lib/graphql/scalar_type.rb +4 -3
- data/lib/graphql/schema.rb +1 -1
- data/lib/graphql/static_validation/rules/document_does_not_exceed_max_depth.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -4
- data/lib/graphql/static_validation/validator.rb +3 -3
- data/lib/graphql/string_type.rb +2 -1
- data/lib/graphql/union_type.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +4 -10
- data/spec/graphql/boolean_type_spec.rb +20 -0
- data/spec/graphql/define/instance_definable_spec.rb +55 -0
- data/spec/graphql/field_spec.rb +14 -2
- data/spec/graphql/float_type_spec.rb +15 -0
- data/spec/graphql/id_type_spec.rb +9 -0
- data/spec/graphql/int_type_spec.rb +15 -0
- data/spec/graphql/introspection/input_value_type_spec.rb +36 -0
- data/spec/graphql/introspection/type_type_spec.rb +0 -23
- data/spec/graphql/query_spec.rb +16 -0
- data/spec/graphql/static_validation/complexity_validator_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/document_does_not_exceed_max_depth_spec.rb +9 -1
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +3 -2
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +5 -4
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +4 -3
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +4 -3
- data/spec/graphql/static_validation/type_stack_spec.rb +3 -2
- data/spec/graphql/static_validation/validator_spec.rb +3 -25
- data/spec/graphql/string_type_spec.rb +15 -0
- data/spec/support/dairy_app.rb +2 -0
- metadata +25 -9
- data/lib/graphql/definition_helpers.rb +0 -4
- data/lib/graphql/definition_helpers/defined_by_config.rb +0 -123
- data/lib/graphql/definition_helpers/string_named_hash.rb +0 -22
data/lib/graphql/float_type.rb
CHANGED
data/lib/graphql/id_type.rb
CHANGED
@@ -8,12 +8,15 @@
|
|
8
8
|
# end
|
9
9
|
#
|
10
10
|
class GraphQL::InputObjectType < GraphQL::BaseType
|
11
|
-
|
12
|
-
defined_by_config :name, :description, :input_fields
|
13
|
-
alias :arguments :input_fields
|
11
|
+
accepts_definitions input_field: GraphQL::Define::AssignArgument
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
# @return [Hash<String, GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
|
14
|
+
attr_accessor :arguments
|
15
|
+
|
16
|
+
alias :input_fields :arguments
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@arguments = {}
|
17
20
|
end
|
18
21
|
|
19
22
|
def kind
|
@@ -25,13 +28,13 @@ class GraphQL::InputObjectType < GraphQL::BaseType
|
|
25
28
|
|
26
29
|
# Items in the input that are unexpected
|
27
30
|
input.each do |name, value|
|
28
|
-
if
|
31
|
+
if arguments[name].nil?
|
29
32
|
result.add_problem("Field is not defined on #{self.name}", [name])
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
36
|
# Items in the input that are expected, but have invalid values
|
34
|
-
invalid_fields =
|
37
|
+
invalid_fields = arguments.map do |name, field|
|
35
38
|
field_result = field.type.validate_input(input[name])
|
36
39
|
if !field_result.valid?
|
37
40
|
result.merge_result!(name, field_result)
|
@@ -44,7 +47,7 @@ class GraphQL::InputObjectType < GraphQL::BaseType
|
|
44
47
|
def coerce_non_null_input(value)
|
45
48
|
input_values = {}
|
46
49
|
|
47
|
-
|
50
|
+
arguments.each do |input_key, input_field_defn|
|
48
51
|
field_value = value[input_key]
|
49
52
|
field_value = input_field_defn.type.coerce_input(field_value)
|
50
53
|
|
data/lib/graphql/int_type.rb
CHANGED
@@ -12,8 +12,13 @@
|
|
12
12
|
#
|
13
13
|
class GraphQL::InterfaceType < GraphQL::BaseType
|
14
14
|
include GraphQL::BaseType::HasPossibleTypes
|
15
|
-
|
16
|
-
|
15
|
+
accepts_definitions :resolve_type, field: GraphQL::Define::AssignObjectField
|
16
|
+
|
17
|
+
attr_accessor :fields
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@fields = {}
|
21
|
+
end
|
17
22
|
|
18
23
|
def kind
|
19
24
|
GraphQL::TypeKinds::INTERFACE
|
@@ -4,5 +4,14 @@ GraphQL::Introspection::InputValueType = GraphQL::ObjectType.define do
|
|
4
4
|
field :name, !types.String, "The key for this value"
|
5
5
|
field :description, types.String, "What this value is used for"
|
6
6
|
field :type, -> { !GraphQL::Introspection::TypeType }, "The expected type for this value"
|
7
|
-
field :defaultValue, types.String, "The value applied if no other value is provided"
|
7
|
+
field :defaultValue, types.String, "The value applied if no other value is provided" do
|
8
|
+
resolve -> (obj, args, ctx) {
|
9
|
+
value = obj.default_value
|
10
|
+
if value.is_a?(String)
|
11
|
+
"\"#{value}\""
|
12
|
+
else
|
13
|
+
value
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
8
17
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module GraphQL
|
2
2
|
# Raised automatically when a field's resolve function returns `nil`
|
3
3
|
# for a non-null field.
|
4
|
-
class InvalidNullError <
|
4
|
+
class InvalidNullError < GraphQL::Error
|
5
5
|
def initialize(field_name, value)
|
6
6
|
@field_name = field_name
|
7
7
|
@value = value
|
data/lib/graphql/object_type.rb
CHANGED
@@ -1,37 +1,39 @@
|
|
1
1
|
# This type exposes fields on an object.
|
2
2
|
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
3
|
+
# @example defining a type for your IMDB clone
|
4
|
+
# MovieType = GraphQL::ObjectType.define do
|
5
|
+
# name "Movie"
|
6
|
+
# description "A full-length film or a short film"
|
7
|
+
# interfaces [ProductionInterface, DurationInterface]
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
9
|
+
# field :runtimeMinutes, !types.Int, property: :runtime_minutes
|
10
|
+
# field :director, PersonType
|
11
|
+
# field :cast, CastType
|
12
|
+
# field :starring, types[PersonType] do
|
13
|
+
# arguments :limit, types.Int
|
14
|
+
# resolve -> (object, args, ctx) {
|
15
|
+
# stars = object.cast.stars
|
16
|
+
# args[:limit] && stars = stars.limit(args[:limit])
|
17
|
+
# stars
|
18
|
+
# }
|
19
|
+
# end
|
20
|
+
# end
|
21
21
|
#
|
22
22
|
class GraphQL::ObjectType < GraphQL::BaseType
|
23
|
-
|
24
|
-
attr_accessor :name, :description, :interfaces
|
23
|
+
accepts_definitions :interfaces, field: GraphQL::Define::AssignObjectField
|
24
|
+
attr_accessor :name, :description, :interfaces
|
25
25
|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
# @return [Hash<String, GraphQL::Field>] Map String fieldnames to their {GraphQL::Field} implementations
|
27
|
+
attr_accessor :fields
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@fields = {}
|
31
|
+
@interfaces = []
|
30
32
|
end
|
31
33
|
|
32
|
-
#
|
34
|
+
# Shovel this type into each interface's `possible_types` array.
|
33
35
|
#
|
34
|
-
#
|
36
|
+
# @param new_interfaces [Array<GraphQL::Interface>] interfaces that this type implements
|
35
37
|
def interfaces=(new_interfaces)
|
36
38
|
@interfaces ||= []
|
37
39
|
(@interfaces - new_interfaces).each { |i| i.possible_types.delete(self) }
|
data/lib/graphql/query.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
class GraphQL::Query
|
2
|
-
class OperationNameMissingError <
|
2
|
+
class OperationNameMissingError < GraphQL::Error
|
3
3
|
def initialize(names)
|
4
4
|
msg = "You must provide an operation name from: #{names.join(", ")}"
|
5
5
|
super(msg)
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
# The executor will send the field's name to the target object
|
11
|
-
# and use the result.
|
12
|
-
DEFAULT_RESOLVE = :__default_resolve
|
13
|
-
attr_reader :schema, :document, :context, :fragments, :operations, :debug
|
9
|
+
attr_reader :schema, :document, :context, :fragments, :operations, :debug, :max_depth
|
14
10
|
|
15
11
|
# Prepare query `query_string` on `schema`
|
16
12
|
# @param schema [GraphQL::Schema]
|
@@ -20,9 +16,10 @@ class GraphQL::Query
|
|
20
16
|
# @param debug [Boolean] if true, errors are raised, if false, errors are put in the `errors` key
|
21
17
|
# @param validate [Boolean] if true, `query_string` will be validated with {StaticValidation::Validator}
|
22
18
|
# @param operation_name [String] if the query string contains many operations, this is the one which should be executed
|
23
|
-
def initialize(schema, query_string, context: nil, variables: {}, debug: false, validate: true, operation_name: nil)
|
19
|
+
def initialize(schema, query_string, context: nil, variables: {}, debug: false, validate: true, operation_name: nil, max_depth: nil)
|
24
20
|
@schema = schema
|
25
21
|
@debug = debug
|
22
|
+
@max_depth = max_depth || schema.max_depth
|
26
23
|
@context = Context.new(query: self, values: context)
|
27
24
|
@validate = validate
|
28
25
|
@operation_name = operation_name
|
@@ -72,7 +69,7 @@ class GraphQL::Query
|
|
72
69
|
private
|
73
70
|
|
74
71
|
def validation_errors
|
75
|
-
@validation_errors ||= schema.static_validator.validate(
|
72
|
+
@validation_errors ||= schema.static_validator.validate(self)
|
76
73
|
end
|
77
74
|
|
78
75
|
|
@@ -62,22 +62,12 @@ module GraphQL
|
|
62
62
|
|
63
63
|
|
64
64
|
# Execute the field's resolve method
|
65
|
-
# then handle the DEFAULT_RESOLVE
|
66
65
|
# @return [Proc] suitable to be the last step in a middleware chain
|
67
66
|
def get_middleware_proc_from_field_resolve
|
68
67
|
-> (_parent_type, parent_object, field_definition, field_args, context, _next) {
|
69
68
|
context.ast_node = ast_node
|
70
69
|
value = field_definition.resolve(parent_object, field_args, context)
|
71
70
|
context.ast_node = nil
|
72
|
-
|
73
|
-
if value == GraphQL::Query::DEFAULT_RESOLVE
|
74
|
-
begin
|
75
|
-
value = target.public_send(ast_node.name)
|
76
|
-
rescue NoMethodError => err
|
77
|
-
raise("Couldn't resolve field '#{ast_node.name}' to #{parent_object.class} '#{parent_object}' (resulted in #{err})")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
71
|
value
|
82
72
|
}
|
83
73
|
end
|
data/lib/graphql/scalar_type.rb
CHANGED
@@ -11,8 +11,7 @@ module GraphQL
|
|
11
11
|
# end
|
12
12
|
#
|
13
13
|
class ScalarType < GraphQL::BaseType
|
14
|
-
|
15
|
-
attr_accessor :name, :description
|
14
|
+
accepts_definitions :coerce, :coerce_input, :coerce_result
|
16
15
|
|
17
16
|
def coerce=(proc)
|
18
17
|
self.coerce_input = proc
|
@@ -21,7 +20,9 @@ module GraphQL
|
|
21
20
|
|
22
21
|
def validate_non_null_input(value)
|
23
22
|
result = Query::InputValidationResult.new
|
24
|
-
|
23
|
+
if coerce_non_null_input(value).nil?
|
24
|
+
result.add_problem("Could not coerce value #{JSON.dump(value)} to #{name}")
|
25
|
+
end
|
25
26
|
result
|
26
27
|
end
|
27
28
|
|
data/lib/graphql/schema.rb
CHANGED
@@ -69,7 +69,7 @@ class GraphQL::Schema
|
|
69
69
|
GraphQL::Schema::TypeExpression.new(self, ast_node).type
|
70
70
|
end
|
71
71
|
|
72
|
-
class InvalidTypeError <
|
72
|
+
class InvalidTypeError < GraphQL::Error
|
73
73
|
def initialize(type, errors)
|
74
74
|
super("Type #{type.respond_to?(:name) ? type.name : "Unnamed type" } is invalid: #{errors.join(", ")}")
|
75
75
|
end
|
@@ -11,10 +11,11 @@ module GraphQL
|
|
11
11
|
# It also provides limited access to the {TypeStack} instance,
|
12
12
|
# which tracks state as you climb in and out of different fields.
|
13
13
|
class ValidationContext
|
14
|
-
attr_reader :schema, :document, :errors, :visitor, :fragments, :operations
|
15
|
-
def initialize(
|
16
|
-
@
|
17
|
-
@
|
14
|
+
attr_reader :query, :schema, :document, :errors, :visitor, :fragments, :operations
|
15
|
+
def initialize(query)
|
16
|
+
@query = query
|
17
|
+
@schema = query.schema
|
18
|
+
@document = query.document
|
18
19
|
@fragments = {}
|
19
20
|
@operations = {}
|
20
21
|
|
@@ -18,12 +18,12 @@ class GraphQL::StaticValidation::Validator
|
|
18
18
|
# Validate `document` against the schema. Returns an array of message hashes.
|
19
19
|
# @param document [GraphQL::Language::Nodes::Document]
|
20
20
|
# @return [Array<Hash>]
|
21
|
-
def validate(
|
22
|
-
context = GraphQL::StaticValidation::ValidationContext.new(
|
21
|
+
def validate(query)
|
22
|
+
context = GraphQL::StaticValidation::ValidationContext.new(query)
|
23
23
|
@rules.each do |rules|
|
24
24
|
rules.new.validate(context)
|
25
25
|
end
|
26
|
-
context.visitor.visit(document)
|
26
|
+
context.visitor.visit(query.document)
|
27
27
|
context.errors.map(&:to_h)
|
28
28
|
end
|
29
29
|
end
|
data/lib/graphql/string_type.rb
CHANGED
data/lib/graphql/union_type.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
class GraphQL::UnionType < GraphQL::BaseType
|
12
12
|
include GraphQL::BaseType::HasPossibleTypes
|
13
13
|
attr_accessor :name, :description, :possible_types
|
14
|
-
|
14
|
+
accepts_definitions :possible_types, :resolve_type
|
15
15
|
|
16
16
|
def kind
|
17
17
|
GraphQL::TypeKinds::UNION
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -134,18 +134,12 @@ https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-first-relay-powered-
|
|
134
134
|
|
135
135
|
## To Do
|
136
136
|
|
137
|
-
- Code clean-up
|
138
|
-
- Raise if you try to configure an attribute which doesn't suit the type (ie, if you try to define `resolve` on an ObjectType, it should somehow raise)
|
139
|
-
- make `DefinitionHelpers` more friendly for extension
|
140
137
|
- Interface's possible types should be a property of the schema, not the interface
|
141
138
|
- Type lookup should be by type name (to support reloaded constants in Rails code)
|
142
|
-
- Depth validator should be aware of fragments
|
143
139
|
- Add a complexity validator (reject queries if they're too big)
|
144
|
-
- Add a custom dump for Relay (it expects default value strings to be double-quoted)
|
145
140
|
- Add docs for shared behaviors & DRY code
|
146
141
|
- Optimize the pure-Ruby parser (hand-write, RACC?!)
|
147
|
-
-
|
148
|
-
|
149
|
-
-
|
150
|
-
|
151
|
-
- For a subscription, `graphql` would send an outbound message to the system (allow the host application to manage its own subscriptions via Pusher, ActionCable, whatever)
|
142
|
+
- Revamp the fixture Schema to be more useful (better names, more extensible)
|
143
|
+
- __Subscriptions__
|
144
|
+
- This is a good chance to make an `Operation` abstraction of which `query`, `mutation` and `subscription` are members
|
145
|
+
- For a subscription, `graphql` would send an outbound message to the system (allow the host application to manage its own subscriptions via Pusher, ActionCable, whatever)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::BOOLEAN_TYPE do
|
4
|
+
describe "coerce_input" do
|
5
|
+
def coerce_input(input)
|
6
|
+
GraphQL::BOOLEAN_TYPE.coerce_input(input)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "accepts true and false" do
|
10
|
+
assert_equal true, coerce_input(true)
|
11
|
+
assert_equal false, coerce_input(false)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "rejects other types" do
|
15
|
+
assert_equal nil, coerce_input("true")
|
16
|
+
assert_equal nil, coerce_input(5.5)
|
17
|
+
assert_equal nil, coerce_input(nil)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "date"
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
module Garden
|
5
|
+
module DefinePlantBetween
|
6
|
+
def self.call(plant, plant_range)
|
7
|
+
plant.start_planting_on = plant_range.begin
|
8
|
+
plant.end_planting_on = plant_range.end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Vegetable
|
13
|
+
attr_accessor :name, :start_planting_on, :end_planting_on
|
14
|
+
include GraphQL::Define::InstanceDefinable
|
15
|
+
accepts_definitions :name, plant_between: DefinePlantBetween
|
16
|
+
|
17
|
+
# definition added later:
|
18
|
+
attr_accessor :height
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe GraphQL::Define::InstanceDefinable do
|
23
|
+
describe "extending definitions" do
|
24
|
+
before do
|
25
|
+
Garden::Vegetable.accepts_definitions(:height)
|
26
|
+
end
|
27
|
+
|
28
|
+
after do
|
29
|
+
Garden::Vegetable.own_dictionary.delete(:height)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "accepts after-the-fact definitions" do
|
33
|
+
corn = Garden::Vegetable.define do
|
34
|
+
name "Corn"
|
35
|
+
height 8
|
36
|
+
end
|
37
|
+
|
38
|
+
assert_equal "Corn", corn.name
|
39
|
+
assert_equal 8, corn.height
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "applying custom definitions" do
|
44
|
+
it "uses custom callables" do
|
45
|
+
tomato = Garden::Vegetable.define do
|
46
|
+
name "Tomato"
|
47
|
+
plant_between Date.new(2000, 4, 20)..Date.new(2000, 6, 1)
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_equal "Tomato", tomato.name
|
51
|
+
assert_equal Date.new(2000, 4, 20), tomato.start_planting_on
|
52
|
+
assert_equal Date.new(2000, 6, 1), tomato.end_planting_on
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|