graphql 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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