graphql_rails 0.8.0 → 1.0.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 +13 -0
- data/Gemfile.lock +20 -23
- data/docs/README.md +21 -5
- data/docs/_sidebar.md +3 -0
- data/docs/components/controller.md +174 -17
- data/docs/components/model.md +151 -3
- data/docs/components/routes.md +28 -0
- data/docs/getting_started/quick_start.md +9 -2
- 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 +1 -1
- 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 +18 -0
- data/lib/graphql_rails.rb +2 -0
- data/lib/graphql_rails/attributes/attributable.rb +16 -13
- data/lib/graphql_rails/attributes/attribute.rb +20 -3
- data/lib/graphql_rails/attributes/input_attribute.rb +20 -10
- data/lib/graphql_rails/attributes/input_type_parser.rb +24 -46
- data/lib/graphql_rails/attributes/type_parseable.rb +128 -0
- data/lib/graphql_rails/attributes/type_parser.rb +58 -54
- data/lib/graphql_rails/concerns/service.rb +15 -0
- data/lib/graphql_rails/controller.rb +20 -16
- data/lib/graphql_rails/controller/action.rb +10 -69
- data/lib/graphql_rails/controller/action_configuration.rb +70 -34
- 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 -5
- data/lib/graphql_rails/controller/log_controller_action.rb +4 -4
- 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/relation_decorator.rb +1 -1
- 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/model.rb +19 -4
- data/lib/graphql_rails/model/build_connection_type.rb +48 -0
- data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +4 -4
- data/lib/graphql_rails/model/build_enum_type.rb +39 -10
- data/lib/graphql_rails/model/build_graphql_input_type.rb +7 -3
- data/lib/graphql_rails/model/build_graphql_type.rb +23 -7
- data/lib/graphql_rails/model/call_graphql_model_method.rb +59 -0
- data/lib/graphql_rails/model/configuration.rb +2 -6
- data/lib/graphql_rails/model/input.rb +10 -6
- 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 +2 -1
- data/lib/graphql_rails/router/route.rb +25 -6
- data/lib/graphql_rails/router/schema_builder.rb +26 -11
- data/lib/graphql_rails/rspec_controller_helpers.rb +4 -2
- 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 +29 -9
- data/lib/graphql_rails/controller/controller_function.rb +0 -50
- data/lib/graphql_rails/controller/format_results.rb +0 -36
@@ -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,25 @@ 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
|
50
|
-
end
|
51
|
-
|
52
|
-
def raise_missing_return_type_error
|
53
|
-
error_message = \
|
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
|
38
|
+
def type_args
|
39
|
+
[type_parser.type_arg, null: !type_parser.required?]
|
59
40
|
end
|
60
41
|
|
61
|
-
|
62
|
-
return default_collection_type if route.collection?
|
63
|
-
|
64
|
-
default_inner_return_type
|
65
|
-
end
|
42
|
+
private
|
66
43
|
|
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
|
44
|
+
attr_reader :route
|
74
45
|
|
75
|
-
|
76
|
-
route.relative_path
|
77
|
-
end
|
46
|
+
delegate :type_parser, to: :action_config
|
78
47
|
|
79
48
|
def action_config
|
80
|
-
controller.controller_configuration.
|
49
|
+
controller.controller_configuration.action_config(name)
|
81
50
|
end
|
82
51
|
|
83
52
|
def namespaced_controller_name
|
@@ -85,40 +54,12 @@ module GraphqlRails
|
|
85
54
|
end
|
86
55
|
|
87
56
|
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
|
57
|
+
@controller_name ||= relative_path.split('#').first
|
110
58
|
end
|
111
59
|
|
112
60
|
def namespaced_model_name
|
113
61
|
namespaced_controller_name.singularize.classify
|
114
62
|
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
63
|
end
|
123
64
|
end
|
124
65
|
end
|
@@ -2,50 +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
|
-
|
31
|
-
self
|
32
|
-
end
|
41
|
+
def options(action_options = nil)
|
42
|
+
@input_attribute_options ||= {}
|
43
|
+
return @input_attribute_options if action_options.nil?
|
33
44
|
|
34
|
-
|
35
|
-
field_name = name.to_s.remove(/!\Z/)
|
45
|
+
@input_attribute_options[:input_format] = action_options[:input_format] if action_options[:input_format]
|
36
46
|
|
37
|
-
attributes[field_name] = Attributes::InputAttribute.new(
|
38
|
-
name.to_s, type,
|
39
|
-
options: action_options.merge(options),
|
40
|
-
**input_options
|
41
|
-
)
|
42
47
|
self
|
43
48
|
end
|
44
49
|
|
45
|
-
def paginated(
|
50
|
+
def paginated(*args)
|
46
51
|
@return_type = nil
|
47
|
-
|
48
|
-
permit(:before, :after, first: :int, last: :int)
|
52
|
+
super
|
49
53
|
end
|
50
54
|
|
51
55
|
def description(new_description = nil)
|
@@ -57,35 +61,51 @@ module GraphqlRails
|
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
|
-
def can_return_nil
|
61
|
-
@can_return_nil = true
|
62
|
-
self
|
63
|
-
end
|
64
|
-
|
65
64
|
def returns(custom_return_type)
|
66
65
|
@return_type = nil
|
67
66
|
@custom_return_type = custom_return_type
|
68
67
|
self
|
69
68
|
end
|
70
69
|
|
71
|
-
def
|
72
|
-
|
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
|
73
77
|
end
|
74
78
|
|
75
|
-
def
|
76
|
-
|
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)
|
77
93
|
end
|
78
94
|
|
79
95
|
def return_type
|
80
96
|
@return_type ||= build_return_type
|
81
97
|
end
|
82
98
|
|
99
|
+
def type_parser
|
100
|
+
@type_parser ||= Attributes::TypeParser.new(custom_return_type, paginated: paginated?)
|
101
|
+
end
|
102
|
+
|
83
103
|
private
|
84
104
|
|
85
|
-
attr_reader :custom_return_type
|
105
|
+
attr_reader :custom_return_type
|
86
106
|
|
87
107
|
def build_return_type
|
88
|
-
return
|
108
|
+
return raise_deprecation_error if custom_return_type.nil?
|
89
109
|
|
90
110
|
if paginated?
|
91
111
|
type_parser.graphql_model ? type_parser.graphql_model.graphql.connection_type : nil
|
@@ -94,8 +114,24 @@ module GraphqlRails
|
|
94
114
|
end
|
95
115
|
end
|
96
116
|
|
97
|
-
def
|
98
|
-
|
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
|
124
|
+
end
|
125
|
+
|
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
|
99
135
|
end
|
100
136
|
end
|
101
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)
|
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)
|
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
|
data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql_rails/controller/request'
|
4
|
+
|
5
|
+
module GraphqlRails
|
6
|
+
class Controller
|
7
|
+
class BuildControllerActionResolver
|
8
|
+
# Resolver which includes controller specific methods.
|
9
|
+
# Used to simplify resolver build for each controller action
|
10
|
+
class ControllerActionResolver < GraphQL::Schema::Resolver
|
11
|
+
def self.controller(controller_class = nil)
|
12
|
+
@controller = controller_class if controller_class
|
13
|
+
@controller
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.controller_action_name(name = nil)
|
17
|
+
@controller_action_name = name if name
|
18
|
+
@controller_action_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def resolve(**inputs)
|
22
|
+
request = Request.new(object, inputs, context)
|
23
|
+
self.class.controller.new(request).call(self.class.controller_action_name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -3,14 +3,20 @@
|
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
4
4
|
require 'graphql_rails/controller/action_configuration'
|
5
5
|
require 'graphql_rails/controller/action_hook'
|
6
|
+
require 'graphql_rails/errors/error'
|
6
7
|
|
7
8
|
module GraphqlRails
|
8
9
|
class Controller
|
9
10
|
# stores all graphql_rails contoller specific config
|
10
11
|
class Configuration
|
12
|
+
class InvalidActionConfiguration < GraphqlRails::Error; end
|
13
|
+
|
14
|
+
LIB_REGEXP = %r{/graphql_rails/lib/}
|
15
|
+
|
11
16
|
attr_reader :action_by_name
|
12
17
|
|
13
|
-
def initialize
|
18
|
+
def initialize(controller)
|
19
|
+
@controller = controller
|
14
20
|
@hooks = {
|
15
21
|
before: {},
|
16
22
|
after: {},
|
@@ -18,6 +24,7 @@ module GraphqlRails
|
|
18
24
|
}
|
19
25
|
|
20
26
|
@action_by_name = {}
|
27
|
+
@action_default = nil
|
21
28
|
end
|
22
29
|
|
23
30
|
def initialize_copy(other)
|
@@ -31,6 +38,12 @@ module GraphqlRails
|
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
41
|
+
def dup_with(controller:)
|
42
|
+
dup.tap do |new_config|
|
43
|
+
new_config.instance_variable_set(:@controller, controller)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
34
47
|
def action_hooks_for(hook_type, action_name)
|
35
48
|
hooks[hook_type].values.select { |hook| hook.applicable_for?(action_name) }
|
36
49
|
end
|
@@ -43,15 +56,53 @@ module GraphqlRails
|
|
43
56
|
ActionHook.new(name: hook_name, **options, &block)
|
44
57
|
end
|
45
58
|
|
59
|
+
def action_default
|
60
|
+
@action_default ||= ActionConfiguration.new(name: :default, controller: nil)
|
61
|
+
yield(@action_default) if block_given?
|
62
|
+
@action_default
|
63
|
+
end
|
64
|
+
|
46
65
|
def action(method_name)
|
47
|
-
|
48
|
-
|
49
|
-
|
66
|
+
action_name = method_name.to_s.underscore
|
67
|
+
@action_by_name[action_name] ||= action_default.dup_with(
|
68
|
+
name: action_name,
|
69
|
+
controller: controller,
|
70
|
+
defined_at: dynamic_source_location
|
71
|
+
)
|
72
|
+
yield(@action_by_name[action_name]) if block_given?
|
73
|
+
@action_by_name[action_name]
|
74
|
+
end
|
75
|
+
|
76
|
+
def action_config(method_name)
|
77
|
+
action_name = method_name.to_s.underscore
|
78
|
+
@action_by_name.fetch(action_name) { raise_invalid_config_error(action_name) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def model(model = nil)
|
82
|
+
action_default.model(model)
|
50
83
|
end
|
51
84
|
|
52
85
|
private
|
53
86
|
|
54
|
-
attr_reader :hooks
|
87
|
+
attr_reader :hooks, :controller
|
88
|
+
|
89
|
+
def dynamic_source_location
|
90
|
+
project_trace = \
|
91
|
+
caller
|
92
|
+
.dup
|
93
|
+
.drop_while { |path| !path.match?(LIB_REGEXP) }
|
94
|
+
.drop_while { |path| path.match?(LIB_REGEXP) }
|
95
|
+
|
96
|
+
project_trace.first
|
97
|
+
end
|
98
|
+
|
99
|
+
def raise_invalid_config_error(action_name)
|
100
|
+
error_message = \
|
101
|
+
"Missing action configuration for #{controller}##{action_name}. " \
|
102
|
+
"Please define it with `action(:#{action_name})`."
|
103
|
+
|
104
|
+
raise InvalidActionConfiguration, error_message
|
105
|
+
end
|
55
106
|
end
|
56
107
|
end
|
57
108
|
end
|