graphql 0.9.4 → 0.9.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c7a6017d64f6fd507c50ac439cd6509e2fb163cd
4
- data.tar.gz: 43f4acf1bd7a7b7bf43b9b0df7e87d34cea37339
3
+ metadata.gz: e860d8ee6cbcf24b38c43ea7182153e9e1ea6c17
4
+ data.tar.gz: 6ffcb982ea45a2e3c16fb68726a1c583849f5184
5
5
  SHA512:
6
- metadata.gz: 313db517b3541bbf8f21a7d1fba1eb257d4be306e44e33f9f6f511d0d3fbfb034cfeca667a2025038bc85b88513fa329db7003922948dd64a78633c44231eb2c
7
- data.tar.gz: 2a86be1427279e98dfb940ac59f2b8669c6d9638ad8a414a34ba80ee87a9d32bcde0a5ccef2d9c660d033b8d6a7be7ad0c1902869a33c5fe711b24d4cf9512b8
6
+ metadata.gz: 6990e5ed872ebf7efed61846915a57ed7e21b76ab82ea9c33233bb4747cdb49c435c2e2d2f0a7b8942dbded68800ca54fe517643a9b1bb4a1fb3cfc0f1cd98c4
7
+ data.tar.gz: a4740639e811cd70e43b01fa22e8a9d35005b5825778bfa1b5ce31940cf1fc9dc8bb2fc2c262579cd2548645bfc982054fa055eae4ea3d9e4c941015f789def5
@@ -1,6 +1,7 @@
1
1
  require "json"
2
2
  require "parslet"
3
3
  require "singleton"
4
+ require "forwardable"
4
5
 
5
6
  module GraphQL
6
7
  class ParseError < StandardError
@@ -55,6 +56,7 @@ require 'graphql/introspection'
55
56
  require 'graphql/language'
56
57
  require 'graphql/directive'
57
58
  require 'graphql/schema'
59
+ require 'graphql/schema/printer'
58
60
 
59
61
  # Order does not matter for these:
60
62
 
@@ -59,25 +59,11 @@ module GraphQL
59
59
  end
60
60
  end
61
61
 
62
- # Print the human-readable name of this type
62
+ # Print the human-readable name of this type using the query-string naming pattern
63
63
  def to_s
64
- Printer.instance.print(self)
64
+ name
65
65
  end
66
66
 
67
67
  alias :inspect :to_s
68
-
69
- # Print a type, using the query-style naming pattern
70
- class Printer
71
- include Singleton
72
- def print(type)
73
- if type.kind.non_null?
74
- "#{print(type.of_type)}!"
75
- elsif type.kind.list?
76
- "[#{print(type.of_type)}]"
77
- else
78
- type.name
79
- end
80
- end
81
- end
82
68
  end
83
69
  end
@@ -1,5 +1,5 @@
1
1
  GraphQL::Introspection::ArgumentsField = GraphQL::Field.define do
2
2
  description "Arguments allowed to this object"
3
- type GraphQL::ListType.new(of_type: GraphQL::Introspection::InputValueType)
3
+ type !GraphQL::ListType.new(of_type: !GraphQL::Introspection::InputValueType)
4
4
  resolve -> (target, a, c) { target.arguments.values }
5
5
  end
@@ -2,7 +2,7 @@ GraphQL::Introspection::FieldType = GraphQL::ObjectType.define do
2
2
  name "__Field"
3
3
  description "Field on a GraphQL type"
4
4
  field :name, !types.String, "The name for accessing this field"
5
- field :description, !types.String, "The description of this field"
5
+ field :description, types.String, "The description of this field"
6
6
  field :type, !GraphQL::Introspection::TypeType, "The return type of this field"
7
7
  field :isDeprecated, !types.Boolean, "Is this field deprecated?" do
8
8
  resolve -> (obj, a, c) { !!obj.deprecation_reason }
@@ -1,7 +1,7 @@
1
1
  GraphQL::Introspection::InputFieldsField = GraphQL::Field.define do
2
2
  name "inputFields"
3
3
  description "fields on this input object"
4
- type types[GraphQL::Introspection::InputValueType]
4
+ type types[!GraphQL::Introspection::InputValueType]
5
5
  resolve -> (target, a, c) {
6
6
  if target.kind.input_object?
7
7
  target.input_fields.values
@@ -3,6 +3,6 @@ GraphQL::Introspection::InputValueType = GraphQL::ObjectType.define do
3
3
  description "An input for a field or InputObject"
4
4
  field :name, !types.String, "The key for this value"
5
5
  field :description, types.String, "What this value is used for"
6
- field :type, -> { GraphQL::Introspection::TypeType }, "The expected type for this value"
6
+ field :type, -> { !GraphQL::Introspection::TypeType }, "The expected type for this value"
7
7
  field :defaultValue, types.String, "The value applied if no other value is provided", property: :default_value
8
8
  end
@@ -1,5 +1,5 @@
1
1
  GraphQL::Introspection::InterfacesField = GraphQL::Field.define do
2
2
  description "Interfaces which this object implements"
3
- type -> { !types[!GraphQL::Introspection::TypeType] }
3
+ type -> { types[!GraphQL::Introspection::TypeType] }
4
4
  resolve -> (target, a, c) { target.kind.object? ? target.interfaces : nil }
5
5
  end
@@ -1,5 +1,5 @@
1
1
  GraphQL::Introspection::PossibleTypesField = GraphQL::Field.define do
2
2
  description "Types which compose this Union or Interface"
3
- type -> { types[GraphQL::Introspection::TypeType] }
3
+ type -> { types[!GraphQL::Introspection::TypeType] }
4
4
  resolve -> (target, a, c) { target.kind.resolves? ? target.possible_types : nil }
5
5
  end
@@ -2,11 +2,11 @@ GraphQL::Introspection::TypeType = GraphQL::ObjectType.define do
2
2
  name "__Type"
3
3
  description "A type in the GraphQL schema"
4
4
 
5
- field :name, !types.String, "The name of this type"
5
+ field :name, types.String, "The name of this type"
6
6
  field :description, types.String, "What this type represents"
7
7
 
8
8
  field :kind do
9
- type GraphQL::Introspection::TypeKindEnum
9
+ type !GraphQL::Introspection::TypeKindEnum
10
10
  description "The kind of this type"
11
11
  resolve -> (target, a, c) { target.kind.name }
12
12
  end
@@ -12,4 +12,8 @@ class GraphQL::ListType < GraphQL::BaseType
12
12
  def kind
13
13
  GraphQL::TypeKinds::LIST
14
14
  end
15
+
16
+ def to_s
17
+ "[#{of_type.to_s}]"
18
+ end
15
19
  end
@@ -20,4 +20,8 @@ class GraphQL::NonNullType < GraphQL::BaseType
20
20
  def kind
21
21
  GraphQL::TypeKinds::NON_NULL
22
22
  end
23
+
24
+ def to_s
25
+ "#{of_type.to_s}!"
26
+ end
23
27
  end
@@ -1,5 +1,3 @@
1
- require 'forwardable'
2
-
3
1
  # Provide read-only access to arguments by string or symbol names.
4
2
  class GraphQL::Query::Arguments
5
3
  extend Forwardable
@@ -22,6 +22,8 @@ module GraphQL
22
22
 
23
23
  private
24
24
 
25
+ # After getting the value from the field's resolve method,
26
+ # continue by "finishing" the value, eg. executing sub-fields or coercing values
25
27
  def get_finished_value(raw_value)
26
28
  if raw_value.nil?
27
29
  nil
@@ -38,20 +40,38 @@ module GraphQL
38
40
  end
39
41
 
40
42
 
43
+ # Get the result of:
44
+ # - Any middleware on this schema
45
+ # - The field's resolve method
41
46
  def get_raw_value
42
- query.context.ast_node = ast_node
43
- value = field.resolve(target, arguments, query.context)
44
- query.context.ast_node = nil
45
-
46
- if value == GraphQL::Query::DEFAULT_RESOLVE
47
- begin
48
- value = target.public_send(ast_node.name)
49
- rescue NoMethodError => err
50
- raise("Couldn't resolve field '#{ast_node.name}' to #{target.class} '#{target}' (resulted in #{err})")
47
+ steps = query.schema.middleware + [get_middleware_proc_from_field_resolve]
48
+ chain = GraphQL::Schema::MiddlewareChain.new(
49
+ steps: steps,
50
+ arguments: [parent_type, target, field, arguments, query.context]
51
+ )
52
+ chain.call
53
+ end
54
+
55
+
56
+ # Execute the field's resolve method
57
+ # then handle the DEFAULT_RESOLVE
58
+ # @return [Proc] suitable to be the last step in a middleware chain
59
+ def get_middleware_proc_from_field_resolve
60
+ -> (_parent_type, parent_object, field_definition, field_args, context, _next) {
61
+ context.ast_node = ast_node
62
+ value = field_definition.resolve(parent_object, field_args, context)
63
+ context.ast_node = nil
64
+
65
+ if value == GraphQL::Query::DEFAULT_RESOLVE
66
+ begin
67
+ value = target.public_send(ast_node.name)
68
+ rescue NoMethodError => err
69
+ raise("Couldn't resolve field '#{ast_node.name}' to #{parent_object.class} '#{parent_object}' (resulted in #{err})")
70
+ end
51
71
  end
52
- end
53
72
 
54
- value
73
+ value
74
+ }
55
75
  end
56
76
  end
57
77
  end
@@ -1,5 +1,7 @@
1
1
  # A GraphQL schema which may be queried with {GraphQL::Query}.
2
2
  class GraphQL::Schema
3
+ extend Forwardable
4
+
3
5
  DIRECTIVES = [GraphQL::Directive::SkipDirective, GraphQL::Directive::IncludeDirective]
4
6
  DYNAMIC_FIELDS = ["__type", "__typename", "__schema"]
5
7
 
@@ -7,6 +9,8 @@ class GraphQL::Schema
7
9
  # Override these if you don't want the default executor:
8
10
  attr_accessor :query_execution_strategy, :mutation_execution_strategy
9
11
 
12
+ # @return [Array<#call>] Middlewares suitable for MiddlewareChain, applied to fields during execution
13
+ attr_reader :middleware
10
14
 
11
15
  # @param query [GraphQL::ObjectType] the query root for the schema
12
16
  # @param mutation [GraphQL::ObjectType, nil] the mutation root for the schema
@@ -15,13 +19,16 @@ class GraphQL::Schema
15
19
  @mutation = mutation
16
20
  @directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
17
21
  @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
22
+ @rescue_middleware = GraphQL::Schema::RescueMiddleware.new
23
+ @middleware = [@rescue_middleware]
18
24
  # Default to the built-in execution strategy:
19
25
  self.query_execution_strategy = GraphQL::Query::SerialExecution
20
26
  self.mutation_execution_strategy = GraphQL::Query::SerialExecution
21
27
  end
22
28
 
23
- # A `{ name => type }` hash of types in this schema
24
- # @return [Hash]
29
+ def_delegators :@rescue_middleware, :rescue_from, :remove_handler
30
+
31
+ # @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
25
32
  def types
26
33
  @types ||= TypeReducer.find_all([query, mutation, GraphQL::Introspection::SchemaType].compact)
27
34
  end
@@ -61,6 +68,8 @@ end
61
68
  require 'graphql/schema/each_item_validator'
62
69
  require 'graphql/schema/field_validator'
63
70
  require 'graphql/schema/implementation_validator'
71
+ require 'graphql/schema/middleware_chain'
72
+ require 'graphql/schema/rescue_middleware'
64
73
  require 'graphql/schema/type_reducer'
65
74
  require 'graphql/schema/type_map'
66
75
  require 'graphql/schema/type_validator'
@@ -0,0 +1,27 @@
1
+ module GraphQL
2
+ class Schema
3
+ # Given {steps} and {arguments}, call steps in order, passing `(*arguments, next_step)`.
4
+ #
5
+ # Steps should call `next_step.call` to continue the chain, or _not_ call it to stop the chain.
6
+ class MiddlewareChain
7
+ # @return [Array<#call(*args)>] Steps in this chain, will be called with arguments and `next_middleware`
8
+ attr_reader :steps
9
+
10
+ # @return [Array] Arguments passed to steps (followed by `next_middleware`)
11
+ attr_reader :arguments
12
+
13
+ def initialize(steps:, arguments:)
14
+ # We're gonna destroy this array, so copy it:
15
+ @steps = steps.dup
16
+ @arguments = arguments
17
+ end
18
+
19
+ # Run the next step in the chain, passing in arguments and handle to the next step
20
+ def call
21
+ next_step = steps.shift
22
+ next_middleware = self
23
+ next_step.call(*arguments, next_middleware)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,145 @@
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
16
+
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"
21
+ end
22
+ schema = Schema.new(query: query_root)
23
+ print_filtered_schema(schema, method(:is_introspection_type))
24
+ end
25
+
26
+ private
27
+
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
32
+
33
+ BUILTIN_SCALARS = Set.new(["String", "Boolean", "Int", "Float", "ID"])
34
+ private_constant :BUILTIN_SCALARS
35
+
36
+ def is_introspection_type(type)
37
+ type.name.start_with?("__")
38
+ end
39
+
40
+ def is_defined_type(type)
41
+ !is_introspection_type(type) && !BUILTIN_SCALARS.include?(type.name)
42
+ end
43
+
44
+ def print_type(type)
45
+ TypeKindPrinters::STRATEGIES.fetch(type.kind).print(type)
46
+ end
47
+
48
+ module TypeKindPrinters
49
+ module FieldPrinter
50
+ def print_fields(type)
51
+ type.fields.values.map{ |field| " #{field.name}#{print_args(field)}: #{field.type}" }.join("\n")
52
+ end
53
+
54
+ def print_args(field)
55
+ return if field.arguments.empty?
56
+ "(#{field.arguments.values.map{ |arg| print_input_value(arg) }.join(", ")})"
57
+ end
58
+
59
+ def print_input_value(arg)
60
+ default_string = " = #{print_value(arg.default_value, arg.type)}" unless arg.default_value.nil?
61
+ "#{arg.name}: #{arg.type.to_s}#{default_string}"
62
+ end
63
+
64
+ def print_value(value, type)
65
+ case type
66
+ when FLOAT_TYPE
67
+ value.to_f.inspect
68
+ when INT_TYPE
69
+ value.to_i.inspect
70
+ when BOOLEAN_TYPE
71
+ (!!value).inspect
72
+ when ScalarType, ID_TYPE, STRING_TYPE
73
+ value.to_s.inspect
74
+ when EnumType
75
+ value.to_s
76
+ when InputObjectType
77
+ fields = value.to_h.map{ |field_name, field_value|
78
+ field_type = type.input_fields.fetch(field_name.to_s).type
79
+ "#{field_name}: #{print_value(field_value, field_type)}"
80
+ }.join(", ")
81
+ "{ #{fields} }"
82
+ when NonNullType
83
+ print_value(value, type.of_type)
84
+ when ListType
85
+ "[#{value.to_a.map{ |v| print_value(v, type.of_type) }.join(", ")}]"
86
+ else
87
+ raise NotImplementedError, "Unexpected value type #{type.inspect}"
88
+ end
89
+ end
90
+ end
91
+
92
+ class ScalarPrinter
93
+ def self.print(type)
94
+ "scalar #{type.name}"
95
+ end
96
+ end
97
+
98
+ class ObjectPrinter
99
+ extend FieldPrinter
100
+ def self.print(type)
101
+ implementations = " implements #{type.interfaces.map(&:to_s).join(", ")}" unless type.interfaces.empty?
102
+ "type #{type.name}#{implementations} {\n#{print_fields(type)}\n}"
103
+ end
104
+ end
105
+
106
+ class InterfacePrinter
107
+ extend FieldPrinter
108
+ def self.print(type)
109
+ "interface #{type.name} {\n#{print_fields(type)}\n}"
110
+ end
111
+ end
112
+
113
+ class UnionPrinter
114
+ def self.print(type)
115
+ "union #{type.name} = #{type.possible_types.map(&:to_s).join(" | ")}\n}"
116
+ end
117
+ end
118
+
119
+ class EnumPrinter
120
+ def self.print(type)
121
+ values = type.values.values.map{ |v| " #{v.name}" }.join("\n")
122
+ "enum #{type.name} {\n#{values}\n}"
123
+ end
124
+ end
125
+
126
+ class InputObjectPrinter
127
+ extend FieldPrinter
128
+ def self.print(type)
129
+ fields = type.input_fields.values.map{ |field| " #{print_input_value(field)}" }.join("\n")
130
+ "input #{type.name} {\n#{fields}\n}"
131
+ end
132
+ end
133
+
134
+ STRATEGIES = {
135
+ GraphQL::TypeKinds::SCALAR => ScalarPrinter,
136
+ GraphQL::TypeKinds::OBJECT => ObjectPrinter,
137
+ GraphQL::TypeKinds::INTERFACE => InterfacePrinter,
138
+ GraphQL::TypeKinds::UNION => UnionPrinter,
139
+ GraphQL::TypeKinds::ENUM => EnumPrinter,
140
+ GraphQL::TypeKinds::INPUT_OBJECT => InputObjectPrinter,
141
+ }
142
+ end
143
+ private_constant :TypeKindPrinters
144
+ end
145
+ end
@@ -0,0 +1,53 @@
1
+ module GraphQL
2
+ class Schema
3
+ # - Store a table of errors & handlers
4
+ # - Rescue errors in a middleware chain, then check for a handler
5
+ # - If a handler is found, use it & return a {GraphQL::ExecutionError}
6
+ # - If no handler is found, re-raise the error
7
+ class RescueMiddleware
8
+ # @return [Hash] `{class => proc}` pairs for handling errors
9
+ attr_reader :rescue_table
10
+ def initialize
11
+ @rescue_table = {}
12
+ end
13
+
14
+ # @example Rescue from not-found by telling the user
15
+ # MySchema.rescue_from(ActiveRecord::NotFound) { "An item could not be found" }
16
+ #
17
+ # @param [Class] a class of error to rescue from
18
+ # @yield [err] A handler to return a message for this error instance
19
+ # @yieldparam [Exception] an error that was rescued
20
+ # @yieldreturn [String] message to put in GraphQL response
21
+ def rescue_from(error_class, &block)
22
+ rescue_table[error_class] = block
23
+ end
24
+
25
+ # Remove the handler for `error_class`
26
+ # @param [Class] the error class whose handler should be removed
27
+ def remove_handler(error_class)
28
+ rescue_table.delete(error_class)
29
+ end
30
+
31
+ # Implement the requirement for {GraphQL::Schema::MiddlewareChain}
32
+ def call(*args, next_middleware)
33
+ begin
34
+ next_middleware.call
35
+ rescue StandardError => err
36
+ attempt_rescue(err)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def attempt_rescue(err)
43
+ handler = rescue_table[err.class]
44
+ if handler
45
+ message = handler.call(err)
46
+ GraphQL::ExecutionError.new(message)
47
+ else
48
+ raise(err)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "0.9.4"
2
+ VERSION = "0.9.5"
3
3
  end
data/readme.md CHANGED
@@ -95,11 +95,19 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
95
95
  - Raise if you try to configure an attribute which doesn't suit the type
96
96
  - ie, if you try to define `resolve` on an ObjectType, it should somehow raise
97
97
  - Incoming enums should be exposed as `EnumValue`s, not `Nodes::Enum`s
98
+ - Overriding `!` on types breaks ActiveSupport `.blank?`
99
+
100
+ ```ruby
101
+ my_type = GraphQL::ObjectType.define { name("MyType") }
102
+ # => MyType
103
+ my_type.present?
104
+ # => MyType!!
105
+ my_type.blank?
106
+ # => MyType!
107
+ ```
108
+
98
109
  - Big ideas:
99
110
  - Use [graphql-parser](https://github.com/shopify/graphql-parser) (Ruby bindings for [libgraphqlparser](https://github.com/graphql/libgraphqlparser)) instead of Parslet
100
- - Add instrumentation
101
- - Some way to expose what queries are run, what types & fields are accessed, how long things are taking, etc
102
- - before-hooks for every field?
103
111
 
104
112
  ## Goals
105
113
 
@@ -131,5 +131,30 @@ describe GraphQL::Query::Executor do
131
131
  assert_raises(RuntimeError) { result }
132
132
  end
133
133
  end
134
+
135
+ describe "if the schema has a rescue handler" do
136
+ before do
137
+ schema.rescue_from(RuntimeError) { "Error was handled!" }
138
+ end
139
+
140
+ after do
141
+ # remove the handler from the middleware:
142
+ schema.remove_handler(RuntimeError)
143
+ end
144
+
145
+ it "adds to the errors key" do
146
+ expected = {
147
+ "data" => {"error" => nil},
148
+ "errors"=>[
149
+ {
150
+ "message"=>"Error was handled!",
151
+ "locations" => [{"line"=>1, "column"=>17}]
152
+ }
153
+ ]
154
+ }
155
+ assert_equal(expected, result)
156
+ end
157
+
158
+ end
134
159
  end
135
160
  end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Schema::MiddlewareChain do
4
+ let(:step_1) { -> (step_values, next_step) { step_values << 1; next_step.call } }
5
+ let(:step_2) { -> (step_values, next_step) { step_values << 2; next_step.call } }
6
+ let(:step_3) { -> (step_values, next_step) { step_values << 3; :return_value } }
7
+ let(:steps) { [step_1, step_2, step_3] }
8
+ let(:step_values) { [] }
9
+ let(:arguments) { [step_values] }
10
+ let(:middleware_chain) { GraphQL::Schema::MiddlewareChain.new(steps: steps, arguments: arguments)}
11
+
12
+ describe "#call" do
13
+ it "runs steps in order" do
14
+ middleware_chain.call
15
+ assert_equal([1,2,3], step_values)
16
+ end
17
+
18
+ it "returns the value of the last middleware" do
19
+ assert_equal(:return_value, middleware_chain.call)
20
+ end
21
+
22
+ describe "when a step returns early" do
23
+ let(:early_return_step) { -> (step_values, next_step) { :early_return } }
24
+ it "doesn't continue the chain" do
25
+ steps.insert(2, early_return_step)
26
+ assert_equal(:early_return, middleware_chain.call)
27
+ assert_equal([1,2], step_values)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Schema::Printer do
4
+ let(:schema) {
5
+ node_type = GraphQL::InterfaceType.define do
6
+ name "Node"
7
+
8
+ field :id, !types.ID
9
+ end
10
+
11
+ choice_type = GraphQL::EnumType.define do
12
+ name "Choice"
13
+
14
+ value "FOO"
15
+ value "BAR"
16
+ end
17
+
18
+ sub_input_type = GraphQL::InputObjectType.define do
19
+ name "Sub"
20
+ input_field :string, types.String
21
+ end
22
+
23
+ variant_input_type = GraphQL::InputObjectType.define do
24
+ name "Varied"
25
+ input_field :id, types.ID
26
+ input_field :int, types.Int
27
+ input_field :float, types.Float
28
+ input_field :bool, types.Boolean
29
+ input_field :enum, choice_type
30
+ input_field :sub, types[sub_input_type]
31
+ end
32
+
33
+ comment_type = GraphQL::ObjectType.define do
34
+ name "Comment"
35
+ description "A blog comment"
36
+ interfaces [node_type]
37
+
38
+ field :id, !types.ID
39
+ end
40
+
41
+ post_type = GraphQL::ObjectType.define do
42
+ name "Post"
43
+ description "A blog post"
44
+
45
+ field :id, !types.ID
46
+ field :title, !types.String
47
+ field :body, !types.String
48
+ field :comments, types[!comment_type]
49
+ end
50
+
51
+ query_root = GraphQL::ObjectType.define do
52
+ name "Query"
53
+ description "The query root of this schema"
54
+
55
+ field :post do
56
+ type post_type
57
+ argument :id, !types.ID
58
+ argument :varied, variant_input_type, default_value: { id: "123", int: 234, float: 2.3, enum: "FOO", sub: [{ string: "str" }] }
59
+ resolve -> (obj, args, ctx) { Post.find(args["id"]) }
60
+ end
61
+ end
62
+
63
+ GraphQL::Schema.new(query: query_root)
64
+ }
65
+
66
+ describe ".print_introspection_schema" do
67
+ it "returns the schema as a string for the introspection types" do
68
+ expected = <<SCHEMA
69
+ type __Directive {
70
+ name: String!
71
+ description: String
72
+ args: [__InputValue!]!
73
+ onOperation: Boolean!
74
+ onFragment: Boolean!
75
+ onField: Boolean!
76
+ }
77
+
78
+ type __EnumValue {
79
+ name: String!
80
+ description: String
81
+ deprecationReason: String
82
+ isDeprecated: Boolean!
83
+ }
84
+
85
+ type __Field {
86
+ name: String!
87
+ description: String
88
+ type: __Type!
89
+ isDeprecated: Boolean!
90
+ args: [__InputValue!]!
91
+ deprecationReason: String
92
+ }
93
+
94
+ type __InputValue {
95
+ name: String!
96
+ description: String
97
+ type: __Type!
98
+ defaultValue: String
99
+ }
100
+
101
+ type __Schema {
102
+ types: [__Type!]!
103
+ directives: [__Directive!]!
104
+ queryType: __Type!
105
+ mutationType: __Type
106
+ }
107
+
108
+ type __Type {
109
+ name: String
110
+ description: String
111
+ kind: __TypeKind!
112
+ fields(includeDeprecated: Boolean = false): [__Field!]
113
+ ofType: __Type
114
+ inputFields: [__InputValue!]
115
+ possibleTypes: [__Type!]
116
+ enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
117
+ interfaces: [__Type!]
118
+ }
119
+
120
+ enum __TypeKind {
121
+ SCALAR
122
+ OBJECT
123
+ INTERFACE
124
+ UNION
125
+ ENUM
126
+ INPUT_OBJECT
127
+ LIST
128
+ NON_NULL
129
+ }
130
+ SCHEMA
131
+ assert_equal expected.chomp, GraphQL::Schema::Printer.print_introspection_schema
132
+ end
133
+ end
134
+
135
+ describe ".print_schema" do
136
+ it "returns the schema as a string for the defined types" do
137
+ expected = <<SCHEMA
138
+ enum Choice {
139
+ FOO
140
+ BAR
141
+ }
142
+
143
+ type Comment implements Node {
144
+ id: ID!
145
+ }
146
+
147
+ interface Node {
148
+ id: ID!
149
+ }
150
+
151
+ type Post {
152
+ id: ID!
153
+ title: String!
154
+ body: String!
155
+ comments: [Comment!]
156
+ }
157
+
158
+ type Query {
159
+ post(id: ID!, varied: Varied = { id: \"123\", int: 234, float: 2.3, enum: FOO, sub: [{ string: \"str\" }] }): Post
160
+ }
161
+
162
+ input Sub {
163
+ string: String
164
+ }
165
+
166
+ input Varied {
167
+ id: ID
168
+ int: Int
169
+ float: Float
170
+ bool: Boolean
171
+ enum: Choice
172
+ sub: [Sub]
173
+ }
174
+ SCHEMA
175
+ assert_equal expected.chomp, GraphQL::Schema::Printer.print_schema(schema)
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ class SpecExampleError < StandardError; end
4
+
5
+ describe GraphQL::Schema::RescueMiddleware do
6
+ let(:error_middleware) { -> (next_middleware) { raise(error_class) } }
7
+
8
+ let(:rescue_middleware) do
9
+ middleware = GraphQL::Schema::RescueMiddleware.new
10
+ middleware.rescue_from(SpecExampleError) { |err| "there was an example error: #{err.class.name}" }
11
+ middleware
12
+ end
13
+
14
+ let(:steps) { [rescue_middleware, error_middleware] }
15
+
16
+ let(:middleware_chain) { GraphQL::Schema::MiddlewareChain.new(steps: steps, arguments: [])}
17
+
18
+ describe "known errors" do
19
+ let(:error_class) { SpecExampleError }
20
+ it "handles them as execution errors" do
21
+ result = middleware_chain.call
22
+ assert_equal("there was an example error: SpecExampleError", result.message)
23
+ assert_equal(GraphQL::ExecutionError, result.class)
24
+ end
25
+ end
26
+
27
+ describe "unknown errors" do
28
+ let(:error_class) { RuntimeError }
29
+ it "re-raises them" do
30
+ assert_raises(RuntimeError) { middleware_chain.call }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Schema do
4
+ let(:schema) { DummySchema }
5
+
6
+ describe "#rescue_from" do
7
+ let(:rescue_middleware) { schema.middleware.first }
8
+
9
+ it "adds handlers to the rescue middleware" do
10
+ assert_equal(1, rescue_middleware.rescue_table.length)
11
+ # normally, you'd use a real class, not a symbol:
12
+ schema.rescue_from(:error_class) { "my custom message" }
13
+ assert_equal(2, rescue_middleware.rescue_table.length)
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,7 @@
1
1
  require_relative './dairy_data'
2
2
 
3
+ class NoSuchDairyError < StandardError; end
4
+
3
5
  EdibleInterface = GraphQL::InterfaceType.define do
4
6
  name "Edible"
5
7
  description "Something you can eat, yum"
@@ -42,7 +44,7 @@ CheeseType = GraphQL::ObjectType.define do
42
44
  # get the strings out:
43
45
  sources = a["source"].map(&:name)
44
46
  if sources.include?("YAK")
45
- GraphQL::ExecutionError.new("No cheeses are made from Yak milk!")
47
+ raise NoSuchDairyError.new("No cheeses are made from Yak milk!")
46
48
  else
47
49
  CHEESES.values.find { |c| sources.include?(c.source) }
48
50
  end
@@ -218,3 +220,4 @@ MutationType = GraphQL::ObjectType.define do
218
220
  end
219
221
 
220
222
  DummySchema = GraphQL::Schema.new(query: QueryType, mutation: MutationType)
223
+ DummySchema.rescue_from(NoSuchDairyError) { |err| err.message }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.4
4
+ version: 0.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-22 00:00:00.000000000 Z
11
+ date: 2015-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -224,6 +224,9 @@ files:
224
224
  - lib/graphql/schema/each_item_validator.rb
225
225
  - lib/graphql/schema/field_validator.rb
226
226
  - lib/graphql/schema/implementation_validator.rb
227
+ - lib/graphql/schema/middleware_chain.rb
228
+ - lib/graphql/schema/printer.rb
229
+ - lib/graphql/schema/rescue_middleware.rb
227
230
  - lib/graphql/schema/type_map.rb
228
231
  - lib/graphql/schema/type_reducer.rb
229
232
  - lib/graphql/schema/type_validator.rb
@@ -275,8 +278,12 @@ files:
275
278
  - spec/graphql/query/type_resolver_spec.rb
276
279
  - spec/graphql/query_spec.rb
277
280
  - spec/graphql/schema/field_validator_spec.rb
281
+ - spec/graphql/schema/middleware_chain_spec.rb
282
+ - spec/graphql/schema/printer_spec.rb
283
+ - spec/graphql/schema/rescue_middleware_spec.rb
278
284
  - spec/graphql/schema/type_reducer_spec.rb
279
285
  - spec/graphql/schema/type_validator_spec.rb
286
+ - spec/graphql/schema_spec.rb
280
287
  - spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
281
288
  - spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
282
289
  - spec/graphql/static_validation/rules/directives_are_defined_spec.rb
@@ -321,7 +328,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
321
328
  version: '0'
322
329
  requirements: []
323
330
  rubyforge_project:
324
- rubygems_version: 2.2.0
331
+ rubygems_version: 2.4.5
325
332
  signing_key:
326
333
  specification_version: 4
327
334
  summary: A GraphQL implementation for Ruby
@@ -346,8 +353,12 @@ test_files:
346
353
  - spec/graphql/query/type_resolver_spec.rb
347
354
  - spec/graphql/query_spec.rb
348
355
  - spec/graphql/schema/field_validator_spec.rb
356
+ - spec/graphql/schema/middleware_chain_spec.rb
357
+ - spec/graphql/schema/printer_spec.rb
358
+ - spec/graphql/schema/rescue_middleware_spec.rb
349
359
  - spec/graphql/schema/type_reducer_spec.rb
350
360
  - spec/graphql/schema/type_validator_spec.rb
361
+ - spec/graphql/schema_spec.rb
351
362
  - spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
352
363
  - spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
353
364
  - spec/graphql/static_validation/rules/directives_are_defined_spec.rb