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
@@ -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
@@ -51,8 +51,7 @@ module GraphqlRails
51
51
  params
52
52
  else
53
53
  filter_options = Rails.configuration.filter_parameters
54
- parametter_filter = ActionDispatch::Http::ParameterFilter.new(filter_options)
55
- parametter_filter.filter(params)
54
+ parameter_filter_class.new(filter_options).filter(params)
56
55
  end
57
56
  end
58
57
 
@@ -61,6 +60,12 @@ module GraphqlRails
61
60
 
62
61
  Rails.application.config.filter_parameters || []
63
62
  end
63
+
64
+ def parameter_filter_class
65
+ return ActiveSupport::ParameterFilter if Object.const_defined?('ActiveSupport::ParameterFilter')
66
+
67
+ ActionDispatch::Http::ParameterFilter
68
+ end
64
69
  end
65
70
  end
66
71
  end
@@ -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
 
@@ -71,9 +71,5 @@ module GraphqlRails
71
71
  self.class.new(decorator: decorator, relation: new_relation, decorator_args: decorator_args)
72
72
  end
73
73
  end
74
-
75
- GraphQL::Relay::BaseConnection.register_connection_implementation(
76
- RelationDecorator, GraphQL::Relay::RelationConnection
77
- )
78
74
  end
79
75
  end
@@ -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
@@ -30,7 +30,11 @@ module GraphqlRails
30
30
  graphql_name("#{type.graphql_name}Connection")
31
31
  edge_type(edge_type)
32
32
 
33
- field :total, Integer, null: false, resolve: CountItems
33
+ field :total, Integer, null: false
34
+
35
+ def total
36
+ CountItems.call(object)
37
+ end
34
38
  end
35
39
  end
36
40
 
@@ -10,7 +10,7 @@ module GraphqlRails
10
10
 
11
11
  include ::GraphqlRails::Service
12
12
 
13
- def initialize(graphql_object, _args, _ctx)
13
+ def initialize(graphql_object)
14
14
  @graphql_object = graphql_object
15
15
  end
16
16
 
@@ -27,7 +27,7 @@ module GraphqlRails
27
27
  attr_reader :graphql_object
28
28
 
29
29
  def list
30
- graphql_object.nodes
30
+ graphql_object.items
31
31
  end
32
32
 
33
33
  def active_record?
@@ -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
 
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlRails
4
+ module Model
5
+ # stores information about model specific config, like attributes and types
6
+ class FindOrBuildGraphqlType
7
+ require 'graphql_rails/concerns/service'
8
+ require 'graphql_rails/model/find_or_build_graphql_type_class'
9
+ require 'graphql_rails/model/add_fields_to_graphql_type'
10
+
11
+ include ::GraphqlRails::Service
12
+
13
+ def initialize(name:, description:, attributes:, type_name:)
14
+ @name = name
15
+ @description = description
16
+ @attributes = attributes
17
+ @type_name = type_name
18
+ end
19
+
20
+ def call
21
+ klass.tap { add_fields_to_graphql_type if new_class? }
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :name, :description, :attributes, :type_name
27
+
28
+ delegate :klass, :new_class?, to: :type_class_finder
29
+
30
+ def type_class_finder
31
+ @type_class_finder ||= FindOrBuildGraphqlTypeClass.new(
32
+ name: name,
33
+ type_name: type_name,
34
+ description: description
35
+ )
36
+ end
37
+
38
+ def add_fields_to_graphql_type
39
+ AddFieldsToGraphqlType.call(klass: klass, attributes: attributes.values.select(&:scalar_type?))
40
+
41
+ attributes.values.reject(&:scalar_type?).tap do |dynamic_attributes|
42
+ find_or_build_dynamic_graphql_types(dynamic_attributes) do |name, description, attributes, type_name|
43
+ self.class.call(
44
+ name: name, description: description,
45
+ attributes: attributes, type_name: type_name
46
+ )
47
+ end
48
+ AddFieldsToGraphqlType.call(klass: klass, attributes: dynamic_attributes)
49
+ end
50
+ end
51
+
52
+ def find_or_build_dynamic_graphql_types(dynamic_attributes)
53
+ dynamic_attributes.each do |attribute|
54
+ yield(
55
+ attribute.graphql_model.graphql.name,
56
+ attribute.graphql_model.graphql.description,
57
+ attribute.graphql_model.graphql.attributes,
58
+ attribute.graphql_model.graphql.type_name
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlRails
4
+ module Model
5
+ # Initializes class to define graphql type and fields.
6
+ class FindOrBuildGraphqlTypeClass
7
+ require 'graphql_rails/concerns/service'
8
+
9
+ include ::GraphqlRails::Service
10
+
11
+ def initialize(name:, type_name:, description: nil)
12
+ @name = name
13
+ @type_name = type_name
14
+ @description = description
15
+ @new_class = false
16
+ end
17
+
18
+ def klass
19
+ @klass ||= Object.const_defined?(type_name) && Object.const_get(type_name) || build_graphql_type_klass
20
+ end
21
+
22
+ def new_class?
23
+ new_class
24
+ end
25
+
26
+ private
27
+
28
+ attr_accessor :new_class
29
+ attr_reader :name, :type_name, :description
30
+
31
+ def build_graphql_type_klass
32
+ graphql_type_name = name
33
+ graphql_type_description = description
34
+
35
+ graphql_type_klass = Class.new(GraphQL::Schema::Object) do
36
+ graphql_name(graphql_type_name)
37
+ description(graphql_type_description)
38
+ end
39
+
40
+ self.new_class = true
41
+
42
+ Object.const_set(type_name, graphql_type_klass)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -85,7 +85,7 @@ module GraphqlRails
85
85
  default_options = { module_name: module_name, group_names: group_names }
86
86
  full_options = default_options.merge(new_router_options)
87
87
 
88
- self.class.new(full_options)
88
+ self.class.new(**full_options)
89
89
  end
90
90
 
91
91
  def add_raw_action(name, *args, &block)
@@ -93,7 +93,7 @@ module GraphqlRails
93
93
  end
94
94
 
95
95
  def build_route(route_builder, name, **options)
96
- route_builder.new(name, full_route_options(options))
96
+ route_builder.new(name, **full_route_options(options))
97
97
  end
98
98
 
99
99
  def full_route_options(extra_options)
@@ -20,12 +20,12 @@ module GraphqlRails
20
20
  @routes ||= initial_routes
21
21
  end
22
22
 
23
- def query(*args)
24
- routes << build_query(*args)
23
+ def query(*args, **kwargs)
24
+ routes << build_query(*args, **kwargs)
25
25
  end
26
26
 
27
- def mutation(*args)
28
- routes << build_mutation(*args)
27
+ def mutation(*args, **kwargs)
28
+ routes << build_mutation(*args, **kwargs)
29
29
  end
30
30
 
31
31
  private
@@ -54,12 +54,12 @@ module GraphqlRails
54
54
  routes
55
55
  end
56
56
 
57
- def build_mutation(*args)
58
- build_route(MutationRoute, *args)
57
+ def build_mutation(*args, **kwargs)
58
+ build_route(MutationRoute, *args, **kwargs)
59
59
  end
60
60
 
61
- def build_query(*args)
62
- build_route(QueryRoute, *args)
61
+ def build_query(*args, **kwargs)
62
+ build_route(QueryRoute, *args, **kwargs)
63
63
  end
64
64
 
65
65
  # rubocop:disable Metrics/ParameterLists
@@ -33,16 +33,12 @@ module GraphqlRails
33
33
  groups.include?(group_name&.to_sym)
34
34
  end
35
35
 
36
- def field_args
37
- options = {}
38
-
36
+ def field_options
39
37
  if function
40
- options[:function] = function
38
+ { function: function }
41
39
  else
42
- options[:resolver] = resolver
40
+ { resolver: resolver }
43
41
  end
44
-
45
- [name, options]
46
42
  end
47
43
 
48
44
  private
@@ -21,6 +21,10 @@ module GraphqlRails
21
21
  raw = raw_actions
22
22
 
23
23
  Class.new(GraphQL::Schema) do
24
+ connections.add(
25
+ GraphqlRails::Decorator::RelationDecorator,
26
+ GraphQL::Pagination::ActiveRecordRelationConnection
27
+ )
24
28
  cursor_encoder(Router::PlainCursorEncoder)
25
29
  raw.each { |action| send(action[:name], *action[:args], &action[:block]) }
26
30
 
@@ -46,7 +50,7 @@ module GraphqlRails
46
50
  graphql_name(type_name)
47
51
 
48
52
  group_routes.each do |route|
49
- field(*route.field_args)
53
+ field(*route.name, **route.field_options)
50
54
  end
51
55
 
52
56
  def self.inspect