graphql_rails 1.0.0 → 1.1.0

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 (39) 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 +3 -0
  7. data/Gemfile +3 -2
  8. data/Gemfile.lock +141 -117
  9. data/docs/README.md +2 -2
  10. data/docs/components/controller.md +21 -5
  11. data/docs/components/model.md +43 -3
  12. data/docs/index.html +1 -1
  13. data/graphql_rails.gemspec +5 -5
  14. data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +10 -7
  15. data/lib/graphql_rails/attributes/attributable.rb +2 -4
  16. data/lib/graphql_rails/attributes/attribute.rb +26 -6
  17. data/lib/graphql_rails/attributes/attribute_name_parser.rb +1 -1
  18. data/lib/graphql_rails/attributes/input_attribute.rb +5 -1
  19. data/lib/graphql_rails/concerns/service.rb +6 -2
  20. data/lib/graphql_rails/controller.rb +6 -6
  21. data/lib/graphql_rails/controller/action.rb +5 -1
  22. data/lib/graphql_rails/controller/build_controller_action_resolver.rb +2 -2
  23. data/lib/graphql_rails/controller/request.rb +1 -1
  24. data/lib/graphql_rails/controller/request/format_errors.rb +1 -1
  25. data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
  26. data/lib/graphql_rails/model/build_graphql_input_type.rb +1 -1
  27. data/lib/graphql_rails/model/call_graphql_model_method.rb +14 -1
  28. data/lib/graphql_rails/model/configurable.rb +6 -2
  29. data/lib/graphql_rails/model/configuration.rb +9 -4
  30. data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
  31. data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
  32. data/lib/graphql_rails/router.rb +2 -2
  33. data/lib/graphql_rails/router/resource_routes_builder.rb +8 -8
  34. data/lib/graphql_rails/router/route.rb +3 -7
  35. data/lib/graphql_rails/router/schema_builder.rb +1 -1
  36. data/lib/graphql_rails/rspec_controller_helpers.rb +2 -2
  37. data/lib/graphql_rails/version.rb +1 -1
  38. metadata +21 -14
  39. data/lib/graphql_rails/model/build_graphql_type.rb +0 -53
@@ -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:
@@ -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">
@@ -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.11', '>= 1.11.6'
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
@@ -46,10 +46,8 @@ module GraphqlRails
46
46
  !required?
47
47
  end
48
48
 
49
- protected
50
-
51
- def options
52
- {}
49
+ def scalar_type?
50
+ type_parser.raw_graphql_type? || type_parser.unwrapped_scalar_type.class == GraphQL::ScalarType
53
51
  end
54
52
 
55
53
  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
@@ -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?
@@ -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)
@@ -36,7 +36,11 @@ module GraphqlRails
36
36
  end
37
37
 
38
38
  def type_args
39
- [type_parser.type_arg, null: !type_parser.required?]
39
+ [type_parser.type_arg]
40
+ end
41
+
42
+ def type_options
43
+ { null: !type_parser.required? }
40
44
  end
41
45
 
42
46
  private
@@ -19,13 +19,13 @@ module GraphqlRails
19
19
  action = build_action
20
20
 
21
21
  Class.new(ControllerActionResolver) do
22
- type(*action.type_args)
22
+ type(*action.type_args, **action.type_options)
23
23
  description(action.description)
24
24
  controller(action.controller)
25
25
  controller_action_name(action.name)
26
26
 
27
27
  action.arguments.each do |attribute|
28
- argument(*attribute.input_argument_args)
28
+ argument(*attribute.input_argument_args, **attribute.input_argument_options)
29
29
  end
30
30
 
31
31
  def self.inspect
@@ -16,7 +16,7 @@ module GraphqlRails
16
16
  end
17
17
 
18
18
  def errors=(new_errors)
19
- @errors = FormatErrors.call(new_errors)
19
+ @errors = FormatErrors.call(not_formatted_errors: new_errors)
20
20
 
21
21
  @errors.each { |error| context.add_error(error) }
22
22
  end
@@ -12,7 +12,7 @@ module GraphqlRails
12
12
  class FormatErrors
13
13
  include Service
14
14
 
15
- def initialize(not_formatted_errors)
15
+ def initialize(not_formatted_errors:)
16
16
  @not_formatted_errors = not_formatted_errors
17
17
  end
18
18
 
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlRails
4
+ module Model
5
+ # Adds graphql attributes as graphql fields to given graphql schema object.
6
+ class AddFieldsToGraphqlType
7
+ require 'graphql_rails/concerns/service'
8
+ require 'graphql_rails/model/call_graphql_model_method'
9
+
10
+ include ::GraphqlRails::Service
11
+
12
+ def initialize(klass:, attributes:)
13
+ @klass = klass
14
+ @attributes = attributes
15
+ end
16
+
17
+ def call
18
+ attributes.each { |attribute| define_graphql_field(attribute) }
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :attributes, :klass
24
+
25
+ def define_graphql_field(attribute) # rubocop:disable Metrics/MethodLength)
26
+ klass.class_eval do
27
+ field(*attribute.field_args, **attribute.field_options) do
28
+ attribute.attributes.values.each do |arg_attribute|
29
+ argument(*arg_attribute.input_argument_args, **arg_attribute.input_argument_options)
30
+ end
31
+ end
32
+
33
+ define_method(attribute.field_name) do |**kwargs|
34
+ CallGraphqlModelMethod.call(
35
+ model: object,
36
+ attribute_config: attribute,
37
+ method_keyword_arguments: kwargs,
38
+ graphql_context: context
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -24,7 +24,7 @@ module GraphqlRails
24
24
  description(type_description)
25
25
 
26
26
  type_attributes.each_value do |type_attribute|
27
- argument(*type_attribute.input_argument_args)
27
+ argument(*type_attribute.input_argument_args, **type_attribute.input_argument_options)
28
28
  end
29
29
 
30
30
  def self.inspect
@@ -31,10 +31,23 @@ module GraphqlRails
31
31
  if custom_keyword_arguments.empty?
32
32
  model.send(method_name)
33
33
  else
34
- model.send(method_name, **custom_keyword_arguments)
34
+ formatted_arguments = formatted_method_input(custom_keyword_arguments)
35
+ model.send(method_name, **formatted_arguments)
35
36
  end
36
37
  end
37
38
 
39
+ def formatted_method_input(keyword_arguments)
40
+ keyword_arguments.transform_values do |input_argument|
41
+ formatted_method_input_argument(input_argument)
42
+ end
43
+ end
44
+
45
+ def formatted_method_input_argument(argument)
46
+ return argument.to_h if argument.is_a?(GraphQL::Schema::InputObject)
47
+
48
+ argument
49
+ end
50
+
38
51
  def method_name
39
52
  attribute_config.property
40
53
  end
@@ -9,11 +9,15 @@ module GraphqlRails
9
9
  @attributes ||= {}
10
10
  end
11
11
 
12
- def name(type_name = nil)
13
- @name = type_name if type_name
12
+ def name(graphql_name = nil)
13
+ @name = graphql_name if graphql_name
14
14
  @name || default_name
15
15
  end
16
16
 
17
+ def type_name
18
+ @type_name ||= "#{name.camelize}Type#{SecureRandom.hex}"
19
+ end
20
+
17
21
  def description(new_description = nil)
18
22
  @description = new_description if new_description
19
23
  @description
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'graphql_rails/attributes'
4
- require 'graphql_rails/model/build_graphql_type'
4
+ require 'graphql_rails/model/find_or_build_graphql_type'
5
5
  require 'graphql_rails/model/build_enum_type'
6
6
  require 'graphql_rails/model/input'
7
7
  require 'graphql_rails/model/configurable'
@@ -32,7 +32,9 @@ module GraphqlRails
32
32
 
33
33
  attributes[key].tap do |attribute|
34
34
  attribute_options.each do |method_name, args|
35
- attribute.public_send(method_name, args)
35
+ send_args = [method_name]
36
+ send_args << args if attribute.method(method_name).parameters.present?
37
+ attribute.public_send(*send_args)
36
38
  end
37
39
 
38
40
  yield(attribute) if block_given?
@@ -54,8 +56,11 @@ module GraphqlRails
54
56
  end
55
57
 
56
58
  def graphql_type
57
- @graphql_type ||= BuildGraphqlType.call(
58
- name: name, description: description, attributes: attributes
59
+ @graphql_type ||= FindOrBuildGraphqlType.call(
60
+ name: name,
61
+ description: description,
62
+ attributes: attributes,
63
+ type_name: type_name
59
64
  )
60
65
  end
61
66