graphql_rails 2.3.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +3 -2
- data/CHANGELOG.md +21 -0
- data/Gemfile.lock +76 -73
- data/docs/README.md +1 -1
- data/docs/components/controller.md +15 -1
- data/docs/components/model.md +130 -6
- data/docs/components/routes.md +20 -10
- data/graphql_rails.gemspec +1 -1
- data/lib/generators/graphql_rails/templates/graphql_controller.erb +1 -1
- data/lib/graphql_rails/attributes/attributable.rb +6 -0
- data/lib/graphql_rails/attributes/attribute.rb +22 -9
- data/lib/graphql_rails/attributes/attribute_configurable.rb +27 -0
- data/lib/graphql_rails/attributes/attribute_name_parser.rb +4 -4
- data/lib/graphql_rails/attributes/input_attribute.rb +7 -1
- data/lib/graphql_rails/attributes/type_parseable.rb +6 -7
- data/lib/graphql_rails/controller/action.rb +4 -4
- data/lib/graphql_rails/controller/action_configuration.rb +1 -1
- data/lib/graphql_rails/controller/build_controller_action_resolver.rb +4 -15
- data/lib/graphql_rails/controller/configuration.rb +7 -2
- data/lib/graphql_rails/controller/handle_controller_error.rb +65 -0
- data/lib/graphql_rails/controller/log_controller_action.rb +1 -0
- data/lib/graphql_rails/controller/request/format_errors.rb +1 -1
- data/lib/graphql_rails/controller/request.rb +3 -2
- data/lib/graphql_rails/controller.rb +9 -6
- data/lib/graphql_rails/decorator/relation_decorator.rb +2 -2
- data/lib/graphql_rails/errors/custom_execution_error.rb +1 -1
- data/lib/graphql_rails/errors/execution_error.rb +1 -1
- data/lib/graphql_rails/model/configuration.rb +15 -2
- data/lib/graphql_rails/model/find_or_build_graphql_input_type.rb +36 -0
- data/lib/graphql_rails/model/find_or_build_graphql_type.rb +47 -13
- data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +7 -3
- data/lib/graphql_rails/model/input.rb +3 -3
- data/lib/graphql_rails/router/event_route.rb +52 -0
- data/lib/graphql_rails/router/mutation_route.rb +1 -1
- data/lib/graphql_rails/router/query_route.rb +1 -1
- data/lib/graphql_rails/router/resource_routes_builder.rb +0 -8
- data/lib/graphql_rails/router/route.rb +12 -2
- data/lib/graphql_rails/router/schema_builder.rb +15 -10
- data/lib/graphql_rails/router.rb +8 -8
- data/lib/graphql_rails/rspec_controller_helpers.rb +17 -3
- data/lib/graphql_rails/types/hidable_by_group.rb +23 -3
- data/lib/graphql_rails/types/input_object_type.rb +16 -0
- data/lib/graphql_rails/version.rb +1 -1
- metadata +10 -14
- data/lib/graphql_rails/model/build_graphql_input_type.rb +0 -43
- data/lib/graphql_rails/router/subscription_route.rb +0 -22
@@ -21,18 +21,12 @@ module GraphqlRails
|
|
21
21
|
@attributes ||= {}
|
22
22
|
end
|
23
23
|
|
24
|
-
def property(new_value = NOT_SET)
|
25
|
-
return @property if new_value == NOT_SET
|
26
|
-
|
27
|
-
@property = new_value.to_s
|
28
|
-
self
|
29
|
-
end
|
30
|
-
|
31
24
|
def field_args
|
32
25
|
[
|
33
26
|
field_name,
|
34
27
|
type_parser.type_arg,
|
35
|
-
description
|
28
|
+
description,
|
29
|
+
*field_args_options
|
36
30
|
].compact
|
37
31
|
end
|
38
32
|
|
@@ -42,7 +36,9 @@ module GraphqlRails
|
|
42
36
|
null: optional?,
|
43
37
|
camelize: camelize?,
|
44
38
|
groups: groups,
|
45
|
-
|
39
|
+
hidden_in_groups: hidden_in_groups,
|
40
|
+
**deprecation_reason_params,
|
41
|
+
**extras_options
|
46
42
|
}
|
47
43
|
end
|
48
44
|
|
@@ -52,6 +48,12 @@ module GraphqlRails
|
|
52
48
|
|
53
49
|
private
|
54
50
|
|
51
|
+
def extras_options
|
52
|
+
return {} if extras.empty?
|
53
|
+
|
54
|
+
{ extras: extras }
|
55
|
+
end
|
56
|
+
|
55
57
|
def camelize?
|
56
58
|
options[:input_format] != :original && options[:attribute_name_format] != :original
|
57
59
|
end
|
@@ -59,6 +61,17 @@ module GraphqlRails
|
|
59
61
|
def deprecation_reason_params
|
60
62
|
{ deprecation_reason: deprecation_reason }.compact
|
61
63
|
end
|
64
|
+
|
65
|
+
def field_args_options
|
66
|
+
options = { **field_args_pagination_options }
|
67
|
+
return nil if options.empty?
|
68
|
+
|
69
|
+
[options]
|
70
|
+
end
|
71
|
+
|
72
|
+
def field_args_pagination_options
|
73
|
+
pagination_options || {}
|
74
|
+
end
|
62
75
|
end
|
63
76
|
end
|
64
77
|
end
|
@@ -17,6 +17,7 @@ module GraphqlRails
|
|
17
17
|
|
18
18
|
chainable_option :description
|
19
19
|
chainable_option :options, default: {}
|
20
|
+
chainable_option :extras, default: []
|
20
21
|
chainable_option :type
|
21
22
|
end
|
22
23
|
|
@@ -32,6 +33,14 @@ module GraphqlRails
|
|
32
33
|
groups(*args)
|
33
34
|
end
|
34
35
|
|
36
|
+
def hidden_in_groups(new_groups = ChainableOptions::NOT_SET)
|
37
|
+
@hidden_in_groups ||= []
|
38
|
+
return @hidden_in_groups if new_groups == ChainableOptions::NOT_SET
|
39
|
+
|
40
|
+
@hidden_in_groups = Array(new_groups).map(&:to_s)
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
35
44
|
def required(new_value = true) # rubocop:disable Style/OptionalBooleanParameter
|
36
45
|
@required = new_value
|
37
46
|
self
|
@@ -55,6 +64,24 @@ module GraphqlRails
|
|
55
64
|
def deprecation_reason
|
56
65
|
@deprecation_reason
|
57
66
|
end
|
67
|
+
|
68
|
+
def property(new_value = ChainableOptions::NOT_SET)
|
69
|
+
return @property if new_value == ChainableOptions::NOT_SET
|
70
|
+
|
71
|
+
@property = new_value.to_s
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def same_as(other_attribute)
|
76
|
+
other = other_attribute.dup
|
77
|
+
other.instance_variables.each do |instance_variable|
|
78
|
+
next if instance_variable == :@initial_name
|
79
|
+
|
80
|
+
instance_variable_set(instance_variable, other.instance_variable_get(instance_variable))
|
81
|
+
end
|
82
|
+
|
83
|
+
self
|
84
|
+
end
|
58
85
|
end
|
59
86
|
end
|
60
87
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module GraphqlRails
|
4
4
|
module Attributes
|
5
5
|
# Parses attribute name and can generates graphql scalar type,
|
6
|
-
#
|
6
|
+
# graphql name and etc. based on that
|
7
7
|
class AttributeNameParser
|
8
8
|
def initialize(original_name, options: {})
|
9
9
|
@original_name = original_name.to_s
|
@@ -13,9 +13,9 @@ module GraphqlRails
|
|
13
13
|
def field_name
|
14
14
|
@field_name ||= \
|
15
15
|
if original_format?
|
16
|
-
|
16
|
+
preprocessed_name
|
17
17
|
else
|
18
|
-
|
18
|
+
preprocessed_name.camelize(:lower)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -47,7 +47,7 @@ module GraphqlRails
|
|
47
47
|
options[:input_format] == :original || options[:attribute_name_format] == :original
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
50
|
+
def preprocessed_name
|
51
51
|
if name.end_with?('?')
|
52
52
|
"is_#{name.remove(/\?\Z/)}"
|
53
53
|
else
|
@@ -31,7 +31,9 @@ module GraphqlRails
|
|
31
31
|
description: description,
|
32
32
|
camelize: false,
|
33
33
|
groups: groups,
|
34
|
+
hidden_in_groups: hidden_in_groups,
|
34
35
|
**default_value_option,
|
36
|
+
**property_params,
|
35
37
|
**deprecation_reason_params
|
36
38
|
}
|
37
39
|
end
|
@@ -58,6 +60,10 @@ module GraphqlRails
|
|
58
60
|
{ deprecation_reason: deprecation_reason }.compact
|
59
61
|
end
|
60
62
|
|
63
|
+
def property_params
|
64
|
+
{ as: property }.compact
|
65
|
+
end
|
66
|
+
|
61
67
|
def attribute_naming_options
|
62
68
|
options.slice(:input_format)
|
63
69
|
end
|
@@ -79,7 +85,7 @@ module GraphqlRails
|
|
79
85
|
end
|
80
86
|
|
81
87
|
def raw_input_type
|
82
|
-
return type if type.is_a?(GraphQL::
|
88
|
+
return type if type.is_a?(GraphQL::Schema::InputObject)
|
83
89
|
return type.graphql_input_type if type.is_a?(Model::Input)
|
84
90
|
end
|
85
91
|
end
|
@@ -42,14 +42,13 @@ module GraphqlRails
|
|
42
42
|
WRAPPER_TYPES = [
|
43
43
|
GraphQL::Schema::List,
|
44
44
|
GraphQL::Schema::NonNull,
|
45
|
-
GraphQL::NonNullType,
|
46
|
-
GraphQL::ListType
|
45
|
+
GraphQL::Language::Nodes::NonNullType,
|
46
|
+
GraphQL::Language::Nodes::ListType
|
47
47
|
].freeze
|
48
48
|
|
49
49
|
GRAPHQL_BASE_TYPES = [
|
50
|
-
GraphQL::
|
51
|
-
GraphQL::
|
52
|
-
GraphQL::InputObjectType
|
50
|
+
GraphQL::Schema::Object,
|
51
|
+
GraphQL::Schema::InputObject
|
53
52
|
].freeze
|
54
53
|
|
55
54
|
RAW_GRAPHQL_TYPES = (WRAPPER_TYPES + GRAPHQL_BASE_TYPES).freeze
|
@@ -95,9 +94,9 @@ module GraphqlRails
|
|
95
94
|
end
|
96
95
|
|
97
96
|
def graphql_type_object?(type_class)
|
98
|
-
return false
|
97
|
+
return false if !type_class.is_a?(Class) && !type_class.is_a?(Module)
|
99
98
|
|
100
|
-
type_class < GraphQL::Schema::Member
|
99
|
+
type_class < GraphQL::Schema::Member::GraphQLTypeNames
|
101
100
|
end
|
102
101
|
|
103
102
|
def applicable_graphql_type?(type)
|
@@ -43,16 +43,16 @@ module GraphqlRails
|
|
43
43
|
{ null: !type_parser.required? }
|
44
44
|
end
|
45
45
|
|
46
|
+
def action_config
|
47
|
+
controller.controller_configuration.action_config(name)
|
48
|
+
end
|
49
|
+
|
46
50
|
private
|
47
51
|
|
48
52
|
attr_reader :route
|
49
53
|
|
50
54
|
delegate :type_parser, to: :action_config
|
51
55
|
|
52
|
-
def action_config
|
53
|
-
controller.controller_configuration.action_config(name)
|
54
|
-
end
|
55
|
-
|
56
56
|
def namespaced_controller_name
|
57
57
|
[route.module_name, controller_name].reject(&:empty?).join('/')
|
58
58
|
end
|
@@ -7,7 +7,7 @@ require 'graphql_rails/errors/error'
|
|
7
7
|
|
8
8
|
module GraphqlRails
|
9
9
|
class Controller
|
10
|
-
# stores all graphql_rails
|
10
|
+
# stores all graphql_rails controller specific config
|
11
11
|
class ActionConfiguration
|
12
12
|
class MissingConfigurationError < GraphqlRails::Error; end
|
13
13
|
class DeprecatedDefaultModelError < GraphqlRails::Error; end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'graphql_rails/controller/action'
|
4
3
|
require 'graphql_rails/concerns/service'
|
5
4
|
require 'graphql_rails/controller/action_configuration'
|
6
5
|
require 'graphql_rails/controller/build_controller_action_resolver/controller_action_resolver'
|
@@ -11,12 +10,12 @@ module GraphqlRails
|
|
11
10
|
class BuildControllerActionResolver
|
12
11
|
include ::GraphqlRails::Service
|
13
12
|
|
14
|
-
def initialize(
|
15
|
-
@
|
13
|
+
def initialize(action:)
|
14
|
+
@action = action
|
16
15
|
end
|
17
16
|
|
18
17
|
def call # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
19
|
-
action =
|
18
|
+
action = self.action
|
20
19
|
|
21
20
|
Class.new(ControllerActionResolver) do
|
22
21
|
graphql_name("ControllerActionResolver#{SecureRandom.hex}")
|
@@ -38,17 +37,7 @@ module GraphqlRails
|
|
38
37
|
|
39
38
|
private
|
40
39
|
|
41
|
-
attr_reader :
|
42
|
-
|
43
|
-
def build_action
|
44
|
-
Action.new(route).tap do |action|
|
45
|
-
assert_action(action)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def assert_action(action)
|
50
|
-
action.return_type
|
51
|
-
end
|
40
|
+
attr_reader :action
|
52
41
|
end
|
53
42
|
end
|
54
43
|
end
|
@@ -7,13 +7,13 @@ require 'graphql_rails/errors/error'
|
|
7
7
|
|
8
8
|
module GraphqlRails
|
9
9
|
class Controller
|
10
|
-
# stores all graphql_rails
|
10
|
+
# stores all graphql_rails controller specific config
|
11
11
|
class Configuration
|
12
12
|
class InvalidActionConfiguration < GraphqlRails::Error; end
|
13
13
|
|
14
14
|
LIB_REGEXP = %r{/graphql_rails/lib/}
|
15
15
|
|
16
|
-
attr_reader :action_by_name
|
16
|
+
attr_reader :action_by_name, :error_handlers
|
17
17
|
|
18
18
|
def initialize(controller)
|
19
19
|
@controller = controller
|
@@ -25,6 +25,7 @@ module GraphqlRails
|
|
25
25
|
|
26
26
|
@action_by_name = {}
|
27
27
|
@action_default = nil
|
28
|
+
@error_handlers = {}
|
28
29
|
end
|
29
30
|
|
30
31
|
def initialize_copy(other)
|
@@ -56,6 +57,10 @@ module GraphqlRails
|
|
56
57
|
ActionHook.new(name: hook_name, **options, &block)
|
57
58
|
end
|
58
59
|
|
60
|
+
def add_error_handler(error, with:, &block)
|
61
|
+
@error_handlers[error] = with || block
|
62
|
+
end
|
63
|
+
|
59
64
|
def action_default
|
60
65
|
@action_default ||= ActionConfiguration.new(name: :default, controller: nil)
|
61
66
|
yield(@action_default) if block_given?
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
class Controller
|
5
|
+
# runs {before/around/after}_action controller hooks
|
6
|
+
class HandleControllerError
|
7
|
+
def initialize(error:, controller:)
|
8
|
+
@error = error
|
9
|
+
@controller = controller
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
return custom_handle_error if custom_handle_error?
|
14
|
+
|
15
|
+
render_unhandled_error(error)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :error, :controller
|
21
|
+
|
22
|
+
def render_unhandled_error(error)
|
23
|
+
return render(error: error) if error.is_a?(GraphQL::ExecutionError)
|
24
|
+
|
25
|
+
render(error: SystemError.new(error))
|
26
|
+
end
|
27
|
+
|
28
|
+
def custom_handle_error
|
29
|
+
return unless custom_handler
|
30
|
+
|
31
|
+
begin
|
32
|
+
if custom_handler.is_a?(Proc)
|
33
|
+
controller.instance_exec(error, &custom_handler)
|
34
|
+
else
|
35
|
+
controller.send(custom_handler)
|
36
|
+
end
|
37
|
+
rescue StandardError => e
|
38
|
+
render_unhandled_error(e)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def custom_handler
|
43
|
+
return @custom_handler if defined?(@custom_handler)
|
44
|
+
|
45
|
+
handler = controller_config.error_handlers.detect do |error_class, _handler|
|
46
|
+
error.class <= error_class
|
47
|
+
end
|
48
|
+
|
49
|
+
@custom_handler = handler&.last
|
50
|
+
end
|
51
|
+
|
52
|
+
def custom_handle_error?
|
53
|
+
custom_handler.present?
|
54
|
+
end
|
55
|
+
|
56
|
+
def controller_config
|
57
|
+
@controller_config ||= controller.class.controller_configuration
|
58
|
+
end
|
59
|
+
|
60
|
+
def render(*args, **kwargs, &block)
|
61
|
+
controller.send(:render, *args, **kwargs, &block)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -8,7 +8,7 @@ require 'graphql_rails/errors/custom_execution_error'
|
|
8
8
|
module GraphqlRails
|
9
9
|
class Controller
|
10
10
|
class Request
|
11
|
-
# Converts user provided free-form errors in to
|
11
|
+
# Converts user provided free-form errors in to meaningful graphql error classes
|
12
12
|
class FormatErrors
|
13
13
|
include Service
|
14
14
|
|
@@ -7,11 +7,12 @@ module GraphqlRails
|
|
7
7
|
require 'graphql_rails/controller/request/format_errors'
|
8
8
|
|
9
9
|
attr_accessor :object_to_return
|
10
|
-
attr_reader :errors, :context
|
10
|
+
attr_reader :errors, :context, :lookahead
|
11
11
|
|
12
12
|
def initialize(graphql_object, inputs, context)
|
13
13
|
@graphql_object = graphql_object
|
14
|
-
@inputs = inputs
|
14
|
+
@inputs = inputs.except(:lookahead)
|
15
|
+
@lookahead = inputs[:lookahead]
|
15
16
|
@context = context
|
16
17
|
end
|
17
18
|
|
@@ -6,6 +6,7 @@ require 'graphql_rails/controller/configuration'
|
|
6
6
|
require 'graphql_rails/controller/request'
|
7
7
|
require 'graphql_rails/controller/action_hooks_runner'
|
8
8
|
require 'graphql_rails/controller/log_controller_action'
|
9
|
+
require 'graphql_rails/controller/handle_controller_error'
|
9
10
|
require 'graphql_rails/errors/system_error'
|
10
11
|
|
11
12
|
module GraphqlRails
|
@@ -45,6 +46,12 @@ module GraphqlRails
|
|
45
46
|
def controller_configuration
|
46
47
|
@controller_configuration ||= Controller::Configuration.new(self)
|
47
48
|
end
|
49
|
+
|
50
|
+
def rescue_from(*errors, with: nil, &block)
|
51
|
+
Array(errors).each do |error|
|
52
|
+
controller_configuration.add_error_handler(error, with: with, &block)
|
53
|
+
end
|
54
|
+
end
|
48
55
|
end
|
49
56
|
|
50
57
|
attr_reader :action_name
|
@@ -86,12 +93,8 @@ module GraphqlRails
|
|
86
93
|
response = hooks_runner.call { public_send(action_name) }
|
87
94
|
|
88
95
|
render response if graphql_request.no_object_to_return?
|
89
|
-
rescue StandardError =>
|
90
|
-
|
91
|
-
render error: error
|
92
|
-
else
|
93
|
-
render error: SystemError.new(error)
|
94
|
-
end
|
96
|
+
rescue StandardError => e
|
97
|
+
HandleControllerError.new(error: e, controller: self).call
|
95
98
|
end
|
96
99
|
|
97
100
|
def graphql_errors_from_render_params(rendering_params)
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module GraphqlRails
|
4
4
|
module Decorator
|
5
|
-
#
|
5
|
+
# wraps active record relation and returns decorated object instead
|
6
6
|
class RelationDecorator
|
7
7
|
delegate :map, :each, to: :to_a
|
8
|
-
delegate :limit_value, :offset_value, :count, :size, :empty?, to: :relation
|
8
|
+
delegate :limit_value, :offset_value, :count, :size, :empty?, :loaded?, to: :relation
|
9
9
|
|
10
10
|
def self.decorates?(object)
|
11
11
|
(defined?(ActiveRecord) && object.is_a?(ActiveRecord::Relation)) ||
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GraphqlRails
|
4
|
-
# base class which is returned in case something bad happens. Contains all error rendering
|
4
|
+
# base class which is returned in case something bad happens. Contains all error rendering structure
|
5
5
|
class CustomExecutionError < ExecutionError
|
6
6
|
attr_reader :extra_graphql_data
|
7
7
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module GraphqlRails
|
4
4
|
require 'graphql'
|
5
5
|
|
6
|
-
# base class which is returned in case something bad happens. Contains all error rendering
|
6
|
+
# base class which is returned in case something bad happens. Contains all error rendering structure
|
7
7
|
class ExecutionError < GraphQL::ExecutionError
|
8
8
|
def to_h
|
9
9
|
super.merge(extra_graphql_data)
|
@@ -24,10 +24,21 @@ module GraphqlRails
|
|
24
24
|
@input = other.instance_variable_get(:@input)&.transform_values(&:dup)
|
25
25
|
end
|
26
26
|
|
27
|
+
def implements(*interfaces)
|
28
|
+
previous_implements = get_or_set_chainable_option(:implements) || []
|
29
|
+
return previous_implements if interfaces.blank?
|
30
|
+
|
31
|
+
full_implements = (previous_implements + interfaces).uniq
|
32
|
+
|
33
|
+
get_or_set_chainable_option(:implements, full_implements) || []
|
34
|
+
end
|
35
|
+
|
27
36
|
def attribute(attribute_name, **attribute_options)
|
28
37
|
key = attribute_name.to_s
|
29
38
|
|
30
|
-
attributes[key] ||= build_attribute(attribute_name)
|
39
|
+
attributes[key] ||= build_attribute(attribute_name)
|
40
|
+
|
41
|
+
attributes[key].tap do |new_attribute|
|
31
42
|
new_attribute.with(**attribute_options)
|
32
43
|
yield(new_attribute) if block_given?
|
33
44
|
end
|
@@ -52,7 +63,8 @@ module GraphqlRails
|
|
52
63
|
name: name,
|
53
64
|
description: description,
|
54
65
|
attributes: attributes,
|
55
|
-
type_name: type_name
|
66
|
+
type_name: type_name,
|
67
|
+
implements: implements
|
56
68
|
)
|
57
69
|
end
|
58
70
|
|
@@ -86,6 +98,7 @@ module GraphqlRails
|
|
86
98
|
description: description,
|
87
99
|
attributes: attributes,
|
88
100
|
type_name: type_name,
|
101
|
+
implements: implements,
|
89
102
|
force_define_attributes: true
|
90
103
|
)
|
91
104
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql_rails/types/input_object_type'
|
4
|
+
require 'graphql_rails/concerns/service'
|
5
|
+
require 'graphql_rails/model/find_or_build_graphql_type'
|
6
|
+
|
7
|
+
module GraphqlRails
|
8
|
+
module Model
|
9
|
+
# stores information about model specific config, like attributes and types
|
10
|
+
class FindOrBuildGraphqlInputType < FindOrBuildGraphqlType
|
11
|
+
include ::GraphqlRails::Service
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def parent_class
|
16
|
+
GraphqlRails::Types::InputObjectType
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_attributes_batch(attributes)
|
20
|
+
klass.class_eval do
|
21
|
+
attributes.each do |attribute|
|
22
|
+
argument(*attribute.input_argument_args, **attribute.input_argument_options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_or_build_dynamic_type(attribute)
|
28
|
+
graphql_model = attribute.graphql_model
|
29
|
+
return unless graphql_model
|
30
|
+
|
31
|
+
graphql = graphql_model.graphql.input(attribute.subtype)
|
32
|
+
find_or_build_graphql_model_type(graphql)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -10,51 +10,85 @@ module GraphqlRails
|
|
10
10
|
|
11
11
|
include ::GraphqlRails::Service
|
12
12
|
|
13
|
-
|
13
|
+
# rubocop:disable Metrics/ParameterLists
|
14
|
+
def initialize(
|
15
|
+
name:,
|
16
|
+
description:,
|
17
|
+
attributes:,
|
18
|
+
type_name:,
|
19
|
+
force_define_attributes: false,
|
20
|
+
implements: []
|
21
|
+
)
|
14
22
|
@name = name
|
15
23
|
@description = description
|
16
24
|
@attributes = attributes
|
17
25
|
@type_name = type_name
|
18
26
|
@force_define_attributes = force_define_attributes
|
27
|
+
@implements = implements
|
19
28
|
end
|
29
|
+
# rubocop:enable Metrics/ParameterLists
|
20
30
|
|
21
31
|
def call
|
22
|
-
klass.tap
|
32
|
+
klass.tap do
|
33
|
+
add_attributes if new_class? || force_define_attributes
|
34
|
+
add_interfaces
|
35
|
+
end
|
23
36
|
end
|
24
37
|
|
25
38
|
private
|
26
39
|
|
27
|
-
attr_reader :name, :description, :attributes, :type_name, :force_define_attributes
|
40
|
+
attr_reader :name, :description, :attributes, :type_name, :force_define_attributes,
|
41
|
+
:implements
|
28
42
|
|
29
43
|
delegate :klass, :new_class?, to: :type_class_finder
|
30
44
|
|
45
|
+
def parent_class
|
46
|
+
GraphqlRails::Types::ObjectType
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_attributes_batch(attributes)
|
50
|
+
AddFieldsToGraphqlType.call(klass: klass, attributes: attributes)
|
51
|
+
end
|
52
|
+
|
31
53
|
def type_class_finder
|
32
54
|
@type_class_finder ||= FindOrBuildGraphqlTypeClass.new(
|
33
55
|
name: name,
|
34
56
|
type_name: type_name,
|
35
|
-
description: description
|
57
|
+
description: description,
|
58
|
+
implements: implements,
|
59
|
+
parent_class: parent_class
|
36
60
|
)
|
37
61
|
end
|
38
62
|
|
39
|
-
def
|
63
|
+
def add_attributes
|
40
64
|
scalar_attributes, dynamic_attributes = attributes.values.partition(&:scalar_type?)
|
41
65
|
|
42
|
-
|
66
|
+
add_attributes_batch(scalar_attributes)
|
43
67
|
dynamic_attributes.each { |attribute| find_or_build_dynamic_type(attribute) }
|
44
|
-
|
68
|
+
add_attributes_batch(dynamic_attributes)
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_interfaces
|
72
|
+
implements.each do |interface|
|
73
|
+
next if klass.interfaces.include?(interface)
|
74
|
+
|
75
|
+
klass.implements(interface)
|
76
|
+
end
|
45
77
|
end
|
46
78
|
|
47
79
|
def find_or_build_dynamic_type(attribute)
|
48
80
|
graphql_model = attribute.graphql_model
|
49
|
-
|
81
|
+
return unless graphql_model
|
82
|
+
|
83
|
+
find_or_build_graphql_model_type(graphql_model.graphql)
|
50
84
|
end
|
51
85
|
|
52
|
-
def find_or_build_graphql_model_type(
|
86
|
+
def find_or_build_graphql_model_type(graphql_config)
|
53
87
|
self.class.call(
|
54
|
-
name:
|
55
|
-
description:
|
56
|
-
attributes:
|
57
|
-
type_name:
|
88
|
+
name: graphql_config.name,
|
89
|
+
description: graphql_config.description,
|
90
|
+
attributes: graphql_config.attributes,
|
91
|
+
type_name: graphql_config.type_name
|
58
92
|
)
|
59
93
|
end
|
60
94
|
end
|