graphql 0.9.4 → 0.9.5

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