search_object_graphql 0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +15 -3
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +3 -2
  5. data/CHANGELOG.md +25 -0
  6. data/Gemfile +2 -0
  7. data/README.md +34 -33
  8. data/Rakefile +3 -1
  9. data/example/.ruby-version +1 -1
  10. data/example/Gemfile +3 -1
  11. data/example/README.md +3 -3
  12. data/example/Rakefile +2 -0
  13. data/example/app/controllers/application_controller.rb +2 -0
  14. data/example/app/controllers/graphql_controller.rb +22 -18
  15. data/example/app/graphql/mutations/.keep +0 -0
  16. data/example/app/graphql/resolvers/base_resolver.rb +6 -0
  17. data/example/app/graphql/resolvers/base_search_resolver.rb +3 -1
  18. data/example/app/graphql/resolvers/category_search.rb +5 -3
  19. data/example/app/graphql/resolvers/post_search.rb +6 -4
  20. data/example/app/graphql/schema.rb +3 -1
  21. data/example/app/graphql/types/base_enum.rb +6 -0
  22. data/example/app/graphql/types/base_input_object.rb +6 -0
  23. data/example/app/graphql/types/base_interface.rb +7 -0
  24. data/example/app/graphql/types/base_object.rb +6 -0
  25. data/example/app/graphql/types/base_scalar.rb +6 -0
  26. data/example/app/graphql/types/base_union.rb +6 -0
  27. data/example/app/graphql/types/category_type.rb +7 -6
  28. data/example/app/graphql/types/date_time_type.rb +3 -4
  29. data/example/app/graphql/types/mutation_type.rb +6 -0
  30. data/example/app/graphql/types/post_type.rb +13 -11
  31. data/example/app/graphql/types/query_type.rb +6 -5
  32. data/example/app/models/application_record.rb +2 -0
  33. data/example/app/models/category.rb +2 -0
  34. data/example/app/models/post.rb +2 -0
  35. data/example/bin/bundle +3 -1
  36. data/example/bin/rails +2 -0
  37. data/example/bin/rake +2 -0
  38. data/example/bin/setup +4 -2
  39. data/example/bin/update +4 -2
  40. data/example/config.ru +2 -0
  41. data/example/config/application.rb +1 -1
  42. data/example/db/migrate/20170507175133_create_demo_tables.rb +1 -1
  43. data/example/db/schema.rb +1 -1
  44. data/example/public/favicon.ico +0 -0
  45. data/lib/search_object/plugin/graphql.rb +26 -47
  46. data/lib/search_object/plugin/graphql/version.rb +3 -1
  47. data/search_object_graphql.gemspec +9 -9
  48. data/spec/search_object/plugin/graphql_spec.rb +114 -96
  49. data/spec/spec_helper.rb +2 -0
  50. data/spec/spec_helper_active_record.rb +2 -0
  51. metadata +31 -35
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ module BaseInterface
5
+ include GraphQL::Schema::Interface
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseObject < GraphQL::Schema::Object
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseScalar < GraphQL::Schema::Scalar
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class BaseUnion < GraphQL::Schema::Union
5
+ end
6
+ end
@@ -1,8 +1,9 @@
1
- Types::CategoryType = GraphQL::ObjectType.define do
2
- name 'Category'
1
+ # frozen_string_literal: true
3
2
 
4
- field :id, !types.ID
5
- field :name, !types.String
6
-
7
- connection :posts, Types::PostType.connection_type, function: Resolvers::PostSearch
3
+ module Types
4
+ class CategoryType < BaseObject
5
+ field :id, ID, null: false
6
+ field :name, String, null: false
7
+ field :posts, resolver: Resolvers::PostSearch
8
+ end
8
9
  end
@@ -1,6 +1,5 @@
1
- Types::DateTimeType = GraphQL::ScalarType.define do
2
- name 'DateTime'
1
+ # frozen_string_literal: true
3
2
 
4
- coerce_input ->(value, _ctx) { Time.zone.parse(value) }
5
- coerce_result ->(value, _ctx) { value.utc.iso8601 }
3
+ module Types
4
+ DateTimeType = GraphQL::Types::ISO8601DateTime
6
5
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class MutationType < Types::BaseObject
5
+ end
6
+ end
@@ -1,13 +1,15 @@
1
- Types::PostType = GraphQL::ObjectType.define do
2
- name 'Post'
1
+ # frozen_string_literal: true
3
2
 
4
- field :id, !types.ID
5
- field :title, !types.String
6
- field :body, !types.String
7
- field :category, !Types::CategoryType
8
- field :viewsCount, !types.Int, property: :views_count
9
- field :likesCount, !types.Int, property: :likes_count
10
- field :commentsCount, !types.Int, property: :comments_count
11
- field :isPublished, !types.Boolean, property: :published?
12
- field :publishedAt, !Types::DateTimeType, property: :published_at
3
+ module Types
4
+ class PostType < BaseObject
5
+ field :id, ID, null: false
6
+ field :title, String, null: false
7
+ field :body, String, null: false
8
+ field :category, CategoryType, null: false
9
+ field :views_count, Int, null: false
10
+ field :likes_count, Int, null: false
11
+ field :comments_count, Int, null: false
12
+ field :is_published, Boolean, null: false, method: :published?
13
+ field :published_at, DateTimeType, null: false
14
+ end
13
15
  end
@@ -1,7 +1,8 @@
1
- Types::QueryType = GraphQL::ObjectType.define do
2
- name 'Query'
1
+ # frozen_string_literal: true
3
2
 
4
- connection :categories, Types::CategoryType.connection_type, function: Resolvers::CategorySearch
5
-
6
- connection :posts, Types::PostType.connection_type, function: Resolvers::PostSearch
3
+ module Types
4
+ class QueryType < Types::BaseObject
5
+ field :categories, resolver: Resolvers::CategorySearch
6
+ field :posts, resolver: Resolvers::PostSearch
7
+ end
7
8
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ApplicationRecord < ActiveRecord::Base
2
4
  self.abstract_class = true
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Category < ApplicationRecord
2
4
  validates :name, presence: true, uniqueness: true
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Post < ApplicationRecord
2
4
  validates :title, presence: true, uniqueness: true
3
5
  validates :body, presence: true
data/example/bin/bundle CHANGED
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
2
+ # frozen_string_literal: true
3
+
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3
5
  load Gem.bin_path('bundler', 'bundle')
data/example/bin/rails CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  APP_PATH = File.expand_path('../config/application', __dir__)
3
5
  require_relative '../config/boot'
4
6
  require 'rails/commands'
data/example/bin/rake CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative '../config/boot'
3
5
  require 'rake'
4
6
  Rake.application.run
data/example/bin/setup CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'pathname'
3
5
  require 'fileutils'
4
- include FileUtils
6
+ include FileUtils # rubocop:disable Style/MixinUsage
5
7
 
6
8
  # path to your application root.
7
- APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
9
+ APP_ROOT = Pathname.new File.expand_path('..', __dir__)
8
10
 
9
11
  def system!(*args)
10
12
  system(*args) || abort("\n== Command #{args} failed ==")
data/example/bin/update CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'pathname'
3
5
  require 'fileutils'
4
- include FileUtils
6
+ include FileUtils # rubocop:disable Style/MixinUsage
5
7
 
6
8
  # path to your application root.
7
- APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
9
+ APP_ROOT = Pathname.new File.expand_path('..', __dir__)
8
10
 
9
11
  def system!(*args)
10
12
  system(*args) || abort("\n== Command #{args} failed ==")
data/example/config.ru CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is used by Rack-based servers to start the application.
2
4
 
3
5
  require_relative 'config/environment'
@@ -3,7 +3,7 @@ require_relative 'boot'
3
3
  require "rails"
4
4
  # Pick the frameworks you want:
5
5
  require "active_model/railtie"
6
- # require "active_job/railtie"
6
+ require "active_job/railtie"
7
7
  require "active_record/railtie"
8
8
  require "action_controller/railtie"
9
9
  # require "action_mailer/railtie"
@@ -7,7 +7,7 @@ class CreateDemoTables < ActiveRecord::Migration[5.1]
7
7
  end
8
8
 
9
9
  create_table :posts do |t|
10
- t.references :category, foreign_key: true
10
+ t.references :category
11
11
  t.string :title, null: false
12
12
  t.index :title, unique: true
13
13
  t.string :body, null: false
data/example/db/schema.rb CHANGED
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20170507175133) do
13
+ ActiveRecord::Schema.define(version: 2017_05_07_175133) do
14
14
 
15
15
  create_table "categories", force: :cascade do |t|
16
16
  t.string "name", null: false
File without changes
@@ -1,26 +1,43 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Graphql
4
6
  def self.included(base)
7
+ raise NotIncludedInResolverError, base unless base.ancestors.include? GraphQL::Schema::Resolver
8
+
5
9
  base.include SearchObject::Plugin::Enum
6
10
  base.extend ClassMethods
7
11
  end
8
12
 
9
13
  attr_reader :object, :context
10
14
 
11
- def initialize(filters: {}, object: nil, context: {}, scope: nil)
15
+ def initialize(filters: {}, object: nil, context: {}, scope: nil, field: nil)
12
16
  @object = object
13
17
  @context = context
14
18
 
15
- super filters: filters, scope: scope
19
+ super filters: filters, scope: scope, field: field
20
+ end
21
+
22
+ # NOTE(rstankov): GraphQL::Schema::Resolver interface
23
+ # Documentation - http://graphql-ruby.org/fields/resolvers.html#using-resolver
24
+ def resolve_with_support(args = {})
25
+ self.params = args.to_h
26
+ results
16
27
  end
17
28
 
18
29
  module ClassMethods
19
30
  def option(name, options = {}, &block)
20
- argument = Helper.build_argument(name, options)
21
- arguments[argument.name] = argument
31
+ type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
32
+ options[:enum] = type.values.map { |value, enum_value| enum_value.value || value } if type.respond_to?(:values)
22
33
 
23
- options[:enum] = argument.type.values.keys if argument.type.is_a? GraphQL::EnumType
34
+ argument(
35
+ name.to_s,
36
+ options[:type],
37
+ required: options[:required] || false,
38
+ description: options[:description],
39
+ camelize: options[:camelize]
40
+ )
24
41
 
25
42
  super(name, options, &block)
26
43
  end
@@ -28,35 +45,11 @@ module SearchObject
28
45
  def types
29
46
  GraphQL::Define::TypeDefiner.instance
30
47
  end
48
+ end
31
49
 
32
- # NOTE(rstankov): GraphQL::Function interface
33
- # Documentation - https://rmosolgo.github.io/graphql-ruby/schema/code_reuse#functions
34
- def call(object, args, context)
35
- new(filters: args.to_h, object: object, context: context).results
36
- end
37
-
38
- def arguments
39
- config[:args] ||= {}
40
- end
41
-
42
- def type(value = :default, &block)
43
- return config[:type] if value == :default && !block_given?
44
- config[:type] = block_given? ? GraphQL::ObjectType.define(&block) : value
45
- end
46
-
47
- def complexity(value = :default)
48
- return config[:complexity] || 1 if value == :default
49
- config[:complexity] = value
50
- end
51
-
52
- def description(value = :default)
53
- return config[:description] if value == :default
54
- config[:description] = value
55
- end
56
-
57
- def deprecation_reason(value = :default)
58
- return config[:deprecation_reason] if value == :default
59
- config[:deprecation_reason] = value
50
+ class NotIncludedInResolverError < ArgumentError
51
+ def initialize(base)
52
+ super "#{base.name} should inherit from GraphQL::Schema::Resolver. Current ancestors #{base.ancestors}"
60
53
  end
61
54
  end
62
55
 
@@ -65,20 +58,6 @@ module SearchObject
65
58
  super "GraphQL type has to passed as :type to '#{name}' option"
66
59
  end
67
60
  end
68
-
69
- # :api: private
70
- module Helper
71
- module_function
72
-
73
- def build_argument(name, options)
74
- argument = GraphQL::Argument.new
75
- argument.name = name.to_s
76
- argument.type = options.fetch(:type) { raise MissingTypeDefinitionError, name }
77
- argument.default_value = options[:default] if options.key? :default
78
- argument.description = options[:description] if options.key? :description
79
- argument
80
- end
81
- end
82
61
  end
83
62
  end
84
63
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Graphql
4
- VERSION = '0.1'.freeze
6
+ VERSION = '1.0.0'
5
7
  end
6
8
  end
7
9
  end
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'English'
5
6
  require 'search_object/plugin/graphql/version'
@@ -19,13 +20,12 @@ Gem::Specification.new do |spec|
19
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
21
  spec.require_paths = ['lib']
21
22
 
22
- spec.add_dependency 'search_object', '~> 1.2'
23
- spec.add_dependency 'graphql', '~> 1.5'
23
+ spec.add_dependency 'graphql', '~> 1.8'
24
+ spec.add_dependency 'search_object', '~> 1.2.2'
24
25
 
25
- spec.add_development_dependency 'bundler', '~> 1.13'
26
- spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'rspec', '~> 3.5'
28
- spec.add_development_dependency 'rubocop', '0.46.0'
29
- spec.add_development_dependency 'rubocop-rspec', '1.8.0'
30
26
  spec.add_development_dependency 'coveralls'
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rspec', '~> 3.8'
29
+ spec.add_development_dependency 'rubocop', '0.62.0'
30
+ spec.add_development_dependency 'rubocop-rspec', '1.31.0'
31
31
  end
@@ -1,29 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'graphql'
3
5
  require 'ostruct'
4
6
  require 'search_object/plugin/graphql'
5
7
 
6
- describe SearchObject::Plugin::Graphql do
7
- Post = Struct.new(:id) do
8
- def to_json
9
- { 'id' => id }
10
- end
8
+ Post = Struct.new(:id) do
9
+ def to_json(_options = {})
10
+ { 'id' => id }
11
11
  end
12
+ end
12
13
 
13
- PostType = GraphQL::ObjectType.define do
14
- name 'Post'
15
-
16
- field :id, !types.ID
17
- end
14
+ class PostType < GraphQL::Schema::Object
15
+ field :id, ID, null: false
16
+ end
18
17
 
18
+ describe SearchObject::Plugin::Graphql do
19
19
  def define_schema(&block)
20
- query_type = GraphQL::ObjectType.define do
21
- name 'Query'
20
+ query_type = Class.new(GraphQL::Schema::Object) do
21
+ graphql_name 'Query'
22
22
 
23
23
  instance_eval(&block)
24
24
  end
25
25
 
26
- GraphQL::Schema.define do
26
+ Class.new(GraphQL::Schema) do
27
27
  query query_type
28
28
 
29
29
  max_complexity 1000
@@ -31,7 +31,7 @@ describe SearchObject::Plugin::Graphql do
31
31
  end
32
32
 
33
33
  def define_search_class(&block)
34
- Class.new do
34
+ Class.new(GraphQL::Schema::Resolver) do
35
35
  include SearchObject.module(:graphql)
36
36
 
37
37
  scope { [] }
@@ -45,20 +45,38 @@ describe SearchObject::Plugin::Graphql do
45
45
 
46
46
  define_schema do
47
47
  if search_object.type.nil?
48
- field :posts, types[PostType], function: search_object
48
+ field :posts, [PostType], resolver: search_object
49
49
  else
50
- field :posts, function: search_object
50
+ field :posts, resolver: search_object
51
51
  end
52
52
  end
53
53
  end
54
54
 
55
- it 'can be used as GraphQL::Function' do
56
- schema = define_search_class_and_return_schema do
55
+ it 'requires class to inherit from GraphQL::Schema::Resolver' do
56
+ expect do
57
+ Class.new { include SearchObject.module(:graphql) }
58
+ end.to raise_error SearchObject::Plugin::Graphql::NotIncludedInResolverError
59
+ end
60
+
61
+ it 'can be used as GraphQL::Schema::Resolver' do
62
+ post_type = Class.new(GraphQL::Schema::Object) do
63
+ graphql_name 'Post'
64
+
65
+ field :id, GraphQL::Types::ID, null: false
66
+ end
67
+
68
+ search_object = define_search_class do
57
69
  scope { [Post.new('1'), Post.new('2'), Post.new('3')] }
58
70
 
71
+ type [post_type], null: 1
72
+
59
73
  option(:id, type: !types.ID) { |scope, value| scope.select { |p| p.id == value } }
60
74
  end
61
75
 
76
+ schema = define_schema do
77
+ field :posts, resolver: search_object
78
+ end
79
+
62
80
  result = schema.execute '{ posts(id: "2") { id } }'
63
81
 
64
82
  expect(result).to eq(
@@ -73,19 +91,19 @@ describe SearchObject::Plugin::Graphql do
73
91
  scope { object.posts }
74
92
  end
75
93
 
76
- parent_type = GraphQL::ObjectType.define do
77
- name 'ParentType'
94
+ parent_type = Class.new(GraphQL::Schema::Object) do
95
+ graphql_name 'Parent'
78
96
 
79
- field :posts, types[PostType], function: search_object
97
+ field :posts, [PostType], resolver: search_object
80
98
  end
81
99
 
82
100
  schema = define_schema do
83
- field :parent, parent_type do
84
- resolve ->(_obj, _args, _ctx) { OpenStruct.new posts: [Post.new('from_parent')] }
85
- end
101
+ field :parent, parent_type, null: false
86
102
  end
87
103
 
88
- result = schema.execute '{ parent { posts { id } } }'
104
+ root = OpenStruct.new(parent: OpenStruct.new(posts: [Post.new('from_parent')]))
105
+
106
+ result = schema.execute '{ parent { posts { id } } }', root_value: root
89
107
 
90
108
  expect(result).to eq(
91
109
  'data' => {
@@ -124,86 +142,40 @@ describe SearchObject::Plugin::Graphql do
124
142
  )
125
143
  end
126
144
 
127
- it 'can define a custom type' do
128
- schema = define_search_class_and_return_schema do
129
- type do
130
- name 'Test'
131
-
132
- field :title, types.String
133
- end
145
+ describe 'option' do
146
+ it 'converts GraphQL::Schema::Enum to SearchObject enum' do
147
+ schema = define_search_class_and_return_schema do
148
+ enum_type = Class.new(GraphQL::Schema::Enum) do
149
+ graphql_name 'PostOrder'
134
150
 
135
- description 'Test description'
136
- end
151
+ value 'PRICE'
152
+ value 'DATE'
153
+ end
137
154
 
138
- result = schema.execute <<-SQL
139
- {
140
- __type(name: "Query") {
141
- name
142
- fields {
143
- name
144
- deprecationReason
145
- type {
146
- name
147
- fields {
148
- name
149
- }
150
- }
151
- }
152
- }
153
- }
154
- SQL
155
+ option(:order, type: enum_type)
155
156
 
156
- expect(result).to eq(
157
- 'data' => {
158
- '__type' => {
159
- 'name' => 'Query',
160
- 'fields' => [{
161
- 'name' => 'posts',
162
- 'deprecationReason' => nil,
163
- 'type' => {
164
- 'name' => 'Test',
165
- 'fields' => [{
166
- 'name' => 'title'
167
- }]
168
- }
169
- }]
170
- }
171
- }
172
- )
173
- end
157
+ define_method(:apply_order_with_price) do |_scope|
158
+ [Post.new('price')]
159
+ end
174
160
 
175
- it 'can be marked as deprecated' do
176
- schema = define_search_class_and_return_schema do
177
- type types[PostType]
178
- deprecation_reason 'Not needed any more'
179
- end
161
+ define_method(:apply_order_with_date) do |_scope|
162
+ [Post.new('date')]
163
+ end
164
+ end
180
165
 
181
- result = schema.execute <<-QUERY
182
- {
183
- __type(name: "Query") {
184
- name
185
- fields {
186
- name
187
- }
188
- }
189
- }
190
- QUERY
166
+ result = schema.execute '{ posts(order: PRICE) { id } }'
191
167
 
192
- expect(result).to eq(
193
- 'data' => {
194
- '__type' => {
195
- 'name' => 'Query',
196
- 'fields' => []
168
+ expect(result).to eq(
169
+ 'data' => {
170
+ 'posts' => [Post.new('price').to_json]
197
171
  }
198
- }
199
- )
200
- end
172
+ )
173
+ end
201
174
 
202
- describe 'option' do
203
175
  it 'converts GraphQL::EnumType to SearchObject enum' do
204
176
  schema = define_search_class_and_return_schema do
205
- enum_type = GraphQL::EnumType.define do
206
- name 'TestEnum'
177
+ enum_type = Class.new(GraphQL::Schema::Enum) do
178
+ graphql_name 'TestEnum'
207
179
 
208
180
  value 'PRICE'
209
181
  value 'DATE'
@@ -245,9 +217,21 @@ describe SearchObject::Plugin::Graphql do
245
217
  )
246
218
  end
247
219
 
220
+ it 'accepts "required"' do
221
+ schema = define_search_class_and_return_schema do
222
+ option(:id, type: types.String, required: true) do |_scope, value|
223
+ [Post.new(value)]
224
+ end
225
+ end
226
+
227
+ result = schema.execute '{ posts { id } }'
228
+
229
+ expect(result['errors'][0]['message']).to eq("Field 'posts' is missing required arguments: id")
230
+ end
231
+
248
232
  it 'accepts description' do
249
233
  schema = define_search_class_and_return_schema do
250
- type PostType
234
+ type PostType, null: true
251
235
 
252
236
  option('option', type: types.String, description: 'what this argument does') { [] }
253
237
  end
@@ -281,6 +265,40 @@ describe SearchObject::Plugin::Graphql do
281
265
  )
282
266
  end
283
267
 
268
+ it 'accepts camelize' do
269
+ schema = define_search_class_and_return_schema do
270
+ type PostType, null: true
271
+
272
+ option('option_field', type: types.String, camelize: false)
273
+ end
274
+
275
+ result = schema.execute <<-SQL
276
+ {
277
+ __type(name: "Query") {
278
+ name
279
+ fields {
280
+ args {
281
+ name
282
+ }
283
+ }
284
+ }
285
+ }
286
+ SQL
287
+
288
+ expect(result.to_h).to eq(
289
+ 'data' => {
290
+ '__type' => {
291
+ 'name' => 'Query',
292
+ 'fields' => [{
293
+ 'args' => [{
294
+ 'name' => 'option_field'
295
+ }]
296
+ }]
297
+ }
298
+ }
299
+ )
300
+ end
301
+
284
302
  it 'raises error when no type is given' do
285
303
  expect { define_search_class { option :name } }.to raise_error described_class::MissingTypeDefinitionError
286
304
  end