graphql 0.12.0 → 0.12.1
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/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
|