graphql_rails 1.0.0 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +1 -0
  3. data/.rubocop.yml +3 -3
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +2 -2
  6. data/CHANGELOG.md +22 -0
  7. data/Gemfile +3 -2
  8. data/Gemfile.lock +145 -119
  9. data/docs/README.md +3 -3
  10. data/docs/components/controller.md +23 -7
  11. data/docs/components/model.md +43 -3
  12. data/docs/getting_started/quick_start.md +1 -1
  13. data/docs/index.html +1 -1
  14. data/docs/other_tools/query_runner.md +1 -1
  15. data/docs/other_tools/schema_dump.md +1 -1
  16. data/graphql_rails.gemspec +5 -5
  17. data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +10 -7
  18. data/lib/graphql_rails/attributes/attributable.rb +5 -9
  19. data/lib/graphql_rails/attributes/attribute.rb +26 -6
  20. data/lib/graphql_rails/attributes/attribute_name_parser.rb +4 -4
  21. data/lib/graphql_rails/attributes/input_attribute.rb +5 -1
  22. data/lib/graphql_rails/attributes/type_parseable.rb +17 -13
  23. data/lib/graphql_rails/concerns/service.rb +6 -2
  24. data/lib/graphql_rails/controller.rb +6 -6
  25. data/lib/graphql_rails/controller/action.rb +5 -1
  26. data/lib/graphql_rails/controller/build_controller_action_resolver.rb +2 -2
  27. data/lib/graphql_rails/controller/log_controller_action.rb +7 -2
  28. data/lib/graphql_rails/controller/request.rb +1 -1
  29. data/lib/graphql_rails/controller/request/format_errors.rb +1 -1
  30. data/lib/graphql_rails/decorator/relation_decorator.rb +0 -4
  31. data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
  32. data/lib/graphql_rails/model/build_connection_type.rb +5 -1
  33. data/lib/graphql_rails/model/build_connection_type/count_items.rb +2 -2
  34. data/lib/graphql_rails/model/build_graphql_input_type.rb +1 -1
  35. data/lib/graphql_rails/model/call_graphql_model_method.rb +14 -1
  36. data/lib/graphql_rails/model/configurable.rb +6 -2
  37. data/lib/graphql_rails/model/configuration.rb +9 -4
  38. data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
  39. data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
  40. data/lib/graphql_rails/router.rb +2 -2
  41. data/lib/graphql_rails/router/resource_routes_builder.rb +8 -8
  42. data/lib/graphql_rails/router/route.rb +3 -7
  43. data/lib/graphql_rails/router/schema_builder.rb +5 -1
  44. data/lib/graphql_rails/rspec_controller_helpers.rb +2 -2
  45. data/lib/graphql_rails/version.rb +1 -1
  46. metadata +25 -18
  47. data/lib/graphql_rails/model/build_graphql_type.rb +0 -53
@@ -78,7 +78,7 @@ Specifies input type:
78
78
  class OrderController < GraphqlRails::Controller
79
79
  action(:create)
80
80
  .permit_input(:price, type: :integer!)
81
- # Same as `.permit(amount: :integer!)`
81
+ # Same as `.permit(price: :integer!)`
82
82
  end
83
83
  ```
84
84
 
@@ -169,7 +169,7 @@ class UsersController < GraphqlRails::Controller
169
169
  end
170
170
  ```
171
171
 
172
- Also check ['decorating controller responses']('components/decorator') for more details about working with active record and decorators.
172
+ Also check ['decorating controller responses'](components/decorator) for more details about working with active record and decorators.
173
173
 
174
174
  #### *max_page_size*
175
175
 
@@ -219,6 +219,22 @@ class UsersController < GraphqlRails::Controller
219
219
  end
220
220
  ```
221
221
 
222
+ You can also return raw graphql-ruby types:
223
+
224
+ ```ruby
225
+ # raw graphql-ruby type:
226
+ class OrderType < GraphQL::Schema::Object
227
+ graphql_name 'Order'
228
+ field :id, ID
229
+ end
230
+
231
+ class UsersController < GraphqlRails::Controller
232
+ action(:last_order).permit(:id).returns(OrderType)
233
+ end
234
+ ```
235
+
236
+ Check [graphql-ruby documentation](https://graphql-ruby.org) for more details about graphql-ruby types.
237
+
222
238
  ### *returns_list*
223
239
 
224
240
  When you have defined `model` dynamically, you can use `returns_list` to indicate that action must return list without specifying model type for each action. By default list and inner types are required but you can change that with `required_list: false` and `required_inner: false`
@@ -430,7 +446,7 @@ class UsersController < GraphqlRails::Controller
430
446
  raise 'Not authenticated' unless User.where(token: params[:token]).exist?
431
447
  end
432
448
 
433
- def require_auth_token
449
+ def require_admin_token
434
450
  raise 'Admin not authenticated' unless Admin.where(token: params[:admin_token]).exist?
435
451
  end
436
452
  end
@@ -438,13 +454,13 @@ end
438
454
 
439
455
  ## decorating objects
440
456
 
441
- See ['Decorating controller responses']('components/decorator') for various options how you can decorate paginated responses
457
+ See ['Decorating controller responses'](components/decorator) for various options how you can decorate paginated responses
442
458
 
443
459
  ## Rendering errors
444
460
 
445
461
  ### Rendering strings as errors
446
462
 
447
- The simples way to render error is to provide list of error messages, like this:
463
+ The simplest way to render an error is to provide a list of error messages, like this:
448
464
 
449
465
  ```ruby
450
466
  class UsersController < GraphqlRails::Controller
@@ -509,12 +525,12 @@ end
509
525
 
510
526
  ### Raising custom error classes
511
527
 
512
- If you want to have customized error classes you need to create errors which inherits from `GraphqlRails::ExecutionError`
528
+ If you want to have customized error classes you need to create errors which inherit from `GraphqlRails::ExecutionError`
513
529
 
514
530
  ```ruby
515
531
  class MyCustomError < GraphqlRails::ExecutionError
516
532
  def to_h
517
- # this part will be rendered in graphl
533
+ # this part will be rendered in graphql
518
534
  { something_custom: 'yes' }
519
535
  end
520
536
  end
@@ -17,13 +17,13 @@ end
17
17
 
18
18
  ## graphql
19
19
 
20
- This method must be called inside your model body. `grapqhl` is used for making your model convertible to graphql type.
20
+ This method must be called inside your model body. `graphql` is used for making your model convertible to graphql type.
21
21
 
22
22
  ## attribute
23
23
 
24
24
  Most commonly you will use `attribute` to make your model methods and attributes visible via graphql endpoint.
25
25
 
26
- ## attribute.type
26
+ ### attribute.type
27
27
 
28
28
  Some types can be determined by attribute name, so you can skip this attribute:
29
29
 
@@ -54,9 +54,33 @@ class User
54
54
  end
55
55
  ```
56
56
 
57
+ #### attribute.type: using graphql-ruby objects
58
+
59
+ You can also use raw graphql-ruby objects as attribute types. Here is an example:
60
+
61
+ ```ruby
62
+ # raw graphql-ruby type:
63
+ class AddressType < GraphQL::Schema::Object
64
+ graphql_name 'Address'
65
+
66
+ field :city, String, null: false
67
+ field :street_name, String, null: false
68
+ field :street_number, Integer
69
+ end
70
+
71
+ # GraphqlRails model:
72
+ class User
73
+ include GraphqlRails::Model
74
+
75
+ graphql.attribute :address, type: AddressType, required: true
76
+ end
77
+ ```
78
+
79
+ Check [graphql-ruby documentation](https://graphql-ruby.org) for more details about graphql-ruby types.
80
+
57
81
  ### attribute.property
58
82
 
59
- By default graphql attribute names are expected to be same as model methods/attributes, but if you want to use different name on grapqhl side, you can use `propery` option:
83
+ By default graphql attribute names are expected to be same as model methods/attributes, but if you want to use different name on grapqhl side, you can use `property` option:
60
84
 
61
85
  ```ruby
62
86
  class User
@@ -86,6 +110,22 @@ class User
86
110
  end
87
111
  ```
88
112
 
113
+ ### attribute.options
114
+
115
+ Allows passing options to attribute definition. Available options:
116
+
117
+ * `attribute_name_format` - if `:original` value is passed, it will not camelCase attribute name.
118
+
119
+ ```ruby
120
+ class User
121
+ include GraphqlRails::Model
122
+
123
+ graphql do |c|
124
+ c.attribute :first_name # will be accessible as firstName from client side
125
+ c.attribute :first_name, options: { attribute_name_format: :original } # will be accessible as first_name from client side
126
+ end
127
+ end
128
+
89
129
  ### attribute.permit
90
130
 
91
131
  To define attributes which are accepted by each model method, you need to call `permit` method, like this:
@@ -11,7 +11,7 @@ bundle exec rails g graphql_rails:install
11
11
  ```ruby
12
12
  # config/graphql/routes.rb
13
13
  GraphqlRails::Router.draw do
14
- # will create createUser, updateUser, deleteUser mutations and user, users queries.
14
+ # will create createUser, updateUser, destroyUser mutations and user, users queries.
15
15
  # expects that UsersController class exist
16
16
  resources :users
17
17
 
data/docs/index.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <title>Document</title>
5
+ <title>GraphqlRails</title>
6
6
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7
7
  <meta name="description" content="Description">
8
8
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
@@ -16,7 +16,7 @@ end
16
16
 
17
17
  ## Executing grouped schema
18
18
 
19
- If you have multiple schemas (read [routes section]((components/routes) on how to do that) and you want to render group specific schema, you need to provide group name, like this:
19
+ If you have multiple schemas (read [routes section](components/routes) on how to do that) and you want to render group specific schema, you need to provide group name, like this:
20
20
 
21
21
  ```ruby
22
22
  class MyRailsClass < ApplicationController
@@ -8,7 +8,7 @@ rake graphql_rails:schema:dump
8
8
 
9
9
  ## Dumping non default schema
10
10
 
11
- You can have multiple graphql schemas. Read more about this in [routes section]((components/routes). In order to generate schema for one of groups, provide optional `name` argument:
11
+ You can have multiple graphql schemas. Read more about this in [routes section](components/routes). In order to generate schema for one of groups, provide optional `name` argument:
12
12
 
13
13
  ```bash
14
14
  rake graphql_rails:schema:dump['your_group_name']
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Povilas Jurčys']
10
10
  spec.email = ['po.jurcys@gmail.com']
11
11
 
12
- spec.summary = %q{GraphQL server and client for rails}
12
+ spec.summary = %q{Rails style structure for GraphQL API.}
13
13
  spec.homepage = 'https://github.com/samesystem/graphql_rails'
14
14
  spec.license = 'MIT'
15
15
 
@@ -20,13 +20,13 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'graphql', '>= 1.9.12'
23
+ spec.add_dependency 'graphql', '~> 1.12', '>= 1.12.4'
24
24
  spec.add_dependency 'activesupport', '>= 4'
25
25
 
26
- spec.add_development_dependency 'bundler', '~> 1.16'
27
- spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'bundler', '~> 2'
27
+ spec.add_development_dependency 'rake', '~> 13.0'
28
28
  spec.add_development_dependency 'rspec', '~> 3.0'
29
29
  spec.add_development_dependency 'activerecord'
30
30
  spec.add_development_dependency 'pry-byebug'
31
- spec.add_development_dependency 'rails', '~> 5'
31
+ spec.add_development_dependency 'rails', '~> 6'
32
32
  end
@@ -3,16 +3,19 @@
3
3
  require 'rails_helper'
4
4
 
5
5
  RSpec.describe GraphqlRouter do
6
- describe '#execute' do
6
+ subject(:schema) { described_class.graphql_schema }
7
+
8
+ describe '#to_definition' do
9
+ subject(:to_definition) { schema.to_definition.strip }
10
+
11
+ let(:schema_dump_path) { Rails.root.join('spec', 'fixtures', 'graphql_schema.graphql') }
12
+ let(:previous_definition) { File.read(schema_dump_path).strip }
13
+
7
14
  it 'returns correct structure' do
8
15
  # if you need to update saved_schema, run rake task:
9
- # $ RAILS_ENV=test rake graphql_rails:schema:dump
10
-
11
- schema_path = Rails.root.join('spec', 'fixtures', 'graphql_schema.graphql')
12
- saved_schema = File.read(schema_path).strip
13
- real_schema = described_class.to_definition.strip
16
+ # $ RAILS_ENV=test bin/rake graphql_rails:schema:dump
14
17
 
15
- expect(real_schema).to eq(saved_schema)
18
+ expect(to_definition).to eq(previous_definition)
16
19
  end
17
20
  end
18
21
  end
@@ -21,11 +21,9 @@ module GraphqlRails
21
21
  end
22
22
 
23
23
  def required?
24
- if @required.nil?
25
- attribute_name_parser.required? || !initial_type.to_s[/!$/].nil?
26
- else
27
- @required
28
- end
24
+ return @required unless @required.nil?
25
+
26
+ attribute_name_parser.required? || !initial_type.to_s[/!$/].nil? || initial_type.is_a?(GraphQL::Schema::NonNull)
29
27
  end
30
28
 
31
29
  def required
@@ -46,10 +44,8 @@ module GraphqlRails
46
44
  !required?
47
45
  end
48
46
 
49
- protected
50
-
51
- def options
52
- {}
47
+ def scalar_type?
48
+ type_parser.raw_graphql_type? || type_parser.core_scalar_type?
53
49
  end
54
50
 
55
51
  private
@@ -13,14 +13,17 @@ module GraphqlRails
13
13
 
14
14
  attr_reader :attributes
15
15
 
16
- def initialize(name, type = nil, description: nil, property: name, required: nil)
16
+ # rubocop:disable Metrics/ParameterLists
17
+ def initialize(name, type = nil, description: nil, property: name, required: nil, options: {})
17
18
  @initial_type = type
18
19
  @initial_name = name
20
+ @options = options
19
21
  @description = description
20
22
  @property = property.to_s
21
23
  @required = required
22
24
  @attributes ||= {}
23
25
  end
26
+ # rubocop:enable Metrics/ParameterLists
24
27
 
25
28
  def type(new_type = nil)
26
29
  return @initial_type if new_type.nil?
@@ -43,18 +46,29 @@ module GraphqlRails
43
46
  self
44
47
  end
45
48
 
49
+ def options(new_options = {})
50
+ return @options if new_options.blank?
51
+
52
+ @options = new_options
53
+ self
54
+ end
55
+
46
56
  def field_args
47
57
  [
48
58
  field_name,
49
59
  type_parser.type_arg,
50
- *description,
51
- {
52
- method: property.to_sym,
53
- null: optional?
54
- }
60
+ *description
55
61
  ]
56
62
  end
57
63
 
64
+ def field_options
65
+ {
66
+ method: property.to_sym,
67
+ null: optional?,
68
+ camelize: camelize?
69
+ }
70
+ end
71
+
58
72
  def argument_args
59
73
  [
60
74
  field_name,
@@ -69,6 +83,12 @@ module GraphqlRails
69
83
  protected
70
84
 
71
85
  attr_reader :initial_type, :initial_name
86
+
87
+ private
88
+
89
+ def camelize?
90
+ options[:input_format] != :original && options[:attribute_name_format] != :original
91
+ end
72
92
  end
73
93
  end
74
94
  end
@@ -27,11 +27,11 @@ module GraphqlRails
27
27
  @graphql_type ||= \
28
28
  case name
29
29
  when 'id', /_id\Z/
30
- GraphQL::ID_TYPE
30
+ GraphQL::Types::ID
31
31
  when /\?\Z/
32
- GraphQL::BOOLEAN_TYPE
32
+ GraphQL::Types::Boolean
33
33
  else
34
- GraphQL::STRING_TYPE
34
+ GraphQL::Types::String
35
35
  end
36
36
  end
37
37
 
@@ -44,7 +44,7 @@ module GraphqlRails
44
44
  attr_reader :options
45
45
 
46
46
  def original_format?
47
- options[:input_format] == :original
47
+ options[:input_format] == :original || options[:attribute_name_format] == :original
48
48
  end
49
49
 
50
50
  def preprocesed_name
@@ -24,7 +24,11 @@ module GraphqlRails
24
24
  def input_argument_args
25
25
  type = raw_input_type || input_type_parser.input_type_arg
26
26
 
27
- [field_name, type, { required: required?, description: description, camelize: false }]
27
+ [field_name, type]
28
+ end
29
+
30
+ def input_argument_options
31
+ { required: required?, description: description, camelize: false }
28
32
  end
29
33
 
30
34
  def paginated?
@@ -11,23 +11,23 @@ module GraphqlRails
11
11
  class UnknownTypeError < ArgumentError; end
12
12
 
13
13
  TYPE_MAPPING = {
14
- 'id' => GraphQL::ID_TYPE,
14
+ 'id' => GraphQL::Types::ID,
15
15
 
16
- 'int' => GraphQL::INT_TYPE,
17
- 'integer' => GraphQL::INT_TYPE,
16
+ 'int' => GraphQL::Types::Int,
17
+ 'integer' => GraphQL::Types::Int,
18
18
 
19
- 'string' => GraphQL::STRING_TYPE,
20
- 'str' => GraphQL::STRING_TYPE,
21
- 'text' => GraphQL::STRING_TYPE,
22
- 'time' => GraphQL::STRING_TYPE,
23
- 'date' => GraphQL::STRING_TYPE,
19
+ 'string' => GraphQL::Types::String,
20
+ 'str' => GraphQL::Types::String,
21
+ 'text' => GraphQL::Types::String,
22
+ 'time' => GraphQL::Types::String,
23
+ 'date' => GraphQL::Types::String,
24
24
 
25
- 'bool' => GraphQL::BOOLEAN_TYPE,
26
- 'boolean' => GraphQL::BOOLEAN_TYPE,
25
+ 'bool' => GraphQL::Types::Boolean,
26
+ 'boolean' => GraphQL::Types::Boolean,
27
27
 
28
- 'float' => GraphQL::FLOAT_TYPE,
29
- 'double' => GraphQL::FLOAT_TYPE,
30
- 'decimal' => GraphQL::FLOAT_TYPE
28
+ 'float' => GraphQL::Types::Float,
29
+ 'double' => GraphQL::Types::Float,
30
+ 'decimal' => GraphQL::Types::Float
31
31
  }.freeze
32
32
 
33
33
  WRAPPER_TYPES = [
@@ -57,6 +57,10 @@ module GraphqlRails
57
57
  unparsed_type < GraphQL::Schema::Member
58
58
  end
59
59
 
60
+ def core_scalar_type?
61
+ unwrapped_scalar_type.in?(TYPE_MAPPING.values)
62
+ end
63
+
60
64
  def graphql_model
61
65
  type_class = \
62
66
  if unparsed_type.is_a?(Class) && unparsed_type < GraphqlRails::Model
@@ -7,8 +7,12 @@ module GraphqlRails
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  class_methods do
10
- def call(*args, &block)
11
- new(*args).call(&block)
10
+ def call(*args, **kwargs, &block)
11
+ if kwargs.present?
12
+ new(*args, **kwargs).call(&block)
13
+ else
14
+ new(*args).call(&block)
15
+ end
12
16
  end
13
17
  end
14
18
  end
@@ -18,16 +18,16 @@ module GraphqlRails
18
18
  subclass.instance_variable_set(:@controller_configuration, new_config)
19
19
  end
20
20
 
21
- def before_action(*args, &block)
22
- controller_configuration.add_action_hook(:before, *args, &block)
21
+ def before_action(*args, **kwargs, &block)
22
+ controller_configuration.add_action_hook(:before, *args, **kwargs, &block)
23
23
  end
24
24
 
25
- def around_action(*args, &block)
26
- controller_configuration.add_action_hook(:around, *args, &block)
25
+ def around_action(*args, **kwargs, &block)
26
+ controller_configuration.add_action_hook(:around, *args, **kwargs, &block)
27
27
  end
28
28
 
29
- def after_action(*args, &block)
30
- controller_configuration.add_action_hook(:after, *args, &block)
29
+ def after_action(*args, **kwargs, &block)
30
+ controller_configuration.add_action_hook(:after, *args, **kwargs, &block)
31
31
  end
32
32
 
33
33
  def action(action_name)