graphql 0.12.1 → 0.13.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 +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
|