graphql 1.6.4 → 1.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +47 -0
  3. data/lib/generators/graphql/install_generator.rb +15 -20
  4. data/lib/generators/graphql/mutation_generator.rb +31 -1
  5. data/lib/generators/graphql/templates/mutation.erb +2 -2
  6. data/lib/generators/graphql/templates/mutation_type.erb +5 -0
  7. data/lib/generators/graphql/templates/schema.erb +0 -1
  8. data/lib/graphql/argument.rb +6 -5
  9. data/lib/graphql/backwards_compatibility.rb +18 -4
  10. data/lib/graphql/base_type.rb +1 -1
  11. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +1 -1
  12. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +1 -1
  13. data/lib/graphql/compatibility/lazy_execution_specification.rb +9 -2
  14. data/lib/graphql/define.rb +1 -0
  15. data/lib/graphql/define/defined_object_proxy.rb +1 -1
  16. data/lib/graphql/define/no_definition_error.rb +7 -0
  17. data/lib/graphql/enum_type.rb +4 -0
  18. data/lib/graphql/execution/execute.rb +3 -3
  19. data/lib/graphql/execution/field_result.rb +1 -1
  20. data/lib/graphql/execution/lazy/resolve.rb +10 -9
  21. data/lib/graphql/execution/multiplex.rb +6 -5
  22. data/lib/graphql/input_object_type.rb +5 -1
  23. data/lib/graphql/interface_type.rb +12 -3
  24. data/lib/graphql/query.rb +21 -5
  25. data/lib/graphql/query/context.rb +11 -0
  26. data/lib/graphql/schema.rb +48 -27
  27. data/lib/graphql/schema/build_from_definition.rb +1 -1
  28. data/lib/graphql/schema/build_from_definition/resolve_map.rb +2 -2
  29. data/lib/graphql/schema/loader.rb +1 -1
  30. data/lib/graphql/schema/traversal.rb +91 -0
  31. data/lib/graphql/schema/validation.rb +1 -1
  32. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +41 -7
  33. data/lib/graphql/union_type.rb +13 -2
  34. data/lib/graphql/version.rb +1 -1
  35. data/readme.md +1 -3
  36. data/spec/generators/graphql/install_generator_spec.rb +3 -1
  37. data/spec/generators/graphql/mutation_generator_spec.rb +14 -0
  38. data/spec/graphql/analysis/max_query_complexity_spec.rb +12 -1
  39. data/spec/graphql/analysis/query_complexity_spec.rb +1 -1
  40. data/spec/graphql/argument_spec.rb +29 -0
  41. data/spec/graphql/define/assign_argument_spec.rb +4 -4
  42. data/spec/graphql/define/instance_definable_spec.rb +1 -1
  43. data/spec/graphql/enum_type_spec.rb +8 -0
  44. data/spec/graphql/execution/lazy_spec.rb +30 -3
  45. data/spec/graphql/interface_type_spec.rb +44 -0
  46. data/spec/graphql/introspection/schema_type_spec.rb +3 -0
  47. data/spec/graphql/introspection/type_type_spec.rb +1 -0
  48. data/spec/graphql/object_type_spec.rb +8 -3
  49. data/spec/graphql/query/context_spec.rb +18 -0
  50. data/spec/graphql/query/executor_spec.rb +1 -1
  51. data/spec/graphql/query/literal_input_spec.rb +31 -15
  52. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +1 -1
  53. data/spec/graphql/query/variables_spec.rb +25 -1
  54. data/spec/graphql/query_spec.rb +24 -9
  55. data/spec/graphql/relay/mutation_spec.rb +1 -1
  56. data/spec/graphql/schema/build_from_definition_spec.rb +1 -1
  57. data/spec/graphql/schema/loader_spec.rb +1 -1
  58. data/spec/graphql/schema/printer_spec.rb +1 -1
  59. data/spec/graphql/schema/{reduce_types_spec.rb → traversal_spec.rb} +21 -4
  60. data/spec/graphql/schema/warden_spec.rb +1 -1
  61. data/spec/graphql/schema_spec.rb +23 -2
  62. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +133 -0
  63. data/spec/graphql/union_type_spec.rb +53 -0
  64. data/spec/spec_helper.rb +9 -0
  65. data/spec/support/dummy/data.rb +14 -5
  66. data/spec/support/dummy/schema.rb +46 -5
  67. data/spec/support/star_wars/data.rb +10 -6
  68. data/spec/support/star_wars/schema.rb +5 -2
  69. metadata +8 -7
  70. data/lib/graphql/schema/instrumented_field_map.rb +0 -40
  71. data/lib/graphql/schema/reduce_types.rb +0 -69
  72. data/lib/graphql/schema/type_map.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 347eef5d121c7ccb7f0c217370e2a02285d36096
4
- data.tar.gz: e2b7083f415d77f05dcb888c121075617c93e677
3
+ metadata.gz: a043673bcabc1f7cbcc834d71919a4a61dd36a6a
4
+ data.tar.gz: efc89423aa48aecce87434a1f0e4567fdf4de290
5
5
  SHA512:
6
- metadata.gz: 07a84a28ba76c6b7f0586b1c4e73745ae4d279482cab00b2a1ae6d087e566156359f993d325cc102c7af3ed23b0dc3f381d29635fef85ebe73d71ed103d7e0f2
7
- data.tar.gz: d5b0dcfeb682086a666c839ebf4d0dcfd695201682016cc55d16d28d6f487012ac8f82e6360ea58780e3a6874c4eee52b49d0e8f44d37817e0672decdb27753f
6
+ metadata.gz: d379a08ef187cef29cec03dfb570d3064bc762ff117a5f4e912ef2b5e4cd6f512f1317b42a1e0ae5ccb0510eb505ab36e0ef8ff61c7331326523b7d49889d7d7
7
+ data.tar.gz: 58ddefe960ea3e4d75d9c55ba06403632974a796f6fa5a3869977b2733d5dad17e0037390848cdd738b605b89e22807e47508d64a03f4c8978f46d714cc393f0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/base'
3
+
4
+ module Graphql
5
+ module Generators
6
+ module Core
7
+ def insert_root_type(type, name)
8
+ log :add_root_type, type
9
+ sentinel = /GraphQL\:\:Schema\.define do\s*\n/m
10
+
11
+ in_root do
12
+ inject_into_file schema_file_path, " #{type}(Types::#{name})\n", after: sentinel, verbose: false, force: false
13
+ end
14
+ end
15
+
16
+ def create_mutation_root_type
17
+ create_dir("app/graphql/mutations")
18
+ template("mutation_type.erb", "app/graphql/types/mutation_type.rb", { skip: true })
19
+ insert_root_type('mutation', 'MutationType')
20
+ end
21
+
22
+ def schema_file_path
23
+ "app/graphql/#{schema_name.underscore}.rb"
24
+ end
25
+
26
+ def create_dir(dir)
27
+ empty_directory(dir)
28
+ if !options[:skip_keeps]
29
+ create_file("#{dir}/.keep")
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def schema_name
36
+ @schema_name ||= begin
37
+ if options[:schema]
38
+ options[:schema]
39
+ else
40
+ require File.expand_path("config/application", destination_root)
41
+ "#{Rails.application.class.parent_name}Schema"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'rails/generators/base'
3
+ require_relative 'core'
3
4
 
4
5
  module Graphql
5
6
  module Generators
@@ -40,6 +41,8 @@ module Graphql
40
41
  #
41
42
  # Use `--no-graphiql` to skip `graphiql-rails` installation.
42
43
  class InstallGenerator < Rails::Generators::Base
44
+ include Core
45
+
43
46
  desc "Install GraphQL folder structure and boilerplate code"
44
47
  source_root File.expand_path('../templates', __FILE__)
45
48
 
@@ -58,6 +61,11 @@ module Graphql
58
61
  default: false,
59
62
  desc: "Skip graphiql-rails installation"
60
63
 
64
+ class_option :skip_mutation_root_type,
65
+ type: :boolean,
66
+ default: false,
67
+ desc: "Skip creation of the mutation root type"
68
+
61
69
  class_option :relay,
62
70
  type: :boolean,
63
71
  default: false,
@@ -81,10 +89,15 @@ if Rails.env.development?
81
89
  RUBY
82
90
 
83
91
  def create_folder_structure
84
- create_dir("app/graphql/mutations")
85
92
  create_dir("app/graphql/types")
93
+ template("schema.erb", schema_file_path)
94
+
95
+ # Note: Yuo can't have a schema without the query type, otherwise introspection breaks
86
96
  template("query_type.erb", "app/graphql/types/query_type.rb")
87
- template("schema.erb", "app/graphql/#{schema_name.underscore}.rb")
97
+ insert_root_type('query', 'QueryType')
98
+
99
+ create_mutation_root_type unless options.skip_mutation_root_type?
100
+
88
101
  template("graphql_controller.erb", "app/controllers/graphql_controller.rb")
89
102
  route('post "/graphql", to: "graphql#execute"')
90
103
 
@@ -121,24 +134,6 @@ RUBY
121
134
  @gemfile_modified = true
122
135
  super(*args)
123
136
  end
124
-
125
- def create_dir(dir)
126
- empty_directory(dir)
127
- if !options[:skip_keeps]
128
- create_file("#{dir}/.keep")
129
- end
130
- end
131
-
132
- def schema_name
133
- @schema_name ||= begin
134
- if options[:schema]
135
- options[:schema]
136
- else
137
- require File.expand_path("config/application", destination_root)
138
- "#{Rails.application.class.parent_name}Schema"
139
- end
140
- end
141
- end
142
137
  end
143
138
  end
144
139
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'rails/generators/named_base'
3
+ require_relative 'core'
3
4
 
4
5
  module Graphql
5
6
  module Generators
@@ -7,12 +8,41 @@ module Graphql
7
8
  #
8
9
  # @example Generate a `Relay::Mutation` by name
9
10
  # rails g graphql:mutation CreatePostMutation
10
- class MutationGenerator < Rails::Generators::NamedBase
11
+ class MutationGenerator < Rails::Generators::Base
12
+ include Core
13
+
11
14
  desc "Create a Relay mutation by name"
12
15
  source_root File.expand_path('../templates', __FILE__)
13
16
 
17
+ argument :name, type: :string
18
+
19
+ def initialize(args, *options) #:nodoc:
20
+ # Unfreeze name in case it's given as a frozen string
21
+ args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen?
22
+ super
23
+
24
+ assign_names!(name)
25
+ end
26
+
27
+ attr_reader :file_name, :mutation_name, :field_name
28
+
14
29
  def create_mutation_file
30
+ create_mutation_root_type
15
31
  template "mutation.erb", "app/graphql/mutations/#{file_name}.rb"
32
+
33
+ sentinel = /name "Mutation"\s*\n/m
34
+ in_root do
35
+ gsub_file "app/graphql/types/mutation_type.rb", / \# TODO\: Add Mutations as fields\s*\n/m, ""
36
+ inject_into_file "app/graphql/types/mutation_type.rb", " field :#{field_name}, Mutations::#{mutation_name}.field\n", after: sentinel, verbose: false, force: false
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def assign_names!(name)
43
+ @field_name = name.camelize(:lower)
44
+ @mutation_name = name.camelize(:upper)
45
+ @file_name = name.camelize.underscore
16
46
  end
17
47
  end
18
48
  end
@@ -1,5 +1,5 @@
1
- Mutations::<%= name %> = GraphQL::Relay::Mutation.define do
2
- name "<%= name %>"
1
+ Mutations::<%= mutation_name %> = GraphQL::Relay::Mutation.define do
2
+ name "<%= mutation_name %>"
3
3
  # TODO: define return fields
4
4
  # return_field :post, Types::PostType
5
5
 
@@ -0,0 +1,5 @@
1
+ Types::MutationType = GraphQL::ObjectType.define do
2
+ name "Mutation"
3
+
4
+ # TODO: Add Mutations as fields
5
+ end
@@ -1,5 +1,4 @@
1
1
  <%= schema_name %> = GraphQL::Schema.define do
2
- query(Types::QueryType)
3
2
  <% if options[:relay] %>
4
3
  # Relay Object Identification:
5
4
 
@@ -96,11 +96,11 @@ module GraphQL
96
96
 
97
97
  NO_DEFAULT_VALUE = Object.new
98
98
  # @api private
99
- def self.from_dsl(name, type = nil, description = nil, default_value: NO_DEFAULT_VALUE, as: nil, prepare: DefaultPrepare, &block)
99
+ def self.from_dsl(name, type = nil, description = nil, default_value: NO_DEFAULT_VALUE, as: nil, prepare: DefaultPrepare, **kwargs, &block)
100
100
  argument = if block_given?
101
101
  GraphQL::Argument.define(&block)
102
102
  else
103
- GraphQL::Argument.new
103
+ GraphQL::Argument.define(**kwargs)
104
104
  end
105
105
 
106
106
  argument.name = name.to_s
@@ -109,9 +109,10 @@ module GraphQL
109
109
  if default_value != NO_DEFAULT_VALUE
110
110
  argument.default_value = default_value
111
111
  end
112
- argument.as = as
113
- argument.prepare = prepare
114
-
112
+ as && argument.as = as
113
+ if prepare != DefaultPrepare
114
+ argument.prepare = prepare
115
+ end
115
116
 
116
117
  argument
117
118
  end
@@ -8,7 +8,9 @@ module GraphQL
8
8
  # check its arity, and if needed, apply a wrapper so that
9
9
  # it can be called with `to` arguments.
10
10
  # If a wrapper is applied, warn the application with `name`.
11
- def wrap_arity(callable, from:, to:, name:)
11
+ #
12
+ # If `last`, then use the last arguments to call the function.
13
+ def wrap_arity(callable, from:, to:, name:, last: false)
12
14
  arity = get_arity(callable)
13
15
  case arity
14
16
  when to
@@ -16,8 +18,13 @@ module GraphQL
16
18
  callable
17
19
  when from
18
20
  # It has the old arity, so wrap it with an arity converter
19
- warn("#{name} with #{from} arguments is deprecated, it now accepts #{to} arguments")
20
- ArityWrapper.new(callable, from)
21
+ message ="#{name} with #{from} arguments is deprecated, it now accepts #{to} arguments, see:"
22
+ backtrace = caller(0, 20)
23
+ # Find the first line in the trace that isn't library internals:
24
+ user_line = backtrace.find {|l| l !~ /lib\/graphql/ }
25
+ warn(message + "\n" + user_line + "\n")
26
+ wrapper = last ? LastArgumentsWrapper : FirstArgumentsWrapper
27
+ wrapper.new(callable, from)
21
28
  else
22
29
  raise "Can't wrap #{callable} (arity: #{arity}) to have arity #{to}"
23
30
  end
@@ -32,7 +39,7 @@ module GraphQL
32
39
  end
33
40
  end
34
41
 
35
- class ArityWrapper
42
+ class FirstArgumentsWrapper
36
43
  def initialize(callable, old_arity)
37
44
  @callable = callable
38
45
  @old_arity = old_arity
@@ -43,5 +50,12 @@ module GraphQL
43
50
  @callable.call(*backwards_compat_args)
44
51
  end
45
52
  end
53
+
54
+ class LastArgumentsWrapper < FirstArgumentsWrapper
55
+ def call(*args)
56
+ backwards_compat_args = args.last(@old_arity)
57
+ @callable.call(*backwards_compat_args)
58
+ end
59
+ end
46
60
  end
47
61
  end
@@ -86,7 +86,7 @@ module GraphQL
86
86
 
87
87
  # Find out which possible type to use for `value`.
88
88
  # Returns self if there are no possible types (ie, not Union or Interface)
89
- def resolve_type(value)
89
+ def resolve_type(value, ctx)
90
90
  self
91
91
  end
92
92
 
@@ -40,7 +40,7 @@ module GraphQL
40
40
 
41
41
  schema = GraphQL::Schema.define(
42
42
  query: query_type,
43
- resolve_type: ->(o, c) { o == :counter ? counter_type : nil },
43
+ resolve_type: ->(t, o, c) { o == :counter ? counter_type : nil },
44
44
  orphan_types: [alt_counter_type, counter_type],
45
45
  query_execution_strategy: execution_strategy,
46
46
  )
@@ -178,7 +178,7 @@ module GraphQL
178
178
  query_execution_strategy execution_strategy
179
179
  query query_type
180
180
 
181
- resolve_type ->(obj, ctx) {
181
+ resolve_type ->(type, obj, ctx) {
182
182
  if obj.respond_to?(:birthdate)
183
183
  person_type
184
184
  elsif obj.respond_to?(:leader_id)
@@ -24,11 +24,17 @@ module GraphQL
24
24
  p2: push(value: 2) {
25
25
  push(value: 3) {
26
26
  value
27
+ push(value: 21) {
28
+ value
29
+ }
27
30
  }
28
31
  }
29
32
  p3: push(value: 4) {
30
33
  push(value: 5) {
31
34
  value
35
+ push(value: 22) {
36
+ value
37
+ }
32
38
  }
33
39
  }
34
40
  }
@@ -37,14 +43,15 @@ module GraphQL
37
43
 
38
44
  expected_data = {
39
45
  "p1"=>{"value"=>1},
40
- "p2"=>{"push"=>{"value"=>3}},
41
- "p3"=>{"push"=>{"value"=>5}},
46
+ "p2"=>{"push"=>{"value"=>3, "push"=>{"value"=>21}}},
47
+ "p3"=>{"push"=>{"value"=>5, "push"=>{"value"=>22}}},
42
48
  }
43
49
  assert_equal expected_data, res["data"]
44
50
 
45
51
  expected_pushes = [
46
52
  [1,2,4], # first level
47
53
  [3,5], # second level
54
+ [21, 22],
48
55
  ]
49
56
  assert_equal expected_pushes, pushes
50
57
  end
@@ -7,6 +7,7 @@ require "graphql/define/assign_mutation_function"
7
7
  require "graphql/define/assign_object_field"
8
8
  require "graphql/define/defined_object_proxy"
9
9
  require "graphql/define/instance_definable"
10
+ require "graphql/define/no_definition_error"
10
11
  require "graphql/define/non_null_with_bang"
11
12
  require "graphql/define/type_definer"
12
13
 
@@ -38,7 +38,7 @@ module GraphQL
38
38
  definition.call(@target, *args, &block)
39
39
  else
40
40
  msg = "#{@target.class.name} can't define '#{name}'"
41
- raise NoMethodError, msg, caller
41
+ raise NoDefinitionError, msg, caller
42
42
  end
43
43
  end
44
44
 
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Define
4
+ class NoDefinitionError < GraphQL::Error
5
+ end
6
+ end
7
+ end
@@ -93,6 +93,10 @@ module GraphQL
93
93
 
94
94
  # @param enum_value [EnumValue] A value to add to this type's set of values
95
95
  def add_value(enum_value)
96
+ if @values_by_name.key?(enum_value.name)
97
+ raise "Enum value names must be unique. `#{enum_value.name}` already exists."
98
+ end
99
+
96
100
  @values_by_name[enum_value.name] = enum_value
97
101
  end
98
102
 
@@ -76,8 +76,8 @@ module GraphQL
76
76
  selection: selection,
77
77
  )
78
78
 
79
- arguments = query.arguments_for(selection, field)
80
79
  raw_value = begin
80
+ arguments = query.arguments_for(selection, field)
81
81
  query_ctx.schema.middleware.invoke([parent_type, object, field, arguments, field_ctx])
82
82
  rescue GraphQL::ExecutionError => err
83
83
  err
@@ -116,7 +116,7 @@ module GraphQL
116
116
 
117
117
  case raw_value
118
118
  when GraphQL::ExecutionError
119
- raw_value.ast_node = field_ctx.ast_node
119
+ raw_value.ast_node ||= field_ctx.ast_node
120
120
  raw_value.path = field_ctx.path
121
121
  query.context.errors.push(raw_value)
122
122
  when Array
@@ -210,7 +210,7 @@ module GraphQL
210
210
  )
211
211
  when GraphQL::TypeKinds::UNION, GraphQL::TypeKinds::INTERFACE
212
212
  query = field_ctx.query
213
- resolved_type = query.resolve_type(value)
213
+ resolved_type = field_type.resolve_type(value, field_ctx)
214
214
  possible_types = query.possible_types(field_type)
215
215
 
216
216
  if !possible_types.include?(resolved_type)
@@ -47,7 +47,7 @@ module GraphQL
47
47
  end
48
48
 
49
49
  def inspect
50
- "#<FieldResult #{value.inspect} (#{field.type})>"
50
+ "#<FieldResult #{value.inspect} (#{@type})>"
51
51
  end
52
52
  end
53
53
  end
@@ -35,14 +35,14 @@ module GraphQL
35
35
  if acc.empty?
36
36
  Lazy::NullResult
37
37
  else
38
- acc.each_with_index do |field_result, idx|
39
- acc[idx] = field_result.value.then do |inner_v|
38
+ Lazy.new {
39
+ acc.each_with_index { |field_result, idx|
40
+ inner_v = field_result.value.value
40
41
  field_result.value = inner_v
41
- resolve_in_place(inner_v)
42
- end
43
- end
44
-
45
- Lazy.new { acc.each_with_index { |l, idx| acc[idx] = l.value }; acc }
42
+ acc[idx] = inner_v
43
+ }
44
+ resolve_in_place(acc)
45
+ }
46
46
  end
47
47
  end
48
48
 
@@ -62,9 +62,10 @@ module GraphQL
62
62
  end
63
63
  when FieldResult
64
64
  field_value = value.value
65
- if field_value.is_a?(Lazy)
65
+ case field_value
66
+ when Lazy
66
67
  acc = acc << value
67
- else
68
+ when SelectionResult
68
69
  acc = each_lazy(acc, field_value)
69
70
  end
70
71
  end