graphql 0.12.1 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql.rb +31 -41
- data/lib/graphql/argument.rb +23 -21
- data/lib/graphql/base_type.rb +5 -8
- data/lib/graphql/define/assign_argument.rb +5 -2
- data/lib/graphql/define/type_definer.rb +2 -1
- data/lib/graphql/directive.rb +34 -36
- data/lib/graphql/directive/include_directive.rb +3 -7
- data/lib/graphql/directive/skip_directive.rb +3 -7
- data/lib/graphql/enum_type.rb +78 -76
- data/lib/graphql/execution_error.rb +1 -3
- data/lib/graphql/field.rb +99 -95
- data/lib/graphql/input_object_type.rb +49 -47
- data/lib/graphql/interface_type.rb +31 -34
- data/lib/graphql/introspection.rb +19 -18
- data/lib/graphql/introspection/directive_location_enum.rb +8 -0
- data/lib/graphql/introspection/directive_type.rb +1 -3
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/fields_field.rb +1 -1
- data/lib/graphql/introspection/introspection_query.rb +1 -3
- data/lib/graphql/introspection/possible_types_field.rb +7 -1
- data/lib/graphql/introspection/schema_field.rb +13 -9
- data/lib/graphql/introspection/type_by_name_field.rb +13 -17
- data/lib/graphql/introspection/typename_field.rb +12 -8
- data/lib/graphql/language.rb +5 -9
- data/lib/graphql/language/lexer.rb +668 -0
- data/lib/graphql/language/lexer.rl +149 -0
- data/lib/graphql/language/parser.rb +842 -116
- data/lib/graphql/language/parser.y +264 -0
- data/lib/graphql/language/token.rb +21 -0
- data/lib/graphql/list_type.rb +33 -31
- data/lib/graphql/non_null_type.rb +33 -31
- data/lib/graphql/object_type.rb +52 -55
- data/lib/graphql/query.rb +83 -80
- data/lib/graphql/query/context.rb +5 -1
- data/lib/graphql/query/directive_resolution.rb +16 -0
- data/lib/graphql/query/executor.rb +3 -3
- data/lib/graphql/query/input_validation_result.rb +17 -15
- data/lib/graphql/query/serial_execution.rb +5 -5
- data/lib/graphql/query/serial_execution/execution_context.rb +4 -3
- data/lib/graphql/query/serial_execution/selection_resolution.rb +19 -21
- data/lib/graphql/query/serial_execution/value_resolution.rb +1 -1
- data/lib/graphql/query/type_resolver.rb +22 -18
- data/lib/graphql/query/variable_validation_error.rb +14 -12
- data/lib/graphql/schema.rb +87 -77
- data/lib/graphql/schema/each_item_validator.rb +16 -12
- data/lib/graphql/schema/field_validator.rb +14 -10
- data/lib/graphql/schema/implementation_validator.rb +26 -22
- data/lib/graphql/schema/middleware_chain.rb +2 -1
- data/lib/graphql/schema/possible_types.rb +34 -0
- data/lib/graphql/schema/printer.rb +122 -120
- data/lib/graphql/schema/type_expression.rb +1 -0
- data/lib/graphql/schema/type_map.rb +3 -10
- data/lib/graphql/schema/type_reducer.rb +65 -81
- data/lib/graphql/schema/type_validator.rb +45 -41
- data/lib/graphql/static_validation.rb +7 -9
- data/lib/graphql/static_validation/all_rules.rb +29 -24
- data/lib/graphql/static_validation/arguments_validator.rb +39 -35
- data/lib/graphql/static_validation/literal_validator.rb +44 -40
- data/lib/graphql/static_validation/message.rb +30 -26
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +15 -11
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +14 -10
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +16 -12
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +59 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +25 -21
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +28 -24
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +84 -80
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +49 -43
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +22 -17
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +19 -15
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +25 -20
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +36 -23
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +29 -25
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +21 -17
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +79 -70
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +24 -20
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +122 -119
- data/lib/graphql/static_validation/type_stack.rb +138 -129
- data/lib/graphql/static_validation/validator.rb +29 -25
- data/lib/graphql/type_kinds.rb +42 -40
- data/lib/graphql/union_type.rb +22 -16
- data/lib/graphql/version.rb +1 -1
- data/readme.md +12 -27
- data/spec/graphql/base_type_spec.rb +3 -3
- data/spec/graphql/directive_spec.rb +10 -18
- data/spec/graphql/enum_type_spec.rb +7 -7
- data/spec/graphql/execution_error_spec.rb +1 -1
- data/spec/graphql/field_spec.rb +14 -13
- data/spec/graphql/id_type_spec.rb +6 -6
- data/spec/graphql/input_object_type_spec.rb +39 -39
- data/spec/graphql/interface_type_spec.rb +16 -32
- data/spec/graphql/introspection/directive_type_spec.rb +5 -9
- data/spec/graphql/introspection/input_value_type_spec.rb +10 -4
- data/spec/graphql/introspection/introspection_query_spec.rb +2 -2
- data/spec/graphql/introspection/schema_type_spec.rb +2 -2
- data/spec/graphql/introspection/type_type_spec.rb +34 -6
- data/spec/graphql/language/parser_spec.rb +299 -105
- data/spec/graphql/language/visitor_spec.rb +4 -4
- data/spec/graphql/list_type_spec.rb +11 -11
- data/spec/graphql/object_type_spec.rb +10 -10
- data/spec/graphql/query/arguments_spec.rb +7 -7
- data/spec/graphql/query/context_spec.rb +11 -3
- data/spec/graphql/query/executor_spec.rb +26 -19
- data/spec/graphql/query/serial_execution/execution_context_spec.rb +6 -6
- data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -2
- data/spec/graphql/query/type_resolver_spec.rb +3 -3
- data/spec/graphql/query_spec.rb +6 -38
- data/spec/graphql/scalar_type_spec.rb +28 -19
- data/spec/graphql/schema/field_validator_spec.rb +1 -1
- data/spec/graphql/schema/middleware_chain_spec.rb +12 -1
- data/spec/graphql/schema/printer_spec.rb +12 -4
- data/spec/graphql/schema/rescue_middleware_spec.rb +1 -1
- data/spec/graphql/schema/type_expression_spec.rb +2 -2
- data/spec/graphql/schema/type_reducer_spec.rb +21 -36
- data/spec/graphql/schema/type_validator_spec.rb +9 -9
- data/spec/graphql/schema_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +5 -5
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +39 -0
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +5 -5
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +5 -5
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +3 -1
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
- data/spec/graphql/static_validation/type_stack_spec.rb +3 -2
- data/spec/graphql/static_validation/validator_spec.rb +26 -6
- data/spec/graphql/union_type_spec.rb +5 -4
- data/spec/spec_helper.rb +2 -5
- data/spec/support/dairy_app.rb +30 -9
- data/spec/support/dairy_data.rb +1 -1
- data/spec/support/star_wars_data.rb +26 -26
- data/spec/support/star_wars_schema.rb +1 -1
- metadata +40 -21
- data/lib/graphql/language/transform.rb +0 -113
- data/lib/graphql/query/directive_chain.rb +0 -44
- data/lib/graphql/repl.rb +0 -27
- data/spec/graphql/language/transform_spec.rb +0 -156
data/lib/graphql/schema.rb
CHANGED
@@ -1,93 +1,103 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
module GraphQL
|
2
|
+
# A GraphQL schema which may be queried with {GraphQL::Query}.
|
3
|
+
class Schema
|
4
|
+
extend Forwardable
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
DIRECTIVES = [GraphQL::Directive::SkipDirective, GraphQL::Directive::IncludeDirective]
|
7
|
+
DYNAMIC_FIELDS = ["__type", "__typename", "__schema"]
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
attr_reader :query, :mutation, :subscription, :directives, :static_validator
|
10
|
+
attr_accessor :max_depth
|
11
|
+
# Override these if you don't want the default executor:
|
12
|
+
attr_accessor :query_execution_strategy,
|
13
|
+
:mutation_execution_strategy,
|
14
|
+
:subscription_execution_strategy
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
# @return [Array<#call>] Middlewares suitable for MiddlewareChain, applied to fields during execution
|
17
|
+
attr_reader :middleware
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
19
|
+
# @param query [GraphQL::ObjectType] the query root for the schema
|
20
|
+
# @param mutation [GraphQL::ObjectType] the mutation root for the schema
|
21
|
+
# @param subscription [GraphQL::ObjectType] the subscription root for the schema
|
22
|
+
# @param max_depth [Integer] maximum query nesting (if it's greater, raise an error)
|
23
|
+
# @param types [Array<GraphQL::BaseType>] additional types to include in this schema
|
24
|
+
def initialize(query:, mutation: nil, subscription: nil, max_depth: nil, types: [])
|
25
|
+
@query = query
|
26
|
+
@mutation = mutation
|
27
|
+
@subscription = subscription
|
28
|
+
@max_depth = max_depth
|
29
|
+
@orphan_types = types
|
30
|
+
@directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
|
31
|
+
@static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
|
32
|
+
@rescue_middleware = GraphQL::Schema::RescueMiddleware.new
|
33
|
+
@middleware = [@rescue_middleware]
|
34
|
+
# Default to the built-in execution strategy:
|
35
|
+
self.query_execution_strategy = GraphQL::Query::SerialExecution
|
36
|
+
self.mutation_execution_strategy = GraphQL::Query::SerialExecution
|
37
|
+
self.subscription_execution_strategy = GraphQL::Query::SerialExecution
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
+
def_delegators :@rescue_middleware, :rescue_from, :remove_handler
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
# @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
|
43
|
+
def types
|
44
|
+
@types ||= begin
|
45
|
+
all_types = @orphan_types + [query, mutation, GraphQL::Introspection::SchemaType]
|
46
|
+
TypeReducer.find_all(all_types.compact)
|
47
|
+
end
|
46
48
|
end
|
47
|
-
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
50
|
+
# Execute a query on itself.
|
51
|
+
# See {Query#initialize} for arguments.
|
52
|
+
# @return [Hash] query result, ready to be serialized as JSON
|
53
|
+
def execute(*args)
|
54
|
+
query = GraphQL::Query.new(self, *args)
|
55
|
+
query.result
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
58
|
+
# Resolve field named `field_name` for type `parent_type`.
|
59
|
+
# Handles dynamic fields `__typename`, `__type` and `__schema`, too
|
60
|
+
def get_field(parent_type, field_name)
|
61
|
+
defined_field = parent_type.get_field(field_name)
|
62
|
+
if defined_field
|
63
|
+
defined_field
|
64
|
+
elsif field_name == "__typename"
|
65
|
+
GraphQL::Introspection::TypenameField.create(parent_type)
|
66
|
+
elsif field_name == "__schema" && parent_type == query
|
67
|
+
GraphQL::Introspection::SchemaField.create(self)
|
68
|
+
elsif field_name == "__type" && parent_type == query
|
69
|
+
GraphQL::Introspection::TypeByNameField.create(self.types)
|
70
|
+
else
|
71
|
+
nil
|
72
|
+
end
|
71
73
|
end
|
72
|
-
end
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
75
|
+
def type_from_ast(ast_node)
|
76
|
+
GraphQL::Schema::TypeExpression.new(self, ast_node).type
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param type_defn [GraphQL::InterfaceType, GraphQL::UnionType] the type whose members you want to retrieve
|
80
|
+
# @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema
|
81
|
+
def possible_types(type_defn)
|
82
|
+
@interface_possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
|
83
|
+
@interface_possible_types.possible_types(type_defn)
|
84
|
+
end
|
77
85
|
|
78
|
-
|
79
|
-
|
80
|
-
|
86
|
+
class InvalidTypeError < GraphQL::Error
|
87
|
+
def initialize(type, name)
|
88
|
+
super("#{name} has an invalid type: must be an instance of GraphQL::BaseType, not #{type.class.inspect} (#{type.inspect})")
|
89
|
+
end
|
81
90
|
end
|
82
91
|
end
|
83
92
|
end
|
84
93
|
|
85
|
-
require
|
86
|
-
require
|
87
|
-
require
|
88
|
-
require
|
89
|
-
require
|
90
|
-
require
|
91
|
-
require
|
92
|
-
require
|
93
|
-
require
|
94
|
+
require "graphql/schema/each_item_validator"
|
95
|
+
require "graphql/schema/field_validator"
|
96
|
+
require "graphql/schema/implementation_validator"
|
97
|
+
require "graphql/schema/middleware_chain"
|
98
|
+
require "graphql/schema/rescue_middleware"
|
99
|
+
require "graphql/schema/possible_types"
|
100
|
+
require "graphql/schema/type_expression"
|
101
|
+
require "graphql/schema/type_reducer"
|
102
|
+
require "graphql/schema/type_map"
|
103
|
+
require "graphql/schema/type_validator"
|
@@ -1,16 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module GraphQL
|
2
|
+
class Schema
|
3
|
+
class EachItemValidator
|
4
|
+
def initialize(errors)
|
5
|
+
@errors = errors
|
6
|
+
end
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
def validate(items, as:, must_be:)
|
9
|
+
if !items.is_a?(Array)
|
10
|
+
@errors << "#{as} must be an Array, not #{items.inspect}"
|
11
|
+
return
|
12
|
+
else
|
13
|
+
invalid_items = items.select {|k| !yield(k) }
|
14
|
+
if invalid_items.any?
|
15
|
+
@errors << "#{as} must be #{must_be}, but some aren't: #{invalid_items.map(&:to_s).join(", ")}"
|
16
|
+
end
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
@@ -1,13 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
module GraphQL
|
2
|
+
class Schema
|
3
|
+
class FieldValidator
|
4
|
+
def validate(field, errors)
|
5
|
+
implementation = GraphQL::Schema::ImplementationValidator.new(field, as: "Field", errors: errors)
|
6
|
+
implementation.must_respond_to(:name)
|
7
|
+
implementation.must_respond_to(:type)
|
8
|
+
implementation.must_respond_to(:description)
|
9
|
+
implementation.must_respond_to(:arguments) do |arguments|
|
10
|
+
validator = GraphQL::Schema::EachItemValidator.new(errors)
|
11
|
+
validator.validate(arguments.keys, as: "#{field.name}.arguments keys", must_be: "Strings") { |k| k.is_a?(String) }
|
12
|
+
end
|
13
|
+
implementation.must_respond_to(:deprecation_reason)
|
14
|
+
end
|
10
15
|
end
|
11
|
-
implementation.must_respond_to(:deprecation_reason)
|
12
16
|
end
|
13
17
|
end
|
@@ -1,26 +1,30 @@
|
|
1
|
-
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
module GraphQL
|
2
|
+
class Schema
|
3
|
+
# A helper to ensure `object` implements the concept `as`
|
4
|
+
class ImplementationValidator
|
5
|
+
attr_reader :object, :errors, :implementation_as
|
6
|
+
def initialize(object, as:, errors:)
|
7
|
+
@object = object
|
8
|
+
@implementation_as = as
|
9
|
+
@errors = errors
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
# Ensure the object responds to `method_name`.
|
13
|
+
# If `block_given?`, yield the return value of that method
|
14
|
+
# If provided, use `as` in the error message, overriding class-level `as`.
|
15
|
+
def must_respond_to(method_name, args: [], as: nil)
|
16
|
+
local_as = as || implementation_as
|
17
|
+
method_signature = "##{method_name}(#{args.join(", ")})"
|
18
|
+
if !object.respond_to?(method_name)
|
19
|
+
errors << "#{object.to_s} must respond to #{method_signature} to be a #{local_as}"
|
20
|
+
elsif block_given?
|
21
|
+
return_value = object.public_send(method_name)
|
22
|
+
if return_value.nil?
|
23
|
+
errors << "#{object.to_s} must return a value for #{method_signature} to be a #{local_as}"
|
24
|
+
else
|
25
|
+
yield(return_value)
|
26
|
+
end
|
27
|
+
end
|
24
28
|
end
|
25
29
|
end
|
26
30
|
end
|
@@ -17,7 +17,8 @@ module GraphQL
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Run the next step in the chain, passing in arguments and handle to the next step
|
20
|
-
def call
|
20
|
+
def call(next_arguments = @arguments)
|
21
|
+
@arguments = next_arguments
|
21
22
|
next_step = steps.shift
|
22
23
|
next_middleware = self
|
23
24
|
next_step.call(*arguments, next_middleware)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module GraphQL
|
2
|
+
class Schema
|
3
|
+
# Find the members of a union or interface within a given schema.
|
4
|
+
#
|
5
|
+
# (Although its members never change, unions are handled this way to simplify execution code.)
|
6
|
+
#
|
7
|
+
# Internally, the calculation is cached. It's assumed that schema members _don't_ change after creating the schema!
|
8
|
+
#
|
9
|
+
# @example Get an interface's possible types
|
10
|
+
# possible_types = GraphQL::Schema::PossibleTypes(MySchema)
|
11
|
+
# possible_types.possible_types(MyInterface)
|
12
|
+
# # => [MyObjectType, MyOtherObjectType]
|
13
|
+
class PossibleTypes
|
14
|
+
def initialize(schema)
|
15
|
+
@object_types = schema.types.values.select { |type| type.kind.object? }
|
16
|
+
|
17
|
+
@storage = Hash.new do |hash, key|
|
18
|
+
hash[key] = @object_types.select { |type| type.interfaces.include?(key) }.sort_by(&:name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def possible_types(type_defn)
|
23
|
+
case type_defn
|
24
|
+
when GraphQL::UnionType
|
25
|
+
type_defn.possible_types
|
26
|
+
when GraphQL::InterfaceType
|
27
|
+
@storage[type_defn]
|
28
|
+
else
|
29
|
+
raise "#{type_defn} doesn't have possible types"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,154 +1,156 @@
|
|
1
1
|
module GraphQL
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
2
|
+
class Schema
|
3
|
+
# Used to convert your {GraphQL::Schema} to a GraphQL schema string
|
4
|
+
#
|
5
|
+
# @example print your schema to standard output
|
6
|
+
# Schema = GraphQL::Schema.new(query: QueryType)
|
7
|
+
# puts GraphQL::Schema::Printer.print_schema(Schema)
|
8
|
+
#
|
9
|
+
module Printer
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# Return a GraphQL schema string for the defined types in the schema
|
13
|
+
# @param schema [GraphQL::Schema]
|
14
|
+
def print_schema(schema)
|
15
|
+
print_filtered_schema(schema, method(:is_defined_type))
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
# Return the GraphQL schema string for the introspection type system
|
19
|
+
def print_introspection_schema
|
20
|
+
query_root = ObjectType.define do
|
21
|
+
name "Query"
|
22
|
+
end
|
23
|
+
schema = Schema.new(query: query_root)
|
24
|
+
print_filtered_schema(schema, method(:is_introspection_type))
|
21
25
|
end
|
22
|
-
schema = Schema.new(query: query_root)
|
23
|
-
print_filtered_schema(schema, method(:is_introspection_type))
|
24
|
-
end
|
25
26
|
|
26
|
-
|
27
|
+
private
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def print_filtered_schema(schema, type_filter)
|
30
|
+
types = schema.types.values.select{ |type| type_filter.call(type) }.sort_by(&:name)
|
31
|
+
types.map{ |type| print_type(type) }.join("\n\n")
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
BUILTIN_SCALARS = Set.new(["String", "Boolean", "Int", "Float", "ID"])
|
35
|
+
private_constant :BUILTIN_SCALARS
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def is_introspection_type(type)
|
38
|
+
type.name.start_with?("__")
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
def is_defined_type(type)
|
42
|
+
!is_introspection_type(type) && !BUILTIN_SCALARS.include?(type.name)
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
def print_type(type)
|
46
|
+
TypeKindPrinters::STRATEGIES.fetch(type.kind).print(type)
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
module TypeKindPrinters
|
50
|
+
module FieldPrinter
|
51
|
+
def print_fields(type)
|
52
|
+
type.all_fields.map{ |field| " #{field.name}#{print_args(field)}: #{field.type}" }.join("\n")
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
def print_args(field)
|
56
|
+
return if field.arguments.empty?
|
57
|
+
"(#{field.arguments.values.map{ |arg| print_input_value(arg) }.join(", ")})"
|
58
|
+
end
|
59
|
+
|
60
|
+
def print_input_value(arg)
|
61
|
+
if arg.default_value.nil?
|
62
|
+
default_string = nil
|
63
|
+
else
|
64
|
+
default_string = " = #{print_value(arg.default_value, arg.type)}"
|
65
|
+
end
|
58
66
|
|
59
|
-
|
60
|
-
if arg.default_value.nil?
|
61
|
-
default_string = nil
|
62
|
-
else
|
63
|
-
default_string = " = #{print_value(arg.default_value, arg.type)}"
|
67
|
+
"#{arg.name}: #{arg.type.to_s}#{default_string}"
|
64
68
|
end
|
65
69
|
|
66
|
-
|
70
|
+
def print_value(value, type)
|
71
|
+
case type
|
72
|
+
when FLOAT_TYPE
|
73
|
+
value.to_f.inspect
|
74
|
+
when INT_TYPE
|
75
|
+
value.to_i.inspect
|
76
|
+
when BOOLEAN_TYPE
|
77
|
+
(!!value).inspect
|
78
|
+
when ScalarType, ID_TYPE, STRING_TYPE
|
79
|
+
value.to_s.inspect
|
80
|
+
when EnumType
|
81
|
+
value.to_s
|
82
|
+
when InputObjectType
|
83
|
+
fields = value.to_h.map{ |field_name, field_value|
|
84
|
+
field_type = type.input_fields.fetch(field_name.to_s).type
|
85
|
+
"#{field_name}: #{print_value(field_value, field_type)}"
|
86
|
+
}.join(", ")
|
87
|
+
"{ #{fields} }"
|
88
|
+
when NonNullType
|
89
|
+
print_value(value, type.of_type)
|
90
|
+
when ListType
|
91
|
+
"[#{value.to_a.map{ |v| print_value(v, type.of_type) }.join(", ")}]"
|
92
|
+
else
|
93
|
+
raise NotImplementedError, "Unexpected value type #{type.inspect}"
|
94
|
+
end
|
95
|
+
end
|
67
96
|
end
|
68
97
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
value.to_f.inspect
|
73
|
-
when INT_TYPE
|
74
|
-
value.to_i.inspect
|
75
|
-
when BOOLEAN_TYPE
|
76
|
-
(!!value).inspect
|
77
|
-
when ScalarType, ID_TYPE, STRING_TYPE
|
78
|
-
value.to_s.inspect
|
79
|
-
when EnumType
|
80
|
-
value.to_s
|
81
|
-
when InputObjectType
|
82
|
-
fields = value.to_h.map{ |field_name, field_value|
|
83
|
-
field_type = type.input_fields.fetch(field_name.to_s).type
|
84
|
-
"#{field_name}: #{print_value(field_value, field_type)}"
|
85
|
-
}.join(", ")
|
86
|
-
"{ #{fields} }"
|
87
|
-
when NonNullType
|
88
|
-
print_value(value, type.of_type)
|
89
|
-
when ListType
|
90
|
-
"[#{value.to_a.map{ |v| print_value(v, type.of_type) }.join(", ")}]"
|
91
|
-
else
|
92
|
-
raise NotImplementedError, "Unexpected value type #{type.inspect}"
|
98
|
+
class ScalarPrinter
|
99
|
+
def self.print(type)
|
100
|
+
"scalar #{type.name}"
|
93
101
|
end
|
94
102
|
end
|
95
|
-
end
|
96
103
|
|
97
|
-
|
98
|
-
|
99
|
-
|
104
|
+
class ObjectPrinter
|
105
|
+
extend FieldPrinter
|
106
|
+
def self.print(type)
|
107
|
+
if type.interfaces.any?
|
108
|
+
implementations = " implements #{type.interfaces.map(&:to_s).join(", ")}"
|
109
|
+
else
|
110
|
+
implementations = nil
|
111
|
+
end
|
112
|
+
"type #{type.name}#{implementations} {\n#{print_fields(type)}\n}"
|
113
|
+
end
|
100
114
|
end
|
101
|
-
end
|
102
115
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
implementations = " implements #{type.interfaces.map(&:to_s).join(", ")}"
|
108
|
-
else
|
109
|
-
implementations = nil
|
116
|
+
class InterfacePrinter
|
117
|
+
extend FieldPrinter
|
118
|
+
def self.print(type)
|
119
|
+
"interface #{type.name} {\n#{print_fields(type)}\n}"
|
110
120
|
end
|
111
|
-
"type #{type.name}#{implementations} {\n#{print_fields(type)}\n}"
|
112
121
|
end
|
113
|
-
end
|
114
122
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
123
|
+
class UnionPrinter
|
124
|
+
def self.print(type)
|
125
|
+
"union #{type.name} = #{type.possible_types.map(&:to_s).join(" | ")}\n}"
|
126
|
+
end
|
119
127
|
end
|
120
|
-
end
|
121
128
|
|
122
|
-
|
123
|
-
|
124
|
-
|
129
|
+
class EnumPrinter
|
130
|
+
def self.print(type)
|
131
|
+
values = type.values.values.map{ |v| " #{v.name}" }.join("\n")
|
132
|
+
"enum #{type.name} {\n#{values}\n}"
|
133
|
+
end
|
125
134
|
end
|
126
|
-
end
|
127
135
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
136
|
+
class InputObjectPrinter
|
137
|
+
extend FieldPrinter
|
138
|
+
def self.print(type)
|
139
|
+
fields = type.input_fields.values.map{ |field| " #{print_input_value(field)}" }.join("\n")
|
140
|
+
"input #{type.name} {\n#{fields}\n}"
|
141
|
+
end
|
132
142
|
end
|
133
|
-
end
|
134
143
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
144
|
+
STRATEGIES = {
|
145
|
+
GraphQL::TypeKinds::SCALAR => ScalarPrinter,
|
146
|
+
GraphQL::TypeKinds::OBJECT => ObjectPrinter,
|
147
|
+
GraphQL::TypeKinds::INTERFACE => InterfacePrinter,
|
148
|
+
GraphQL::TypeKinds::UNION => UnionPrinter,
|
149
|
+
GraphQL::TypeKinds::ENUM => EnumPrinter,
|
150
|
+
GraphQL::TypeKinds::INPUT_OBJECT => InputObjectPrinter,
|
151
|
+
}
|
141
152
|
end
|
142
|
-
|
143
|
-
STRATEGIES = {
|
144
|
-
GraphQL::TypeKinds::SCALAR => ScalarPrinter,
|
145
|
-
GraphQL::TypeKinds::OBJECT => ObjectPrinter,
|
146
|
-
GraphQL::TypeKinds::INTERFACE => InterfacePrinter,
|
147
|
-
GraphQL::TypeKinds::UNION => UnionPrinter,
|
148
|
-
GraphQL::TypeKinds::ENUM => EnumPrinter,
|
149
|
-
GraphQL::TypeKinds::INPUT_OBJECT => InputObjectPrinter,
|
150
|
-
}
|
153
|
+
private_constant :TypeKindPrinters
|
151
154
|
end
|
152
|
-
private_constant :TypeKindPrinters
|
153
155
|
end
|
154
156
|
end
|