graphql_rails 2.1.0 → 2.3.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 +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +117 -116
- data/docs/README.md +23 -45
- data/docs/_sidebar.md +0 -1
- data/docs/components/controller.md +15 -1
- data/docs/components/decorator.md +1 -1
- data/docs/components/model.md +62 -0
- data/docs/components/routes.md +45 -8
- data/lib/graphql_rails/attributes/attribute.rb +8 -14
- data/lib/graphql_rails/attributes/attribute_configurable.rb +15 -0
- data/lib/graphql_rails/attributes/input_attribute.rb +17 -1
- data/lib/graphql_rails/attributes/type_parseable.rb +1 -7
- data/lib/graphql_rails/controller/build_controller_action_resolver.rb +2 -0
- data/lib/graphql_rails/controller/log_controller_action.rb +7 -2
- data/lib/graphql_rails/controller.rb +1 -1
- data/lib/graphql_rails/decorator/relation_decorator.rb +22 -18
- data/lib/graphql_rails/decorator.rb +12 -4
- data/lib/graphql_rails/errors/system_error.rb +11 -1
- data/lib/graphql_rails/errors/validation_error.rb +14 -1
- data/lib/graphql_rails/input_configurable.rb +1 -1
- data/lib/graphql_rails/model/find_or_build_graphql_type.rb +1 -5
- data/lib/graphql_rails/router/build_schema_action_type.rb +112 -0
- data/lib/graphql_rails/router/mutation_route.rb +4 -0
- data/lib/graphql_rails/router/query_route.rb +4 -0
- data/lib/graphql_rails/router/resource_routes_builder.rb +8 -0
- data/lib/graphql_rails/router/route.rb +3 -2
- data/lib/graphql_rails/router/schema_builder.rb +14 -18
- data/lib/graphql_rails/router/subscription_route.rb +22 -0
- data/lib/graphql_rails/router.rb +32 -10
- data/lib/graphql_rails/version.rb +1 -1
- metadata +5 -4
- data/docs/getting_started/quick_start.md +0 -62
data/docs/components/routes.md
CHANGED
@@ -73,14 +73,15 @@ end
|
|
73
73
|
|
74
74
|
This will generate `userDetails` field on GraphQL side.
|
75
75
|
|
76
|
-
## _query_ and _mutation_
|
76
|
+
## _query_ and _mutation_ & _subscription_
|
77
77
|
|
78
|
-
in case you want to have non-CRUD controller with custom actions you can define your own `query`/`mutation` actions like this:
|
78
|
+
in case you want to have non-CRUD controller with custom actions you can define your own `query`/`mutation`/`subscription` actions like this:
|
79
79
|
|
80
80
|
```ruby
|
81
81
|
MyGraphqlSchema = GraphqlRails::Router.draw do
|
82
82
|
mutation :logIn, to: 'sessions#login'
|
83
|
-
query :me, to 'users#current_user'
|
83
|
+
query :me, to: 'users#current_user'
|
84
|
+
subscribtion :new_message, to: 'messages#created'
|
84
85
|
end
|
85
86
|
```
|
86
87
|
|
@@ -88,18 +89,54 @@ end
|
|
88
89
|
|
89
90
|
### _module_ options
|
90
91
|
|
91
|
-
|
92
|
+
If you want to want to route everything to controllers, located at `controllers/admin/top_secret`, you can use scope with `module` param:
|
92
93
|
|
93
94
|
```ruby
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
scope module: 'admin/top_secret' do
|
96
|
+
mutation :logIn, to: 'sessions#login' # this will trigger Admin::TopSecret::SessionsController
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
### Named scope
|
98
101
|
|
102
|
+
If you want to nest some routes under some other node, you can use named scope:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
scope :admin do
|
99
106
|
mutation :logIn, to: 'sessions#login' # this will trigger ::SessionsController
|
100
107
|
end
|
101
108
|
```
|
102
109
|
|
110
|
+
This action will be accessible via:
|
111
|
+
|
112
|
+
```graphql
|
113
|
+
mutation {
|
114
|
+
admin {
|
115
|
+
logIn(email: 'john@example.com') { ... }
|
116
|
+
}
|
117
|
+
}
|
118
|
+
```
|
119
|
+
|
120
|
+
## _namespace_
|
121
|
+
|
122
|
+
You may wish to organize groups of controllers under a namespace. Most commonly, you might group a number of administrative controllers under an `Admin::` namespace, and place these controllers under the app/controllers/admin directory. You can route to such a group by using a namespace block:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
namespace :admin do
|
126
|
+
resources :articles, only: :show
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
On GraphQL side, you can reach such route with the following query:
|
131
|
+
|
132
|
+
```graphql
|
133
|
+
query {
|
134
|
+
admin {
|
135
|
+
article(id: '123') { ... }
|
136
|
+
}
|
137
|
+
}
|
138
|
+
```
|
139
|
+
|
103
140
|
## _group_
|
104
141
|
|
105
142
|
You can have multiple routers / schemas. In order to add resources or query only to specific schema, you need wrap it with `group` method, like this:
|
@@ -32,8 +32,8 @@ module GraphqlRails
|
|
32
32
|
[
|
33
33
|
field_name,
|
34
34
|
type_parser.type_arg,
|
35
|
-
|
36
|
-
]
|
35
|
+
description
|
36
|
+
].compact
|
37
37
|
end
|
38
38
|
|
39
39
|
def field_options
|
@@ -41,21 +41,11 @@ module GraphqlRails
|
|
41
41
|
method: property.to_sym,
|
42
42
|
null: optional?,
|
43
43
|
camelize: camelize?,
|
44
|
-
groups: groups
|
44
|
+
groups: groups,
|
45
|
+
**deprecation_reason_params
|
45
46
|
}
|
46
47
|
end
|
47
48
|
|
48
|
-
def argument_args
|
49
|
-
[
|
50
|
-
field_name,
|
51
|
-
type_parser.type_arg,
|
52
|
-
{
|
53
|
-
description: description,
|
54
|
-
required: required?
|
55
|
-
}
|
56
|
-
]
|
57
|
-
end
|
58
|
-
|
59
49
|
protected
|
60
50
|
|
61
51
|
attr_reader :initial_name
|
@@ -65,6 +55,10 @@ module GraphqlRails
|
|
65
55
|
def camelize?
|
66
56
|
options[:input_format] != :original && options[:attribute_name_format] != :original
|
67
57
|
end
|
58
|
+
|
59
|
+
def deprecation_reason_params
|
60
|
+
{ deprecation_reason: deprecation_reason }.compact
|
61
|
+
end
|
68
62
|
end
|
69
63
|
end
|
70
64
|
end
|
@@ -40,6 +40,21 @@ module GraphqlRails
|
|
40
40
|
def optional(new_value = true) # rubocop:disable Style/OptionalBooleanParameter
|
41
41
|
required(!new_value)
|
42
42
|
end
|
43
|
+
|
44
|
+
def deprecated(reason = 'Deprecated')
|
45
|
+
@deprecation_reason = \
|
46
|
+
if [false, nil].include?(reason)
|
47
|
+
nil
|
48
|
+
else
|
49
|
+
reason.is_a?(String) ? reason : 'Deprecated'
|
50
|
+
end
|
51
|
+
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def deprecation_reason
|
56
|
+
@deprecation_reason
|
57
|
+
end
|
43
58
|
end
|
44
59
|
end
|
45
60
|
end
|
@@ -12,6 +12,7 @@ module GraphqlRails
|
|
12
12
|
|
13
13
|
chainable_option :subtype
|
14
14
|
chainable_option :enum
|
15
|
+
chainable_option :default_value
|
15
16
|
|
16
17
|
def initialize(name, config:)
|
17
18
|
@config = config
|
@@ -25,7 +26,14 @@ module GraphqlRails
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def input_argument_options
|
28
|
-
{
|
29
|
+
{
|
30
|
+
required: required?,
|
31
|
+
description: description,
|
32
|
+
camelize: false,
|
33
|
+
groups: groups,
|
34
|
+
**default_value_option,
|
35
|
+
**deprecation_reason_params
|
36
|
+
}
|
29
37
|
end
|
30
38
|
|
31
39
|
def paginated?
|
@@ -36,12 +44,20 @@ module GraphqlRails
|
|
36
44
|
|
37
45
|
attr_reader :initial_name, :config
|
38
46
|
|
47
|
+
def default_value_option
|
48
|
+
{ default_value: default_value }.compact
|
49
|
+
end
|
50
|
+
|
39
51
|
def attribute_name_parser
|
40
52
|
@attribute_name_parser ||= AttributeNameParser.new(
|
41
53
|
initial_name, options: attribute_naming_options
|
42
54
|
)
|
43
55
|
end
|
44
56
|
|
57
|
+
def deprecation_reason_params
|
58
|
+
{ deprecation_reason: deprecation_reason }.compact
|
59
|
+
end
|
60
|
+
|
45
61
|
def attribute_naming_options
|
46
62
|
options.slice(:input_format)
|
47
63
|
end
|
@@ -52,12 +52,6 @@ module GraphqlRails
|
|
52
52
|
GraphQL::InputObjectType
|
53
53
|
].freeze
|
54
54
|
|
55
|
-
PARSEABLE_RAW_GRAPHQL_TYPES = [
|
56
|
-
GraphQL::Schema::Object,
|
57
|
-
GraphQL::Schema::Scalar,
|
58
|
-
GraphQL::Schema::Enum
|
59
|
-
].freeze
|
60
|
-
|
61
55
|
RAW_GRAPHQL_TYPES = (WRAPPER_TYPES + GRAPHQL_BASE_TYPES).freeze
|
62
56
|
|
63
57
|
def unwrapped_scalar_type
|
@@ -103,7 +97,7 @@ module GraphqlRails
|
|
103
97
|
def graphql_type_object?(type_class)
|
104
98
|
return false unless type_class.is_a?(Class)
|
105
99
|
|
106
|
-
|
100
|
+
type_class < GraphQL::Schema::Member
|
107
101
|
end
|
108
102
|
|
109
103
|
def applicable_graphql_type?(type)
|
@@ -19,6 +19,8 @@ module GraphqlRails
|
|
19
19
|
action = build_action
|
20
20
|
|
21
21
|
Class.new(ControllerActionResolver) do
|
22
|
+
graphql_name("ControllerActionResolver#{SecureRandom.hex}")
|
23
|
+
|
22
24
|
type(*action.type_args, **action.type_options)
|
23
25
|
description(action.description)
|
24
26
|
controller(action.controller)
|
@@ -62,9 +62,14 @@ module GraphqlRails
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def parameter_filter_class
|
65
|
-
|
65
|
+
if ActiveSupport.gem_version.segments.first < 6
|
66
|
+
return ActiveSupport::ParameterFilter if Object.const_defined?('ActiveSupport::ParameterFilter')
|
66
67
|
|
67
|
-
|
68
|
+
ActionDispatch::Http::ParameterFilter
|
69
|
+
else
|
70
|
+
require 'active_support/parameter_filter'
|
71
|
+
ActiveSupport::ParameterFilter
|
72
|
+
end
|
68
73
|
end
|
69
74
|
end
|
70
75
|
end
|
@@ -12,40 +12,41 @@ module GraphqlRails
|
|
12
12
|
defined?(Mongoid) && object.is_a?(Mongoid::Criteria)
|
13
13
|
end
|
14
14
|
|
15
|
-
def initialize(decorator:, relation:, decorator_args: [])
|
15
|
+
def initialize(decorator:, relation:, decorator_args: [], decorator_kwargs: {})
|
16
16
|
@relation = relation
|
17
17
|
@decorator = decorator
|
18
18
|
@decorator_args = decorator_args
|
19
|
+
@decorator_kwargs = decorator_kwargs
|
19
20
|
end
|
20
21
|
|
21
22
|
%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)
|
23
|
+
define_method method_name do |*args, **kwargs, &block|
|
24
|
+
chainable_method(method_name, *args, **kwargs, &block)
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
28
|
%i[first second last find find_by].each do |method_name|
|
28
|
-
define_method method_name do |*args, &block|
|
29
|
-
decoratable_object_method(method_name, *args, &block)
|
29
|
+
define_method method_name do |*args, **kwargs, &block|
|
30
|
+
decoratable_object_method(method_name, *args, **kwargs, &block)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
34
|
%i[find_each].each do |method_name|
|
34
|
-
define_method method_name do |*args, &block|
|
35
|
-
decoratable_block_method(method_name, *args, &block)
|
35
|
+
define_method method_name do |*args, **kwargs, &block|
|
36
|
+
decoratable_block_method(method_name, *args, **kwargs, &block)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
40
|
def to_a
|
40
|
-
@to_a ||= relation.to_a.map { |it| decorator.new(it, *decorator_args) }
|
41
|
+
@to_a ||= relation.to_a.map { |it| decorator.new(it, *decorator_args, **decorator_kwargs) }
|
41
42
|
end
|
42
43
|
|
43
44
|
private
|
44
45
|
|
45
|
-
attr_reader :relation, :decorator, :decorator_args
|
46
|
+
attr_reader :relation, :decorator, :decorator_args, :decorator_kwargs
|
46
47
|
|
47
|
-
def decoratable_object_method(method_name, *args, &block)
|
48
|
-
object = relation.public_send(method_name, *args, &block)
|
48
|
+
def decoratable_object_method(method_name, *args, **kwargs, &block)
|
49
|
+
object = relation.public_send(method_name, *args, **kwargs, &block)
|
49
50
|
decorate(object)
|
50
51
|
end
|
51
52
|
|
@@ -53,22 +54,25 @@ module GraphqlRails
|
|
53
54
|
return object_or_list if object_or_list.blank?
|
54
55
|
|
55
56
|
if object_or_list.is_a?(Array)
|
56
|
-
object_or_list.map { |it| decorator.new(it, *decorator_args) }
|
57
|
+
object_or_list.map { |it| decorator.new(it, *decorator_args, **decorator_kwargs) }
|
57
58
|
else
|
58
|
-
decorator.new(object_or_list, *decorator_args)
|
59
|
+
decorator.new(object_or_list, *decorator_args, **decorator_kwargs)
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
62
|
-
def decoratable_block_method(method_name, *args)
|
63
|
-
relation.public_send(method_name, *args) do |object, *other_args|
|
63
|
+
def decoratable_block_method(method_name, *args, **kwargs)
|
64
|
+
relation.public_send(method_name, *args, **kwargs) do |object, *other_args|
|
64
65
|
decorated_object = decorate(object)
|
65
66
|
yield(decorated_object, *other_args)
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
69
|
-
def chainable_method(method_name, *args, &block)
|
70
|
-
new_relation = relation.public_send(method_name, *args, &block)
|
71
|
-
self.class.new(
|
70
|
+
def chainable_method(method_name, *args, **kwargs, &block)
|
71
|
+
new_relation = relation.public_send(method_name, *args, **kwargs, &block)
|
72
|
+
self.class.new(
|
73
|
+
decorator: decorator, relation: new_relation,
|
74
|
+
decorator_args: decorator_args, decorator_kwargs: decorator_kwargs
|
75
|
+
)
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|
@@ -25,17 +25,25 @@ module GraphqlRails
|
|
25
25
|
extend ActiveSupport::Concern
|
26
26
|
|
27
27
|
class_methods do
|
28
|
-
def decorate(object, *args)
|
28
|
+
def decorate(object, *args, **kwargs)
|
29
29
|
if Decorator::RelationDecorator.decorates?(object)
|
30
|
-
|
30
|
+
decorate_with_relation_decorator(object, args, kwargs)
|
31
31
|
elsif object.nil?
|
32
32
|
nil
|
33
33
|
elsif object.is_a?(Array)
|
34
|
-
object.map { |item| new(item, *args) }
|
34
|
+
object.map { |item| new(item, *args, **kwargs) }
|
35
35
|
else
|
36
|
-
new(object, *args)
|
36
|
+
new(object, *args, **kwargs)
|
37
37
|
end
|
38
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def decorate_with_relation_decorator(object, args, kwargs)
|
43
|
+
Decorator::RelationDecorator.new(
|
44
|
+
relation: object, decorator: self, decorator_args: args, decorator_kwargs: kwargs
|
45
|
+
)
|
46
|
+
end
|
39
47
|
end
|
40
48
|
end
|
41
49
|
end
|
@@ -1,8 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GraphqlRails
|
4
|
-
#
|
4
|
+
# Base class which is returned in case something bad happens. Contains all error rendering structure
|
5
5
|
class SystemError < ExecutionError
|
6
|
+
delegate :backtrace, to: :original_error
|
7
|
+
|
8
|
+
attr_reader :original_error
|
9
|
+
|
10
|
+
def initialize(original_error)
|
11
|
+
super(original_error.message)
|
12
|
+
|
13
|
+
@original_error = original_error
|
14
|
+
end
|
15
|
+
|
6
16
|
def to_h
|
7
17
|
super.except('locations')
|
8
18
|
end
|
@@ -3,10 +3,12 @@
|
|
3
3
|
module GraphqlRails
|
4
4
|
# GraphQL error that is raised when invalid data is given
|
5
5
|
class ValidationError < ExecutionError
|
6
|
+
BASE_FIELD_NAME = 'base'
|
7
|
+
|
6
8
|
attr_reader :short_message, :field
|
7
9
|
|
8
10
|
def initialize(short_message, field)
|
9
|
-
super([field
|
11
|
+
super([humanized_field(field), short_message].compact.join(' '))
|
10
12
|
@short_message = short_message
|
11
13
|
@field = field
|
12
14
|
end
|
@@ -18,5 +20,16 @@ module GraphqlRails
|
|
18
20
|
def to_h
|
19
21
|
super.merge('field' => field, 'short_message' => short_message)
|
20
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def humanized_field(field)
|
27
|
+
return if field.blank?
|
28
|
+
|
29
|
+
stringified_field = field.to_s
|
30
|
+
return if stringified_field == BASE_FIELD_NAME
|
31
|
+
|
32
|
+
stringified_field.humanize
|
33
|
+
end
|
21
34
|
end
|
22
35
|
end
|
@@ -46,11 +46,7 @@ module GraphqlRails
|
|
46
46
|
|
47
47
|
def find_or_build_dynamic_type(attribute)
|
48
48
|
graphql_model = attribute.graphql_model
|
49
|
-
if graphql_model
|
50
|
-
find_or_build_graphql_model_type(graphql_model)
|
51
|
-
else
|
52
|
-
AddFieldsToGraphqlType.call(klass: klass, attributes: [attribute])
|
53
|
-
end
|
49
|
+
find_or_build_graphql_model_type(graphql_model) if graphql_model
|
54
50
|
end
|
55
51
|
|
56
52
|
def find_or_build_graphql_model_type(graphql_model)
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
class Router
|
5
|
+
# Builds GraphQL type used in graphql schema
|
6
|
+
class BuildSchemaActionType
|
7
|
+
ROUTES_KEY = :__routes__
|
8
|
+
|
9
|
+
# @private
|
10
|
+
class SchemaActionType < GraphQL::Schema::Object
|
11
|
+
def self.inspect
|
12
|
+
"#{GraphQL::Schema::Object}(#{graphql_name})"
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def fields_for_nested_routes(type_name_prefix:, scoped_routes:)
|
17
|
+
routes_by_scope = scoped_routes.dup
|
18
|
+
unscoped_routes = routes_by_scope.delete(ROUTES_KEY) || []
|
19
|
+
|
20
|
+
scoped_only_fields(type_name_prefix, routes_by_scope)
|
21
|
+
unscoped_routes.each { route_field(_1) }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def route_field(route)
|
27
|
+
field(*route.name, **route.field_options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def scoped_only_fields(type_name_prefix, routes_by_scope)
|
31
|
+
routes_by_scope.each_pair do |scope_name, inner_scope_routes|
|
32
|
+
scope_field(scope_name, "#{type_name_prefix}#{scope_name.to_s.camelize}", inner_scope_routes)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def scope_field(scope_name, scope_type_name, scoped_routes)
|
37
|
+
scope_type = build_scope_type_class(
|
38
|
+
type_name: scope_type_name,
|
39
|
+
scoped_routes: scoped_routes
|
40
|
+
)
|
41
|
+
|
42
|
+
field(scope_name.to_s.camelize(:lower), scope_type, null: false)
|
43
|
+
define_method(scope_type_name.underscore) { self }
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_scope_type_class(type_name:, scoped_routes:)
|
47
|
+
Class.new(SchemaActionType) do
|
48
|
+
graphql_name("#{type_name}Scope")
|
49
|
+
|
50
|
+
fields_for_nested_routes(
|
51
|
+
type_name_prefix: type_name,
|
52
|
+
scoped_routes: scoped_routes
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.call(**kwargs)
|
60
|
+
new(**kwargs).call
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(type_name:, routes:)
|
64
|
+
@type_name = type_name
|
65
|
+
@routes = routes
|
66
|
+
end
|
67
|
+
|
68
|
+
def call
|
69
|
+
type_name = self.type_name
|
70
|
+
scoped_routes = self.scoped_routes
|
71
|
+
|
72
|
+
Class.new(SchemaActionType) do
|
73
|
+
graphql_name(type_name)
|
74
|
+
|
75
|
+
fields_for_nested_routes(
|
76
|
+
type_name_prefix: type_name,
|
77
|
+
scoped_routes: scoped_routes
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
attr_reader :type_name, :routes
|
85
|
+
|
86
|
+
def scoped_routes
|
87
|
+
routes.each_with_object({}) do |route, result|
|
88
|
+
scope_names = route.scope_names.map { _1.to_s.camelize(:lower) }
|
89
|
+
path_to_routes = scope_names + [ROUTES_KEY]
|
90
|
+
deep_append(result, path_to_routes, route)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# adds array element to nested hash
|
95
|
+
# usage:
|
96
|
+
# deep_hash = { a: { b: [1] } }
|
97
|
+
# deep_append(deep_hash, [:a, :b], 2)
|
98
|
+
# deep_hash #=> { a: { b: [1, 2] } }
|
99
|
+
def deep_append(hash, keys, value)
|
100
|
+
deepest_hash = hash
|
101
|
+
*other_keys, last_key = keys
|
102
|
+
|
103
|
+
other_keys.each do |key|
|
104
|
+
deepest_hash[key] ||= {}
|
105
|
+
deepest_hash = deepest_hash[key]
|
106
|
+
end
|
107
|
+
deepest_hash[last_key] ||= []
|
108
|
+
deepest_hash[last_key] += [value]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -28,6 +28,10 @@ module GraphqlRails
|
|
28
28
|
routes << build_mutation(*args, **kwargs)
|
29
29
|
end
|
30
30
|
|
31
|
+
def subscription(*args, **kwargs)
|
32
|
+
routes << build_subscription(*args, **kwargs)
|
33
|
+
end
|
34
|
+
|
31
35
|
private
|
32
36
|
|
33
37
|
attr_reader :autogenerated_action_names, :name, :options
|
@@ -62,6 +66,10 @@ module GraphqlRails
|
|
62
66
|
build_route(QueryRoute, *args, **kwargs)
|
63
67
|
end
|
64
68
|
|
69
|
+
def build_subscription(*args, **kwargs)
|
70
|
+
build_route(SubscriptionRoute, *args, **kwargs)
|
71
|
+
end
|
72
|
+
|
65
73
|
# rubocop:disable Metrics/ParameterLists
|
66
74
|
def build_route(builder, action, prefix: action, suffix: false, on: :member, **custom_options)
|
67
75
|
if suffix == true
|
@@ -6,15 +6,16 @@ module GraphqlRails
|
|
6
6
|
class Router
|
7
7
|
# Generic class for any type graphql action. Should not be used directly
|
8
8
|
class Route
|
9
|
-
attr_reader :name, :module_name, :on, :relative_path, :groups
|
9
|
+
attr_reader :name, :module_name, :on, :relative_path, :groups, :scope_names
|
10
10
|
|
11
|
-
def initialize(name, to: '',
|
11
|
+
def initialize(name, on:, to: '', groups: nil, scope_names: [], **options) # rubocop:disable Metrics/ParameterLists
|
12
12
|
@name = name.to_s.camelize(:lower)
|
13
13
|
@module_name = options[:module].to_s
|
14
14
|
@function = options[:function]
|
15
15
|
@groups = groups
|
16
16
|
@relative_path = to
|
17
17
|
@on = on.to_sym
|
18
|
+
@scope_names = scope_names
|
18
19
|
end
|
19
20
|
|
20
21
|
def path
|