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