graphql 1.6.4 → 1.6.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.
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