graphql_rails 0.7.0 → 1.2.1
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/.hound.yml +1 -0
- data/.rubocop.yml +3 -3
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +35 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +181 -71
- data/docs/README.md +40 -8
- data/docs/_sidebar.md +5 -0
- data/docs/components/controller.md +295 -9
- data/docs/components/decorator.md +69 -0
- data/docs/components/model.md +267 -6
- data/docs/components/routes.md +28 -0
- data/docs/getting_started/quick_start.md +10 -3
- data/docs/index.html +1 -1
- data/docs/logging_and_monitoring/logging_and_monitoring.md +35 -0
- data/docs/other_tools/query_runner.md +49 -0
- data/docs/other_tools/schema_dump.md +29 -0
- data/docs/testing/testing.md +3 -1
- data/graphql_rails.gemspec +5 -4
- data/lib/generators/graphql_rails/install_generator.rb +50 -0
- data/lib/generators/graphql_rails/templates/example_users_controller.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_application_controller.erb +8 -0
- data/lib/generators/graphql_rails/templates/graphql_controller.erb +20 -0
- data/lib/generators/graphql_rails/templates/graphql_router.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +21 -0
- data/lib/graphql_rails.rb +6 -0
- data/lib/graphql_rails/attributes/attributable.rb +22 -17
- data/lib/graphql_rails/attributes/attribute.rb +67 -3
- data/lib/graphql_rails/attributes/attribute_name_parser.rb +4 -4
- data/lib/graphql_rails/attributes/input_attribute.rb +33 -15
- data/lib/graphql_rails/attributes/input_type_parser.rb +62 -0
- data/lib/graphql_rails/attributes/type_name_info.rb +38 -0
- data/lib/graphql_rails/attributes/type_parseable.rb +132 -0
- data/lib/graphql_rails/attributes/type_parser.rb +59 -53
- data/lib/graphql_rails/concerns/service.rb +19 -0
- data/lib/graphql_rails/controller.rb +42 -21
- data/lib/graphql_rails/controller/action.rb +12 -67
- data/lib/graphql_rails/controller/action_configuration.rb +70 -28
- data/lib/graphql_rails/controller/build_controller_action_resolver.rb +52 -0
- data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +28 -0
- data/lib/graphql_rails/controller/configuration.rb +56 -3
- data/lib/graphql_rails/controller/log_controller_action.rb +71 -0
- data/lib/graphql_rails/controller/request.rb +29 -8
- data/lib/graphql_rails/controller/request/format_errors.rb +58 -0
- data/lib/graphql_rails/decorator.rb +41 -0
- data/lib/graphql_rails/decorator/relation_decorator.rb +75 -0
- data/lib/graphql_rails/errors/custom_execution_error.rb +22 -0
- data/lib/graphql_rails/errors/execution_error.rb +6 -7
- data/lib/graphql_rails/errors/system_error.rb +14 -0
- data/lib/graphql_rails/errors/validation_error.rb +1 -5
- data/lib/graphql_rails/input_configurable.rb +47 -0
- data/lib/graphql_rails/integrations.rb +19 -0
- data/lib/graphql_rails/integrations/lograge.rb +39 -0
- data/lib/graphql_rails/integrations/sentry.rb +34 -0
- data/lib/graphql_rails/model.rb +26 -4
- data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
- data/lib/graphql_rails/model/build_connection_type.rb +52 -0
- data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +5 -5
- data/lib/graphql_rails/model/build_enum_type.rb +39 -10
- data/lib/graphql_rails/model/build_graphql_input_type.rb +8 -4
- data/lib/graphql_rails/model/call_graphql_model_method.rb +72 -0
- data/lib/graphql_rails/model/configurable.rb +6 -2
- data/lib/graphql_rails/model/configuration.rb +30 -16
- data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
- data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
- data/lib/graphql_rails/model/input.rb +11 -7
- data/lib/graphql_rails/query_runner.rb +68 -0
- data/lib/graphql_rails/railtie.rb +10 -0
- data/lib/graphql_rails/router.rb +40 -13
- data/lib/graphql_rails/router/resource_routes_builder.rb +10 -9
- data/lib/graphql_rails/router/route.rb +21 -6
- data/lib/graphql_rails/router/schema_builder.rb +30 -11
- data/lib/graphql_rails/rspec_controller_helpers.rb +6 -4
- data/lib/graphql_rails/tasks/dump_graphql_schema.rb +57 -0
- data/lib/graphql_rails/tasks/schema.rake +14 -0
- data/lib/graphql_rails/version.rb +1 -1
- metadata +70 -19
- data/lib/graphql_rails/controller/controller_function.rb +0 -50
- data/lib/graphql_rails/controller/format_results.rb +0 -36
- data/lib/graphql_rails/model/build_graphql_type.rb +0 -37
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
# includes all service object related logic
|
5
|
+
module Service
|
6
|
+
require 'active_support/concern'
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
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
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,35 +4,46 @@ require 'active_support/hash_with_indifferent_access'
|
|
4
4
|
require 'active_support/core_ext/hash'
|
5
5
|
require 'graphql_rails/controller/configuration'
|
6
6
|
require 'graphql_rails/controller/request'
|
7
|
-
require 'graphql_rails/controller/format_results'
|
8
7
|
require 'graphql_rails/controller/action_hooks_runner'
|
8
|
+
require 'graphql_rails/controller/log_controller_action'
|
9
|
+
require 'graphql_rails/errors/system_error'
|
9
10
|
|
10
11
|
module GraphqlRails
|
11
12
|
# base class for all graphql_rails controllers
|
12
13
|
class Controller
|
13
14
|
class << self
|
14
|
-
def inherited(
|
15
|
-
|
15
|
+
def inherited(subclass)
|
16
|
+
super
|
17
|
+
new_config = controller_configuration.dup_with(controller: subclass)
|
18
|
+
subclass.instance_variable_set(:@controller_configuration, new_config)
|
16
19
|
end
|
17
20
|
|
18
|
-
def before_action(*args, &block)
|
19
|
-
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)
|
20
23
|
end
|
21
24
|
|
22
|
-
def around_action(*args, &block)
|
23
|
-
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)
|
24
27
|
end
|
25
28
|
|
26
|
-
def after_action(*args, &block)
|
27
|
-
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)
|
28
31
|
end
|
29
32
|
|
30
33
|
def action(action_name)
|
31
34
|
controller_configuration.action(action_name)
|
32
35
|
end
|
33
36
|
|
37
|
+
def action_default
|
38
|
+
controller_configuration.action_default
|
39
|
+
end
|
40
|
+
|
41
|
+
def model(*args)
|
42
|
+
controller_configuration.model(*args)
|
43
|
+
end
|
44
|
+
|
34
45
|
def controller_configuration
|
35
|
-
@controller_configuration ||= Controller::Configuration.new
|
46
|
+
@controller_configuration ||= Controller::Configuration.new(self)
|
36
47
|
end
|
37
48
|
end
|
38
49
|
|
@@ -44,14 +55,10 @@ module GraphqlRails
|
|
44
55
|
|
45
56
|
def call(method_name)
|
46
57
|
@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
|
58
|
+
with_controller_action_logging do
|
59
|
+
call_with_rendering
|
60
|
+
graphql_request.object_to_return
|
61
|
+
end
|
55
62
|
ensure
|
56
63
|
@action_name = nil
|
57
64
|
end
|
@@ -74,13 +81,17 @@ module GraphqlRails
|
|
74
81
|
|
75
82
|
private
|
76
83
|
|
77
|
-
def call_with_rendering
|
84
|
+
def call_with_rendering
|
78
85
|
hooks_runner = ActionHooksRunner.new(action_name: action_name, controller: self)
|
79
86
|
response = hooks_runner.call { public_send(action_name) }
|
80
87
|
|
81
88
|
render response if graphql_request.no_object_to_return?
|
82
89
|
rescue StandardError => error
|
83
|
-
|
90
|
+
if error.is_a?(GraphQL::ExecutionError)
|
91
|
+
render error: error
|
92
|
+
else
|
93
|
+
render error: SystemError.new(error.message)
|
94
|
+
end
|
84
95
|
end
|
85
96
|
|
86
97
|
def graphql_errors_from_render_params(rendering_params)
|
@@ -88,7 +99,17 @@ module GraphqlRails
|
|
88
99
|
return [] if rendering_params.keys.count != 1
|
89
100
|
|
90
101
|
errors = rendering_params[:error] || rendering_params[:errors]
|
91
|
-
Array(errors)
|
102
|
+
errors.is_a?(Enumerable) ? errors : Array(errors)
|
103
|
+
end
|
104
|
+
|
105
|
+
def with_controller_action_logging(&block)
|
106
|
+
LogControllerAction.call(
|
107
|
+
controller_name: self.class.name,
|
108
|
+
action_name: action_name,
|
109
|
+
params: params,
|
110
|
+
graphql_request: graphql_request,
|
111
|
+
&block
|
112
|
+
)
|
92
113
|
end
|
93
114
|
end
|
94
115
|
end
|
@@ -9,14 +9,14 @@ module GraphqlRails
|
|
9
9
|
class Controller
|
10
10
|
# analyzes route and extracts controller action related data
|
11
11
|
class Action
|
12
|
-
|
12
|
+
delegate :relative_path, to: :route
|
13
13
|
|
14
14
|
def initialize(route)
|
15
15
|
@route = route
|
16
16
|
end
|
17
17
|
|
18
18
|
def return_type
|
19
|
-
action_config.return_type
|
19
|
+
action_config.return_type
|
20
20
|
end
|
21
21
|
|
22
22
|
def arguments
|
@@ -28,56 +28,29 @@ module GraphqlRails
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def name
|
31
|
-
@name ||=
|
31
|
+
@name ||= relative_path.split('#').last
|
32
32
|
end
|
33
33
|
|
34
34
|
def description
|
35
35
|
action_config.description
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
attr_reader :route
|
41
|
-
|
42
|
-
def default_inner_return_type
|
43
|
-
raise_missing_return_type_error if model_graphql_type.nil?
|
44
|
-
|
45
|
-
if action_config.can_return_nil?
|
46
|
-
model_graphql_type
|
47
|
-
else
|
48
|
-
model_graphql_type.to_non_null_type
|
49
|
-
end
|
38
|
+
def type_args
|
39
|
+
[type_parser.type_arg]
|
50
40
|
end
|
51
41
|
|
52
|
-
def
|
53
|
-
|
54
|
-
"Return type for #{route.path.inspect} is not defined. " \
|
55
|
-
"To do so, add `action(:#{name}).returns(YourType)` in #{controller.name} " \
|
56
|
-
"or make sure that you have model named #{namespaced_model_name}"
|
57
|
-
|
58
|
-
raise MissingConfigurationError, error_message
|
42
|
+
def type_options
|
43
|
+
{ null: !type_parser.required? }
|
59
44
|
end
|
60
45
|
|
61
|
-
|
62
|
-
return default_collection_type if route.collection?
|
63
|
-
|
64
|
-
default_inner_return_type
|
65
|
-
end
|
46
|
+
private
|
66
47
|
|
67
|
-
|
68
|
-
if action_config.paginated?
|
69
|
-
action_model.graphql.connection_type
|
70
|
-
else
|
71
|
-
default_inner_return_type.to_list_type.to_non_null_type
|
72
|
-
end
|
73
|
-
end
|
48
|
+
attr_reader :route
|
74
49
|
|
75
|
-
|
76
|
-
route.relative_path
|
77
|
-
end
|
50
|
+
delegate :type_parser, to: :action_config
|
78
51
|
|
79
52
|
def action_config
|
80
|
-
controller.controller_configuration.
|
53
|
+
controller.controller_configuration.action_config(name)
|
81
54
|
end
|
82
55
|
|
83
56
|
def namespaced_controller_name
|
@@ -85,40 +58,12 @@ module GraphqlRails
|
|
85
58
|
end
|
86
59
|
|
87
60
|
def controller_name
|
88
|
-
@controller_name ||=
|
89
|
-
end
|
90
|
-
|
91
|
-
def action_model
|
92
|
-
namespace = namespaced_model_name.split('::')
|
93
|
-
model_name = namespace.pop
|
94
|
-
model = nil
|
95
|
-
|
96
|
-
while model.nil? && !namespace.empty?
|
97
|
-
model = namespaced_model(namespace, model_name)
|
98
|
-
namespace.pop
|
99
|
-
end
|
100
|
-
|
101
|
-
model || namespaced_model(namespace, model_name)
|
102
|
-
end
|
103
|
-
|
104
|
-
def namespaced_model(namespace, model_name)
|
105
|
-
[namespace, model_name].join('::').constantize
|
106
|
-
rescue NameError => err
|
107
|
-
raise unless err.message.match?(/uninitialized constant/)
|
108
|
-
|
109
|
-
nil
|
61
|
+
@controller_name ||= relative_path.split('#').first
|
110
62
|
end
|
111
63
|
|
112
64
|
def namespaced_model_name
|
113
65
|
namespaced_controller_name.singularize.classify
|
114
66
|
end
|
115
|
-
|
116
|
-
def model_graphql_type
|
117
|
-
return unless action_model
|
118
|
-
return unless action_model < GraphqlRails::Model
|
119
|
-
|
120
|
-
action_model.graphql.graphql_type
|
121
|
-
end
|
122
67
|
end
|
123
68
|
end
|
124
69
|
end
|
@@ -2,39 +2,54 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/string/filters'
|
4
4
|
require 'graphql_rails/attributes'
|
5
|
+
require 'graphql_rails/input_configurable'
|
6
|
+
require 'graphql_rails/errors/error'
|
5
7
|
|
6
8
|
module GraphqlRails
|
7
9
|
class Controller
|
8
10
|
# stores all graphql_rails contoller specific config
|
9
11
|
class ActionConfiguration
|
10
|
-
|
12
|
+
class MissingConfigurationError < GraphqlRails::Error; end
|
13
|
+
class DeprecatedDefaultModelError < GraphqlRails::Error; end
|
14
|
+
|
15
|
+
include InputConfigurable
|
16
|
+
|
17
|
+
attr_reader :attributes, :pagination_options, :name, :controller, :defined_at
|
11
18
|
|
12
19
|
def initialize_copy(other)
|
13
20
|
super
|
14
21
|
@attributes = other.instance_variable_get(:@attributes).dup.transform_values(&:dup)
|
22
|
+
@action_options = other.instance_variable_get(:@action_options).dup.transform_values(&:dup)
|
23
|
+
@pagination_options = other.instance_variable_get(:@pagination_options)&.dup&.transform_values(&:dup)
|
15
24
|
end
|
16
25
|
|
17
|
-
def initialize
|
26
|
+
def initialize(name:, controller:)
|
27
|
+
@name = name
|
28
|
+
@controller = controller
|
18
29
|
@attributes = {}
|
19
30
|
@action_options = {}
|
20
|
-
@can_return_nil = false
|
21
31
|
end
|
22
32
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
33
|
+
def dup_with(name:, controller:, defined_at:)
|
34
|
+
dup.tap do |new_action|
|
35
|
+
new_action.instance_variable_set(:@defined_at, defined_at)
|
36
|
+
new_action.instance_variable_set(:@name, name)
|
37
|
+
new_action.instance_variable_set(:@controller, controller)
|
38
|
+
end
|
26
39
|
end
|
27
40
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
41
|
+
def options(action_options = nil)
|
42
|
+
@input_attribute_options ||= {}
|
43
|
+
return @input_attribute_options if action_options.nil?
|
44
|
+
|
45
|
+
@input_attribute_options[:input_format] = action_options[:input_format] if action_options[:input_format]
|
46
|
+
|
31
47
|
self
|
32
48
|
end
|
33
49
|
|
34
|
-
def paginated(
|
50
|
+
def paginated(*args)
|
35
51
|
@return_type = nil
|
36
|
-
|
37
|
-
permit(:before, :after, first: :int, last: :int)
|
52
|
+
super
|
38
53
|
end
|
39
54
|
|
40
55
|
def description(new_description = nil)
|
@@ -46,35 +61,51 @@ module GraphqlRails
|
|
46
61
|
end
|
47
62
|
end
|
48
63
|
|
49
|
-
def can_return_nil
|
50
|
-
@can_return_nil = true
|
51
|
-
self
|
52
|
-
end
|
53
|
-
|
54
64
|
def returns(custom_return_type)
|
55
65
|
@return_type = nil
|
56
66
|
@custom_return_type = custom_return_type
|
57
67
|
self
|
58
68
|
end
|
59
69
|
|
60
|
-
def
|
61
|
-
|
70
|
+
def model(model_name = nil)
|
71
|
+
if model_name
|
72
|
+
@model = model_name
|
73
|
+
self
|
74
|
+
else
|
75
|
+
@model || raise_missing_config_error
|
76
|
+
end
|
62
77
|
end
|
63
78
|
|
64
|
-
def
|
65
|
-
|
79
|
+
def returns_single(required: true)
|
80
|
+
model_name = model.to_s
|
81
|
+
model_name = "#{model_name}!" if required
|
82
|
+
|
83
|
+
returns(model_name)
|
84
|
+
end
|
85
|
+
|
86
|
+
def returns_list(required_inner: true, required_list: true)
|
87
|
+
model_name = model.to_s
|
88
|
+
model_name = "#{model_name}!" if required_inner
|
89
|
+
list_name = "[#{model_name}]"
|
90
|
+
list_name = "#{list_name}!" if required_list
|
91
|
+
|
92
|
+
returns(list_name)
|
66
93
|
end
|
67
94
|
|
68
95
|
def return_type
|
69
96
|
@return_type ||= build_return_type
|
70
97
|
end
|
71
98
|
|
99
|
+
def type_parser
|
100
|
+
@type_parser ||= Attributes::TypeParser.new(custom_return_type, paginated: paginated?)
|
101
|
+
end
|
102
|
+
|
72
103
|
private
|
73
104
|
|
74
|
-
attr_reader :custom_return_type
|
105
|
+
attr_reader :custom_return_type
|
75
106
|
|
76
107
|
def build_return_type
|
77
|
-
return
|
108
|
+
return raise_deprecation_error if custom_return_type.nil?
|
78
109
|
|
79
110
|
if paginated?
|
80
111
|
type_parser.graphql_model ? type_parser.graphql_model.graphql.connection_type : nil
|
@@ -83,13 +114,24 @@ module GraphqlRails
|
|
83
114
|
end
|
84
115
|
end
|
85
116
|
|
86
|
-
def
|
87
|
-
|
117
|
+
def raise_deprecation_error
|
118
|
+
message = \
|
119
|
+
'Default return types are deprecated. ' \
|
120
|
+
"You need to manually set something like `action(:#{name}).returns('#{suggested_model_name}')`"
|
121
|
+
|
122
|
+
full_backtrace = ([defined_at] + caller).compact
|
123
|
+
raise DeprecatedDefaultModelError, message, full_backtrace
|
88
124
|
end
|
89
125
|
|
90
|
-
def
|
91
|
-
|
92
|
-
|
126
|
+
def suggested_model_name
|
127
|
+
controller&.name.to_s.demodulize.sub(/Controller$/, '').singularize
|
128
|
+
end
|
129
|
+
|
130
|
+
def raise_missing_config_error
|
131
|
+
error_message = \
|
132
|
+
'Default model for controller is not defined. To do so add `model(YourModel)`'
|
133
|
+
|
134
|
+
raise MissingConfigurationError, error_message
|
93
135
|
end
|
94
136
|
end
|
95
137
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql_rails/controller/action'
|
4
|
+
require 'graphql_rails/concerns/service'
|
5
|
+
require 'graphql_rails/controller/action_configuration'
|
6
|
+
require 'graphql_rails/controller/build_controller_action_resolver/controller_action_resolver'
|
7
|
+
|
8
|
+
module GraphqlRails
|
9
|
+
class Controller
|
10
|
+
# graphql resolver which redirects actions to appropriate controller and controller action
|
11
|
+
class BuildControllerActionResolver
|
12
|
+
include ::GraphqlRails::Service
|
13
|
+
|
14
|
+
def initialize(route:)
|
15
|
+
@route = route
|
16
|
+
end
|
17
|
+
|
18
|
+
def call # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
19
|
+
action = build_action
|
20
|
+
|
21
|
+
Class.new(ControllerActionResolver) do
|
22
|
+
type(*action.type_args, **action.type_options)
|
23
|
+
description(action.description)
|
24
|
+
controller(action.controller)
|
25
|
+
controller_action_name(action.name)
|
26
|
+
|
27
|
+
action.arguments.each do |attribute|
|
28
|
+
argument(*attribute.input_argument_args, **attribute.input_argument_options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.inspect
|
32
|
+
"ControllerActionResolver(#{controller.name}##{controller_action_name})"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :route
|
40
|
+
|
41
|
+
def build_action
|
42
|
+
Action.new(route).tap do |action|
|
43
|
+
assert_action(action)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def assert_action(action)
|
48
|
+
action.return_type
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|