graphql_rails 0.7.0 → 0.8.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.
- 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?
|