graphql 0.12.0 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/schema.rb +8 -2
- data/lib/graphql/schema/type_map.rb +10 -3
- data/lib/graphql/schema/type_reducer.rb +21 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/interface_type_spec.rb +1 -1
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/schema/type_reducer_spec.rb +26 -7
- data/spec/support/dairy_app.rb +7 -0
- metadata +16 -7
- data/lib/graphql/static_validation/complexity_validator.rb +0 -27
- data/spec/graphql/static_validation/complexity_validator_spec.rb +0 -15
- data/spec/support/calculator_schema.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6682396f1618ee3660ec3120bf597c109d0e2752
|
4
|
+
data.tar.gz: 93a3813ff37f3eb9f4e447eda9aabd3db2688de3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 09eb390035a47146f403a393c1695d7d6355a7d7573c70a277547113f2abad887cded1bb275fa7865c57df5631119487fdd3e50ddf859e705960fc0f67c8fe6e
|
7
|
+
data.tar.gz: d4bef1a775db4a7bd2d58a60c283bce67942ab41bfb551d3d1a7f8080ecdcfc2959d7c0f51872cb80016e77cca68bd04fbede2479217c4b994e6919fb68f86e8
|
data/lib/graphql/schema.rb
CHANGED
@@ -18,11 +18,14 @@ class GraphQL::Schema
|
|
18
18
|
# @param query [GraphQL::ObjectType] the query root for the schema
|
19
19
|
# @param mutation [GraphQL::ObjectType] the mutation root for the schema
|
20
20
|
# @param subscription [GraphQL::ObjectType] the subscription root for the schema
|
21
|
-
|
21
|
+
# @param max_depth [Integer] maximum query nesting (if it's greater, raise an error)
|
22
|
+
# @param types [Array<GraphQL::BaseType>] additional types to include in this schema
|
23
|
+
def initialize(query:, mutation: nil, subscription: nil, max_depth: nil, types: [])
|
22
24
|
@query = query
|
23
25
|
@mutation = mutation
|
24
26
|
@subscription = subscription
|
25
27
|
@max_depth = max_depth
|
28
|
+
@orphan_types = types
|
26
29
|
@directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
|
27
30
|
@static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
|
28
31
|
@rescue_middleware = GraphQL::Schema::RescueMiddleware.new
|
@@ -37,7 +40,10 @@ class GraphQL::Schema
|
|
37
40
|
|
38
41
|
# @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
|
39
42
|
def types
|
40
|
-
@types ||=
|
43
|
+
@types ||= begin
|
44
|
+
all_types = @orphan_types + [query, mutation, GraphQL::Introspection::SchemaType]
|
45
|
+
TypeReducer.find_all(all_types.compact)
|
46
|
+
end
|
41
47
|
end
|
42
48
|
|
43
49
|
# Execute a query on itself.
|
@@ -8,10 +8,14 @@ module GraphQL
|
|
8
8
|
# If you want a type, but want to handle the undefined case, use {#fetch}.
|
9
9
|
class TypeMap
|
10
10
|
extend Forwardable
|
11
|
-
def_delegators :@storage, :key?, :keys, :values
|
11
|
+
def_delegators :@storage, :key?, :keys, :values, :fetch, :to_h
|
12
|
+
|
13
|
+
# Used for detecting deprecated interface member inferrance
|
14
|
+
attr_accessor :safely_discovered_types
|
12
15
|
|
13
16
|
def initialize
|
14
17
|
@storage = {}
|
18
|
+
@safely_discovered_types = []
|
15
19
|
end
|
16
20
|
|
17
21
|
def [](key)
|
@@ -26,8 +30,11 @@ module GraphQL
|
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
29
|
-
def
|
30
|
-
|
33
|
+
def warnings
|
34
|
+
interface_only_types = @storage.values - safely_discovered_types
|
35
|
+
interface_only_types.map do |unsafe_type|
|
36
|
+
"Type \"#{unsafe_type}\" was inferred from an interface's #possible_types. This won't be supported in the next version of GraphQL. Pass this type with the `types:` argument to `Schema.new` instead!"
|
37
|
+
end
|
31
38
|
end
|
32
39
|
end
|
33
40
|
end
|
@@ -21,8 +21,13 @@ class GraphQL::Schema::TypeReducer
|
|
21
21
|
def self.find_all(types)
|
22
22
|
type_map = GraphQL::Schema::TypeMap.new
|
23
23
|
types.reduce(type_map) do |memo, type|
|
24
|
-
|
24
|
+
type_map.safely_discovered_types << type
|
25
|
+
self.new(type.unwrap, memo).result
|
25
26
|
end
|
27
|
+
type_map.warnings.each do |warning|
|
28
|
+
warn(warning)
|
29
|
+
end
|
30
|
+
type_map
|
26
31
|
end
|
27
32
|
|
28
33
|
private
|
@@ -34,22 +39,29 @@ class GraphQL::Schema::TypeReducer
|
|
34
39
|
reduce_type(field.type, type_hash)
|
35
40
|
field.arguments.each do |name, argument|
|
36
41
|
reduce_type(argument.type, type_hash)
|
42
|
+
safely_discovered(type_hash, argument.type)
|
37
43
|
end
|
44
|
+
safely_discovered(type_hash, field.type)
|
38
45
|
end
|
39
46
|
end
|
40
47
|
if type.kind.object?
|
41
48
|
type.interfaces.each do |interface|
|
42
49
|
reduce_type(interface, type_hash)
|
50
|
+
safely_discovered(type_hash, interface)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
if type.kind.resolves?
|
46
54
|
type.possible_types.each do |possible_type|
|
47
55
|
reduce_type(possible_type, type_hash)
|
56
|
+
if type.kind.union?
|
57
|
+
safely_discovered(type_hash, possible_type)
|
58
|
+
end
|
48
59
|
end
|
49
60
|
end
|
50
61
|
if type.kind.input_object?
|
51
62
|
type.input_fields.each do |name, input_field|
|
52
63
|
reduce_type(input_field.type, type_hash)
|
64
|
+
safely_discovered(type_hash, input_field.type)
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|
@@ -72,4 +84,12 @@ class GraphQL::Schema::TypeReducer
|
|
72
84
|
raise GraphQL::Schema::InvalidTypeError.new(type, errors)
|
73
85
|
end
|
74
86
|
end
|
87
|
+
|
88
|
+
def safely_discovered(type_hash, type)
|
89
|
+
inner_type = type.unwrap
|
90
|
+
if !type_hash.safely_discovered_types.include?(inner_type)
|
91
|
+
|
92
|
+
type_hash.safely_discovered_types << inner_type
|
93
|
+
end
|
94
|
+
end
|
75
95
|
end
|
data/lib/graphql/version.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe GraphQL::InterfaceType do
|
4
4
|
let(:interface) { EdibleInterface }
|
5
5
|
it 'has possible types' do
|
6
|
-
assert_equal([CheeseType, MilkType], interface.possible_types)
|
6
|
+
assert_equal([CheeseType, MilkType, HoneyType], interface.possible_types)
|
7
7
|
end
|
8
8
|
|
9
9
|
it 'resolves types for objects' do
|
@@ -57,7 +57,7 @@ describe GraphQL::Introspection::TypeType do
|
|
57
57
|
"animalProduct" => {
|
58
58
|
"name"=>"AnimalProduct",
|
59
59
|
"kind"=>"INTERFACE",
|
60
|
-
"possibleTypes"=>[{"name"=>"Cheese"}, {"name"=>"Milk"}],
|
60
|
+
"possibleTypes"=>[{"name"=>"Cheese"}, {"name"=>"Milk"}, {"name"=>"Honey"}],
|
61
61
|
"fields"=>[
|
62
62
|
{"name"=>"source"},
|
63
63
|
]
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe GraphQL::Schema::TypeReducer do
|
4
|
+
let(:type_map) { GraphQL::Schema::TypeMap.new }
|
4
5
|
it 'finds types from a single type and its fields' do
|
5
|
-
reducer = GraphQL::Schema::TypeReducer.new(CheeseType,
|
6
|
+
reducer = GraphQL::Schema::TypeReducer.new(CheeseType, type_map)
|
6
7
|
expected = {
|
7
8
|
"Cheese" => CheeseType,
|
8
9
|
"Float" => GraphQL::FLOAT_TYPE,
|
@@ -13,13 +14,14 @@ describe GraphQL::Schema::TypeReducer do
|
|
13
14
|
"Milk" => MilkType,
|
14
15
|
"ID" => GraphQL::ID_TYPE,
|
15
16
|
"AnimalProduct" => AnimalProductInterface,
|
17
|
+
"Honey" => HoneyType,
|
16
18
|
}
|
17
19
|
assert_equal(expected.keys, reducer.result.keys)
|
18
|
-
assert_equal(expected, reducer.result)
|
20
|
+
assert_equal(expected, reducer.result.to_h)
|
19
21
|
end
|
20
22
|
|
21
23
|
it 'finds type from arguments' do
|
22
|
-
reducer = GraphQL::Schema::TypeReducer.new(QueryType,
|
24
|
+
reducer = GraphQL::Schema::TypeReducer.new(QueryType, type_map)
|
23
25
|
assert_equal(DairyProductInputType, reducer.result["DairyProductInput"])
|
24
26
|
end
|
25
27
|
|
@@ -34,13 +36,13 @@ describe GraphQL::Schema::TypeReducer do
|
|
34
36
|
input_field :child, type_child
|
35
37
|
end
|
36
38
|
|
37
|
-
reducer = GraphQL::Schema::TypeReducer.new(type_parent,
|
39
|
+
reducer = GraphQL::Schema::TypeReducer.new(type_parent, type_map)
|
38
40
|
expected = {
|
39
41
|
"InputTypeParent" => type_parent,
|
40
42
|
"InputTypeChild" => type_child,
|
41
43
|
"String" => GraphQL::STRING_TYPE
|
42
44
|
}
|
43
|
-
assert_equal(expected, reducer.result)
|
45
|
+
assert_equal(expected, reducer.result.to_h)
|
44
46
|
end
|
45
47
|
|
46
48
|
describe 'when a type is invalid' do
|
@@ -59,12 +61,12 @@ describe GraphQL::Schema::TypeReducer do
|
|
59
61
|
}
|
60
62
|
|
61
63
|
it 'raises an InvalidTypeError when passed nil' do
|
62
|
-
reducer = GraphQL::Schema::TypeReducer.new(invalid_type,
|
64
|
+
reducer = GraphQL::Schema::TypeReducer.new(invalid_type, type_map)
|
63
65
|
assert_raises(GraphQL::Schema::InvalidTypeError) { reducer.result }
|
64
66
|
end
|
65
67
|
|
66
68
|
it 'raises an InvalidTypeError when passed an object that isnt a GraphQL::BaseType' do
|
67
|
-
reducer = GraphQL::Schema::TypeReducer.new(another_invalid_type,
|
69
|
+
reducer = GraphQL::Schema::TypeReducer.new(another_invalid_type, type_map)
|
68
70
|
assert_raises(GraphQL::Schema::InvalidTypeError) { reducer.result }
|
69
71
|
end
|
70
72
|
end
|
@@ -93,4 +95,21 @@ describe GraphQL::Schema::TypeReducer do
|
|
93
95
|
assert_raises(RuntimeError) { type_map["SomeType"] }
|
94
96
|
end
|
95
97
|
end
|
98
|
+
|
99
|
+
describe "when a type can only be inferred through an interface" do
|
100
|
+
it "logs a warning" do
|
101
|
+
out, err = capture_io do
|
102
|
+
GraphQL::Schema::TypeReducer.find_all([EdibleInterface])
|
103
|
+
end
|
104
|
+
assert_match /Type "Milk" was inferred from an interface/, err
|
105
|
+
assert_match /Type "Honey" was inferred from an interface/, err
|
106
|
+
end
|
107
|
+
|
108
|
+
it "is ok if the types are passed in explicitly" do
|
109
|
+
out, err = capture_io do
|
110
|
+
GraphQL::Schema::TypeReducer.find_all([EdibleInterface, HoneyType, MilkType])
|
111
|
+
end
|
112
|
+
assert_equal "", err
|
113
|
+
end
|
114
|
+
end
|
96
115
|
end
|
data/spec/support/dairy_app.rb
CHANGED
@@ -73,6 +73,13 @@ MilkType = GraphQL::ObjectType.define do
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
# No actual data; This type is an "orphan", only accessible through Interfaces
|
77
|
+
HoneyType = GraphQL::ObjectType.define do
|
78
|
+
name 'Honey'
|
79
|
+
description "Sweet, dehydrated bee barf"
|
80
|
+
interfaces [EdibleInterface, AnimalProductInterface]
|
81
|
+
end
|
82
|
+
|
76
83
|
DairyType = GraphQL::ObjectType.define do
|
77
84
|
name 'Dairy'
|
78
85
|
description 'A farm where milk is harvested and cheese is produced'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
4
|
+
version: 0.12.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parslet
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '2.4'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: listen
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.0.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 3.0.0
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: minitest
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -243,7 +257,6 @@ files:
|
|
243
257
|
- lib/graphql/static_validation.rb
|
244
258
|
- lib/graphql/static_validation/all_rules.rb
|
245
259
|
- lib/graphql/static_validation/arguments_validator.rb
|
246
|
-
- lib/graphql/static_validation/complexity_validator.rb
|
247
260
|
- lib/graphql/static_validation/literal_validator.rb
|
248
261
|
- lib/graphql/static_validation/message.rb
|
249
262
|
- lib/graphql/static_validation/rules/argument_literals_are_compatible.rb
|
@@ -310,7 +323,6 @@ files:
|
|
310
323
|
- spec/graphql/schema/type_reducer_spec.rb
|
311
324
|
- spec/graphql/schema/type_validator_spec.rb
|
312
325
|
- spec/graphql/schema_spec.rb
|
313
|
-
- spec/graphql/static_validation/complexity_validator_spec.rb
|
314
326
|
- spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
|
315
327
|
- spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
|
316
328
|
- spec/graphql/static_validation/rules/directives_are_defined_spec.rb
|
@@ -333,7 +345,6 @@ files:
|
|
333
345
|
- spec/graphql/string_type_spec.rb
|
334
346
|
- spec/graphql/union_type_spec.rb
|
335
347
|
- spec/spec_helper.rb
|
336
|
-
- spec/support/calculator_schema.rb
|
337
348
|
- spec/support/dairy_app.rb
|
338
349
|
- spec/support/dairy_data.rb
|
339
350
|
- spec/support/minimum_input_object.rb
|
@@ -403,7 +414,6 @@ test_files:
|
|
403
414
|
- spec/graphql/schema/type_reducer_spec.rb
|
404
415
|
- spec/graphql/schema/type_validator_spec.rb
|
405
416
|
- spec/graphql/schema_spec.rb
|
406
|
-
- spec/graphql/static_validation/complexity_validator_spec.rb
|
407
417
|
- spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
|
408
418
|
- spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
|
409
419
|
- spec/graphql/static_validation/rules/directives_are_defined_spec.rb
|
@@ -426,7 +436,6 @@ test_files:
|
|
426
436
|
- spec/graphql/string_type_spec.rb
|
427
437
|
- spec/graphql/union_type_spec.rb
|
428
438
|
- spec/spec_helper.rb
|
429
|
-
- spec/support/calculator_schema.rb
|
430
439
|
- spec/support/dairy_app.rb
|
431
440
|
- spec/support/dairy_data.rb
|
432
441
|
- spec/support/minimum_input_object.rb
|
@@ -1,27 +0,0 @@
|
|
1
|
-
class GraphQL::StaticValidation::ComplexityValidator
|
2
|
-
include GraphQL::StaticValidation::Message::MessageHelper
|
3
|
-
|
4
|
-
def initialize(max_fields:, list_multiplier:)
|
5
|
-
@max_fields = max_fields
|
6
|
-
@list_multiplier = list_multiplier
|
7
|
-
end
|
8
|
-
|
9
|
-
def validate(context)
|
10
|
-
visitor = context.visitor
|
11
|
-
complexity = 0
|
12
|
-
visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) {
|
13
|
-
field_type = field_definition.type
|
14
|
-
if field_type.kind.list?
|
15
|
-
complexity += list_multiplier
|
16
|
-
else
|
17
|
-
complexity += 1
|
18
|
-
end
|
19
|
-
}
|
20
|
-
|
21
|
-
visitor[GraphQL::Language::Nodes::Document].exit << -> (node, parent) {
|
22
|
-
if complexity > @max_fields
|
23
|
-
context.errors << message("This query is too complex. Request fewer fields.", node)
|
24
|
-
end
|
25
|
-
}
|
26
|
-
end
|
27
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# require "spec_helper"
|
2
|
-
#
|
3
|
-
# describe GraphQL::StaticValidation::ComplexityValidator do
|
4
|
-
# let(:query_string) {query_string)}
|
5
|
-
# let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, rules: [complexity_validator]) }
|
6
|
-
# let(:errors) { validator.validate(document) }
|
7
|
-
# let(:complexity_validator) { GraphQL::StaticValidation::ComplexityValidator.new(max_fields: 6, list_multiplier: 2) }
|
8
|
-
#
|
9
|
-
# describe "too many fields" do
|
10
|
-
# it "adds errors if there are too many fields"
|
11
|
-
# it "counts fields for each time they're included from fragments"
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# it "multiplies fields inside list types"
|
15
|
-
# end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# This GraphQL Schema performs operations on numbers.
|
2
|
-
# It can perform operations on values you give it and return values
|
3
|
-
# It also maintains some state which can be updated or read
|
4
|
-
module Calculator
|
5
|
-
# You can reduce a list of values to a single value
|
6
|
-
REDUCERS = {
|
7
|
-
"SUM" => -> (values) { [values.inject(&:+)] },
|
8
|
-
"MAX" => :max.to_proc,
|
9
|
-
"MIN" => :min.to_proc,
|
10
|
-
}
|
11
|
-
|
12
|
-
ReducerEnum = GraphQL::EnumType.define do
|
13
|
-
REDUCERS.map do |op_name, op_proc|
|
14
|
-
value(op_name)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# You can make a new list by transforming each member
|
19
|
-
MAPPERS = {
|
20
|
-
"ADD" => -> (val, arg) { val + arg },
|
21
|
-
"SUB" => -> (val, arg) { val - arg },
|
22
|
-
"EQ" => -> (val, arg) { val == arg ? 1 : 0},
|
23
|
-
}
|
24
|
-
|
25
|
-
MapperEnum = GraphQL::EnumType.define do
|
26
|
-
MAPPERS.map do |op_name, op_proc|
|
27
|
-
value(op_name)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Input a list of integers
|
32
|
-
ListOfInts = GraphQL::INT_TYPE.to_list_type
|
33
|
-
|
34
|
-
# Expose a list of values and perform operations
|
35
|
-
ValuesType = GraphQL::ObjectType.define do
|
36
|
-
field :values, ListOfInts do
|
37
|
-
resolve -> (obj, _args, _ctx) { obj }
|
38
|
-
end
|
39
|
-
|
40
|
-
# Get a single value based on the values in this list
|
41
|
-
field :reduce, types.Int do
|
42
|
-
argument :operation, !ReducerEnum
|
43
|
-
resolve -> (obj, args, ctx) {
|
44
|
-
reduce_name = args[:operation]
|
45
|
-
reduce_fn = REDUCERS[reduce_name]
|
46
|
-
reduce_fn.call(obj)
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
# Handle self-referential fields by wrapping the type in a proc.
|
51
|
-
# It will be lazy-eval'ed
|
52
|
-
field :map, -> { ValuesType } do
|
53
|
-
argument :operation, !MapperEnum
|
54
|
-
argument :argument, !types.Int
|
55
|
-
resolve -> (obj, args, ctx) {
|
56
|
-
op_arg = args[:argument]
|
57
|
-
op_name = args[:operation]
|
58
|
-
op_func = MAPPERS[op_name]
|
59
|
-
obj.map { |item| op_func.call(item, op_arg) }
|
60
|
-
}
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
QueryType = GraphQL::ObjectType.define do
|
65
|
-
name("Query")
|
66
|
-
|
67
|
-
field :perform, ValuesType do
|
68
|
-
argument :initial, !ListOfInts
|
69
|
-
resolve -> (obj, args, ctx) { args[:initial] }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
Schema = GraphQL::Schema.new(query: QueryType)
|
74
|
-
end
|