graphql_rails 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +89 -2
- data/docs/README.md +16 -0
- data/docs/_sidebar.md +2 -0
- data/docs/components/controller.md +113 -0
- data/docs/components/decorator.md +69 -0
- data/docs/components/model.md +75 -2
- data/docs/logging_and_monitoring/logging_and_monitoring.md +35 -0
- data/graphql_rails.gemspec +1 -0
- data/lib/graphql_rails/attributes/attributable.rb +5 -1
- data/lib/graphql_rails/attributes/attribute.rb +31 -4
- data/lib/graphql_rails/attributes/input_attribute.rb +14 -10
- data/lib/graphql_rails/attributes/input_type_parser.rb +84 -0
- data/lib/graphql_rails/attributes/type_name_info.rb +38 -0
- data/lib/graphql_rails/attributes/type_parser.rb +30 -28
- data/lib/graphql_rails/controller/action_configuration.rb +13 -7
- data/lib/graphql_rails/controller/configuration.rb +2 -0
- data/lib/graphql_rails/controller/log_controller_action.rb +66 -0
- data/lib/graphql_rails/controller.rb +26 -9
- data/lib/graphql_rails/decorator/relation_decorator.rb +79 -0
- data/lib/graphql_rails/decorator.rb +41 -0
- data/lib/graphql_rails/integrations/lograge.rb +39 -0
- data/lib/graphql_rails/integrations/sentry.rb +34 -0
- data/lib/graphql_rails/integrations.rb +19 -0
- data/lib/graphql_rails/model/configuration.rb +20 -7
- data/lib/graphql_rails/model/input.rb +1 -1
- data/lib/graphql_rails/model.rb +7 -0
- data/lib/graphql_rails/version.rb +1 -1
- data/lib/graphql_rails.rb +4 -0
- metadata +26 -2
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql'
|
4
|
+
|
5
|
+
module GraphqlRails
|
6
|
+
module Attributes
|
7
|
+
# converts string value in to GraphQL type
|
8
|
+
class InputTypeParser < TypeParser
|
9
|
+
def initialize(unparsed_type, subtype:)
|
10
|
+
super(unparsed_type)
|
11
|
+
@subtype = subtype
|
12
|
+
end
|
13
|
+
|
14
|
+
def graphql_type
|
15
|
+
return nil if unparsed_type.nil?
|
16
|
+
|
17
|
+
partly_parsed_type || parsed_type
|
18
|
+
end
|
19
|
+
|
20
|
+
def nullable_type
|
21
|
+
return nil if unparsed_type.nil?
|
22
|
+
|
23
|
+
partly_parsed_type || parsed_nullable_type
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def partly_parsed_type
|
29
|
+
return unparsed_type if raw_graphql_type?
|
30
|
+
return unparsed_type.graphql_input_type if unparsed_type.is_a?(GraphqlRails::Model::Input)
|
31
|
+
end
|
32
|
+
|
33
|
+
def parsed_nullable_type
|
34
|
+
if list?
|
35
|
+
parsed_inner_type.to_list_type
|
36
|
+
else
|
37
|
+
type_by_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parsed_type
|
42
|
+
if list?
|
43
|
+
parsed_list_type
|
44
|
+
else
|
45
|
+
parsed_inner_type
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def raw_graphql_type?
|
50
|
+
unparsed_type.is_a?(GraphQL::InputObjectType) || super
|
51
|
+
end
|
52
|
+
|
53
|
+
def dynamicly_defined_type
|
54
|
+
type_class = graphql_model
|
55
|
+
return unless type_class
|
56
|
+
|
57
|
+
type_class.graphql.input(*subtype).graphql_input_type
|
58
|
+
end
|
59
|
+
|
60
|
+
def parsed_list_type
|
61
|
+
list_type = parsed_inner_type.to_list_type
|
62
|
+
|
63
|
+
if required_list?
|
64
|
+
list_type = list_type.to_graphql if list_type.respond_to?(:to_graphql)
|
65
|
+
list_type.to_non_null_type
|
66
|
+
else
|
67
|
+
list_type
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def parsed_inner_type
|
72
|
+
if required_inner_type?
|
73
|
+
type_by_name.to_non_null_type
|
74
|
+
else
|
75
|
+
type_by_name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
attr_reader :subtype
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Attributes
|
5
|
+
# checks various attributes based on graphql type name
|
6
|
+
class TypeNameInfo
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
end
|
12
|
+
|
13
|
+
def nullable_inner_name
|
14
|
+
inner_name[/[^!]+/]
|
15
|
+
end
|
16
|
+
|
17
|
+
def inner_name
|
18
|
+
name[/[^!\[\]]+!?/]
|
19
|
+
end
|
20
|
+
|
21
|
+
def required_inner_type?
|
22
|
+
inner_name.include?('!')
|
23
|
+
end
|
24
|
+
|
25
|
+
def list?
|
26
|
+
name.include?(']')
|
27
|
+
end
|
28
|
+
|
29
|
+
def required?
|
30
|
+
name.end_with?('!')
|
31
|
+
end
|
32
|
+
|
33
|
+
def required_list?
|
34
|
+
required? && list?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -6,6 +6,8 @@ module GraphqlRails
|
|
6
6
|
module Attributes
|
7
7
|
# converts string value in to GraphQL type
|
8
8
|
class TypeParser
|
9
|
+
require_relative './type_name_info'
|
10
|
+
|
9
11
|
class UnknownTypeError < ArgumentError; end
|
10
12
|
|
11
13
|
TYPE_MAPPING = {
|
@@ -28,6 +30,13 @@ module GraphqlRails
|
|
28
30
|
'decimal' => GraphQL::FLOAT_TYPE
|
29
31
|
}.freeze
|
30
32
|
|
33
|
+
RAW_GRAPHQL_TYPES = [
|
34
|
+
GraphQL::Schema::List,
|
35
|
+
GraphQL::BaseType,
|
36
|
+
GraphQL::ObjectType,
|
37
|
+
GraphQL::InputObjectType
|
38
|
+
].freeze
|
39
|
+
|
31
40
|
def initialize(unparsed_type)
|
32
41
|
@unparsed_type = unparsed_type
|
33
42
|
end
|
@@ -43,20 +52,31 @@ module GraphqlRails
|
|
43
52
|
end
|
44
53
|
|
45
54
|
def graphql_model
|
46
|
-
type_class =
|
55
|
+
type_class = nullable_inner_name.safe_constantize
|
47
56
|
return unless type_class.respond_to?(:graphql)
|
48
57
|
|
49
58
|
type_class
|
50
59
|
end
|
51
60
|
|
61
|
+
protected
|
62
|
+
|
63
|
+
def dynamicly_defined_type
|
64
|
+
type_class = graphql_model
|
65
|
+
return unless type_class
|
66
|
+
|
67
|
+
type_class.graphql.graphql_type
|
68
|
+
end
|
69
|
+
|
52
70
|
private
|
53
71
|
|
72
|
+
delegate :list?, :required_inner_type?, :required_list?, :nullable_inner_name, to: :type_name_info
|
73
|
+
|
54
74
|
attr_reader :unparsed_type
|
55
75
|
|
56
76
|
def parsed_list_type
|
57
77
|
list_type = parsed_inner_type.to_list_type
|
58
78
|
|
59
|
-
if
|
79
|
+
if required_list?
|
60
80
|
list_type.to_non_null_type
|
61
81
|
else
|
62
82
|
list_type
|
@@ -71,31 +91,20 @@ module GraphqlRails
|
|
71
91
|
end
|
72
92
|
end
|
73
93
|
|
74
|
-
def required_inner_type?
|
75
|
-
!!unparsed_type[/\w!/] # rubocop:disable Style/DoubleNegation
|
76
|
-
end
|
77
|
-
|
78
|
-
def list?
|
79
|
-
unparsed_type.to_s.include?(']')
|
80
|
-
end
|
81
|
-
|
82
|
-
def required_list_type?
|
83
|
-
unparsed_type.to_s.include?(']!')
|
84
|
-
end
|
85
|
-
|
86
94
|
def raw_graphql_type?
|
87
|
-
unparsed_type.is_a?(
|
88
|
-
|
89
|
-
|
90
|
-
|
95
|
+
return true if RAW_GRAPHQL_TYPES.detect { |raw_type| unparsed_type.is_a?(raw_type) }
|
96
|
+
|
97
|
+
defined?(GraphQL::Schema::Member) &&
|
98
|
+
unparsed_type.is_a?(Class) &&
|
99
|
+
unparsed_type < GraphQL::Schema::Member
|
91
100
|
end
|
92
101
|
|
93
|
-
def
|
94
|
-
unparsed_type.to_s
|
102
|
+
def type_name_info
|
103
|
+
@type_name_info ||= TypeNameInfo.new(unparsed_type.to_s)
|
95
104
|
end
|
96
105
|
|
97
106
|
def type_by_name
|
98
|
-
TYPE_MAPPING.fetch(
|
107
|
+
TYPE_MAPPING.fetch(nullable_inner_name.downcase) do
|
99
108
|
dynamicly_defined_type || raise(
|
100
109
|
UnknownTypeError,
|
101
110
|
"Type #{unparsed_type.inspect} is not supported. Supported scalar types are: #{TYPE_MAPPING.keys}." \
|
@@ -103,13 +112,6 @@ module GraphqlRails
|
|
103
112
|
)
|
104
113
|
end
|
105
114
|
end
|
106
|
-
|
107
|
-
def dynamicly_defined_type
|
108
|
-
type_class = graphql_model
|
109
|
-
return unless type_class
|
110
|
-
|
111
|
-
type_class.graphql.graphql_type
|
112
|
-
end
|
113
115
|
end
|
114
116
|
end
|
115
117
|
end
|
@@ -26,8 +26,19 @@ module GraphqlRails
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def permit(*no_type_attributes, **typed_attributes)
|
29
|
-
no_type_attributes.each { |attribute|
|
30
|
-
typed_attributes.each { |attribute, type|
|
29
|
+
no_type_attributes.each { |attribute| permit_input(attribute) }
|
30
|
+
typed_attributes.each { |attribute, type| permit_input(attribute, type: type) }
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def permit_input(name, type: nil, options: {}, **input_options)
|
35
|
+
field_name = name.to_s.remove(/!\Z/)
|
36
|
+
|
37
|
+
attributes[field_name] = Attributes::InputAttribute.new(
|
38
|
+
name.to_s, type,
|
39
|
+
options: action_options.merge(options),
|
40
|
+
**input_options
|
41
|
+
)
|
31
42
|
self
|
32
43
|
end
|
33
44
|
|
@@ -86,11 +97,6 @@ module GraphqlRails
|
|
86
97
|
def type_parser
|
87
98
|
Attributes::TypeParser.new(custom_return_type)
|
88
99
|
end
|
89
|
-
|
90
|
-
def permit_attribute(name, type = nil)
|
91
|
-
field_name = name.to_s.remove(/!\Z/)
|
92
|
-
attributes[field_name] = Attributes::InputAttribute.new(name.to_s, type, options: action_options)
|
93
|
-
end
|
94
100
|
end
|
95
101
|
end
|
96
102
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql_rails/errors/execution_error'
|
4
|
+
|
5
|
+
module GraphqlRails
|
6
|
+
class Controller
|
7
|
+
# logs controller start and end times
|
8
|
+
class LogControllerAction
|
9
|
+
START_PROCESSING_KEY = 'start_processing.graphql_action_controller'
|
10
|
+
PROCESS_ACTION_KEY = 'process_action.graphql_action_controller'
|
11
|
+
|
12
|
+
def self.call(**kwargs, &block)
|
13
|
+
new(**kwargs).call(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(controller_name:, action_name:, params:, graphql_request:)
|
17
|
+
@controller_name = controller_name
|
18
|
+
@action_name = action_name
|
19
|
+
@params = params
|
20
|
+
@graphql_request = graphql_request
|
21
|
+
end
|
22
|
+
|
23
|
+
def call
|
24
|
+
ActiveSupport::Notifications.instrument(START_PROCESSING_KEY, default_payload)
|
25
|
+
ActiveSupport::Notifications.instrument(PROCESS_ACTION_KEY, default_payload) do |payload|
|
26
|
+
yield.tap do
|
27
|
+
payload[:status] = status
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :controller_name, :action_name, :params, :graphql_request
|
35
|
+
|
36
|
+
def default_payload
|
37
|
+
{
|
38
|
+
controller: controller_name,
|
39
|
+
action: action_name,
|
40
|
+
params: filtered_params
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def status
|
45
|
+
graphql_request.errors.present? ? 500 : 200
|
46
|
+
end
|
47
|
+
|
48
|
+
def filtered_params
|
49
|
+
@filtered_params ||=
|
50
|
+
if filter_parameters.empty?
|
51
|
+
params
|
52
|
+
else
|
53
|
+
filter_options = Rails.configuration.filter_parameters
|
54
|
+
parametter_filter = ActionDispatch::Http::ParameterFilter.new(filter_options)
|
55
|
+
parametter_filter.filter(params)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def filter_parameters
|
60
|
+
return [] if !defined?(Rails) || Rails.application.nil?
|
61
|
+
|
62
|
+
Rails.application.config.filter_parameters || []
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -6,12 +6,14 @@ require 'graphql_rails/controller/configuration'
|
|
6
6
|
require 'graphql_rails/controller/request'
|
7
7
|
require 'graphql_rails/controller/format_results'
|
8
8
|
require 'graphql_rails/controller/action_hooks_runner'
|
9
|
+
require 'graphql_rails/controller/log_controller_action'
|
9
10
|
|
10
11
|
module GraphqlRails
|
11
12
|
# base class for all graphql_rails controllers
|
12
13
|
class Controller
|
13
14
|
class << self
|
14
15
|
def inherited(sublass)
|
16
|
+
super
|
15
17
|
sublass.instance_variable_set(:@controller_configuration, controller_configuration.dup)
|
16
18
|
end
|
17
19
|
|
@@ -44,14 +46,10 @@ module GraphqlRails
|
|
44
46
|
|
45
47
|
def call(method_name)
|
46
48
|
@action_name = method_name
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
action_config: self.class.action(method_name),
|
52
|
-
params: params,
|
53
|
-
graphql_context: graphql_request.context
|
54
|
-
).call
|
49
|
+
with_controller_action_logging do
|
50
|
+
call_with_rendering
|
51
|
+
format_controller_results
|
52
|
+
end
|
55
53
|
ensure
|
56
54
|
@action_name = nil
|
57
55
|
end
|
@@ -74,7 +72,7 @@ module GraphqlRails
|
|
74
72
|
|
75
73
|
private
|
76
74
|
|
77
|
-
def call_with_rendering
|
75
|
+
def call_with_rendering
|
78
76
|
hooks_runner = ActionHooksRunner.new(action_name: action_name, controller: self)
|
79
77
|
response = hooks_runner.call { public_send(action_name) }
|
80
78
|
|
@@ -90,5 +88,24 @@ module GraphqlRails
|
|
90
88
|
errors = rendering_params[:error] || rendering_params[:errors]
|
91
89
|
Array(errors)
|
92
90
|
end
|
91
|
+
|
92
|
+
def with_controller_action_logging(&block)
|
93
|
+
LogControllerAction.call(
|
94
|
+
controller_name: self.class.name,
|
95
|
+
action_name: action_name,
|
96
|
+
params: params,
|
97
|
+
graphql_request: graphql_request,
|
98
|
+
&block
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def format_controller_results
|
103
|
+
FormatResults.new(
|
104
|
+
graphql_request.object_to_return,
|
105
|
+
action_config: self.class.action(action_name),
|
106
|
+
params: params,
|
107
|
+
graphql_context: graphql_request.context
|
108
|
+
).call
|
109
|
+
end
|
93
110
|
end
|
94
111
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Decorator
|
5
|
+
# wrapps active record relation and returns decorated object instead
|
6
|
+
class RelationDecorator
|
7
|
+
delegate :map, :each, to: :to_a
|
8
|
+
delegate :limit_value, :offset_value, :count, :size, to: :relation
|
9
|
+
|
10
|
+
def self.decorates?(object)
|
11
|
+
(defined?(ActiveRecord) && object.is_a?(ActiveRecord::Relation)) ||
|
12
|
+
defined?(Mongoid) && object.is_a?(Mongoid::Criteria)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(decorator:, relation:, decorator_args: [])
|
16
|
+
@relation = relation
|
17
|
+
@decorator = decorator
|
18
|
+
@decorator_args = decorator_args
|
19
|
+
end
|
20
|
+
|
21
|
+
%i[where limit order group offset from select having all unscope].each do |method_name|
|
22
|
+
define_method method_name do |*args, &block|
|
23
|
+
chainable_method(method_name, *args, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
%i[first second last].each do |method_name|
|
28
|
+
define_method method_name do |*args, &block|
|
29
|
+
decoratable_object_method(method_name, *args, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
%i[find_each].each do |method_name|
|
34
|
+
define_method method_name do |*args, &block|
|
35
|
+
decoratable_block_method(method_name, *args, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_a
|
40
|
+
@to_a ||= relation.to_a.map { |it| decorator.new(it) }
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :relation, :decorator, :decorator_args
|
46
|
+
|
47
|
+
def decoratable_object_method(method_name, *args, &block)
|
48
|
+
object = relation.public_send(method_name, *args, &block)
|
49
|
+
decorate(object)
|
50
|
+
end
|
51
|
+
|
52
|
+
def decorate(object_or_list)
|
53
|
+
return object_or_list if object_or_list.blank?
|
54
|
+
|
55
|
+
if object_or_list.is_a?(Array)
|
56
|
+
object_or_list.map { |it| decorator.new(it, *decorator_args) }
|
57
|
+
else
|
58
|
+
decorator.new(object_or_list, *decorator_args)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def decoratable_block_method(method_name, *args)
|
63
|
+
relation.public_send(method_name, *args) do |object, *other_args|
|
64
|
+
decorated_object = decorate(object)
|
65
|
+
yield(decorated_object, *other_args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def chainable_method(method_name, *args, &block)
|
70
|
+
new_relation = relation.public_send(method_name, *args, &block)
|
71
|
+
self.class.new(decorator: decorator, relation: new_relation, decorator_args: decorator_args)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
GraphQL::Relay::BaseConnection.register_connection_implementation(
|
76
|
+
RelationDecorator, GraphQL::Relay::RelationConnection
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
# adds `.decorate` class method to any class. Handy when using with paginated responses
|
5
|
+
#
|
6
|
+
# usage:
|
7
|
+
# class FriendDecorator < SimpleDecorator
|
8
|
+
# include GraphqlRails::Decorator
|
9
|
+
#
|
10
|
+
# graphql.attribute :full_name
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# class User
|
14
|
+
# has_many :friends
|
15
|
+
# graphql.attribute :decorated_friends, paginated: true, type: 'FriendDecorator!'
|
16
|
+
#
|
17
|
+
# def decorated_friends
|
18
|
+
# FriendDecorator.decorate(friends)
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
module Decorator
|
22
|
+
require 'active_support/concern'
|
23
|
+
require 'graphql_rails/decorator/relation_decorator'
|
24
|
+
|
25
|
+
extend ActiveSupport::Concern
|
26
|
+
|
27
|
+
class_methods do
|
28
|
+
def decorate(object, *args)
|
29
|
+
if Decorator::RelationDecorator.decorates?(object)
|
30
|
+
Decorator::RelationDecorator.new(relation: object, decorator: self, decorator_args: args)
|
31
|
+
elsif object.nil?
|
32
|
+
nil
|
33
|
+
elsif object.is_a?(Array)
|
34
|
+
object.map { |item| new(item, *args) }
|
35
|
+
else
|
36
|
+
new(object, *args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Integrations
|
5
|
+
# lograge integration
|
6
|
+
#
|
7
|
+
# usage:
|
8
|
+
# add `GraphqlRails::Integrations::Lograge.enable` in your initializers
|
9
|
+
module Lograge
|
10
|
+
require 'lograge'
|
11
|
+
|
12
|
+
# lograge subscriber for graphql_rails controller events
|
13
|
+
class GraphqlActionControllerSubscriber < ::Lograge::LogSubscribers::Base
|
14
|
+
def process_action(event)
|
15
|
+
process_main_event(event)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def initial_data(payload)
|
21
|
+
{
|
22
|
+
controller: payload[:controller],
|
23
|
+
action: payload[:action]
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.enable
|
29
|
+
return unless active?
|
30
|
+
|
31
|
+
GraphqlActionControllerSubscriber.attach_to :graphql_action_controller
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.active?
|
35
|
+
!defined?(Rails) || Rails.configuration&.lograge&.enabled
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Integrations
|
5
|
+
# sentry integration
|
6
|
+
module Sentry
|
7
|
+
require 'active_support/concern'
|
8
|
+
|
9
|
+
# controller extension which logs errors to sentry
|
10
|
+
module SentryLogger
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do
|
14
|
+
around_action :log_to_sentry
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def log_to_sentry
|
19
|
+
Raven.context.transaction.pop
|
20
|
+
Raven.context.transaction.push "#{self.class}##{action_name}"
|
21
|
+
yield
|
22
|
+
rescue Exception => error # rubocop:disable Lint/RescueException
|
23
|
+
Raven.capture_exception(error) unless error.is_a?(GraphQL::ExecutionError)
|
24
|
+
raise error
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.enable
|
30
|
+
GraphqlRails::Controller.include(SentryLogger)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
# allows to enable various integrations
|
5
|
+
module Integrations
|
6
|
+
def self.enable(*integrations)
|
7
|
+
@enabled_integrations ||= []
|
8
|
+
|
9
|
+
to_be_enabled_integrations = integrations.map(&:to_s) - @enabled_integrations
|
10
|
+
|
11
|
+
to_be_enabled_integrations.each do |integration|
|
12
|
+
require_relative "./integrations/#{integration}"
|
13
|
+
Integrations.const_get(integration.classify).enable
|
14
|
+
end
|
15
|
+
|
16
|
+
@enabled_integrations += to_be_enabled_integrations
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -17,13 +17,26 @@ module GraphqlRails
|
|
17
17
|
@model_class = model_class
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
20
|
+
def initialize_copy(other)
|
21
|
+
super
|
22
|
+
@connection_type = nil
|
23
|
+
@graphql_type = nil
|
24
|
+
@input = other.instance_variable_get(:@input)&.transform_values(&:dup)
|
25
|
+
@attributes = other.instance_variable_get(:@attributes)&.transform_values(&:dup)
|
26
|
+
end
|
27
|
+
|
28
|
+
def attribute(attribute_name, **attribute_options)
|
29
|
+
key = attribute_name.to_s
|
30
|
+
|
31
|
+
attributes[key] ||= Attributes::Attribute.new(attribute_name)
|
32
|
+
|
33
|
+
attributes[key].tap do |attribute|
|
34
|
+
attribute_options.each do |method_name, args|
|
35
|
+
attribute.public_send(method_name, args)
|
36
|
+
end
|
37
|
+
|
38
|
+
yield(attribute) if block_given?
|
39
|
+
end
|
27
40
|
end
|
28
41
|
|
29
42
|
def input(input_name = nil)
|
@@ -35,7 +35,7 @@ module GraphqlRails
|
|
35
35
|
|
36
36
|
def default_name
|
37
37
|
@default_name ||= begin
|
38
|
-
suffix = input_name_suffix ? input_name_suffix.to_s.
|
38
|
+
suffix = input_name_suffix ? input_name_suffix.to_s.camelize : ''
|
39
39
|
"#{model_class.name.split('::').last}#{suffix}Input"
|
40
40
|
end
|
41
41
|
end
|
data/lib/graphql_rails/model.rb
CHANGED
@@ -23,6 +23,13 @@ module GraphqlRails
|
|
23
23
|
|
24
24
|
# static methods for GraphqlRails::Model
|
25
25
|
module ClassMethods
|
26
|
+
def inherited(subclass)
|
27
|
+
super
|
28
|
+
subclass.instance_variable_set(:@graphql, graphql.dup)
|
29
|
+
subclass.graphql.instance_variable_set(:@model_class, self)
|
30
|
+
subclass.graphql.instance_variable_set(:@graphql_type, nil)
|
31
|
+
end
|
32
|
+
|
26
33
|
def graphql
|
27
34
|
@graphql ||= Model::Configuration.new(self)
|
28
35
|
yield(@graphql) if block_given?
|