graphiti 1.2.16 → 1.3.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +96 -0
- data/.standard.yml +4 -4
- data/Appraisals +23 -17
- data/CHANGELOG.md +7 -1
- data/Guardfile +5 -5
- data/deprecated_generators/graphiti/generator_mixin.rb +1 -0
- data/deprecated_generators/graphiti/resource_generator.rb +1 -1
- data/gemfiles/{rails_5.gemfile → rails_5_2.gemfile} +2 -2
- data/gemfiles/{rails_5_graphiti_rails.gemfile → rails_5_2_graphiti_rails.gemfile} +3 -4
- data/gemfiles/rails_6.gemfile +1 -1
- data/gemfiles/rails_6_graphiti_rails.gemfile +2 -3
- data/gemfiles/{rails_4.gemfile → rails_7.gemfile} +2 -2
- data/gemfiles/rails_7_graphiti_rails.gemfile +19 -0
- data/graphiti.gemspec +16 -16
- data/lib/graphiti/adapters/abstract.rb +20 -5
- data/lib/graphiti/adapters/active_record/belongs_to_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/has_many_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/has_one_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/{inferrence.rb → inference.rb} +2 -2
- data/lib/graphiti/adapters/active_record/many_to_many_sideload.rb +19 -0
- data/lib/graphiti/adapters/active_record.rb +119 -74
- data/lib/graphiti/adapters/graphiti_api.rb +1 -1
- data/lib/graphiti/adapters/null.rb +1 -1
- data/lib/graphiti/adapters/persistence/associations.rb +78 -0
- data/lib/graphiti/configuration.rb +3 -1
- data/lib/graphiti/debugger.rb +12 -8
- data/lib/graphiti/delegates/pagination.rb +47 -13
- data/lib/graphiti/deserializer.rb +3 -3
- data/lib/graphiti/errors.rb +109 -15
- data/lib/graphiti/extensions/extra_attribute.rb +4 -4
- data/lib/graphiti/extensions/temp_id.rb +1 -1
- data/lib/graphiti/filter_operators.rb +0 -1
- data/lib/graphiti/hash_renderer.rb +198 -21
- data/lib/graphiti/query.rb +105 -73
- data/lib/graphiti/railtie.rb +5 -5
- data/lib/graphiti/renderer.rb +19 -1
- data/lib/graphiti/request_validator.rb +10 -10
- data/lib/graphiti/request_validators/update_validator.rb +4 -5
- data/lib/graphiti/request_validators/validator.rb +38 -24
- data/lib/graphiti/resource/configuration.rb +35 -7
- data/lib/graphiti/resource/dsl.rb +34 -8
- data/lib/graphiti/resource/interface.rb +13 -3
- data/lib/graphiti/resource/links.rb +3 -3
- data/lib/graphiti/resource/persistence.rb +2 -1
- data/lib/graphiti/resource/polymorphism.rb +8 -2
- data/lib/graphiti/resource/remote.rb +2 -2
- data/lib/graphiti/resource/sideloading.rb +4 -4
- data/lib/graphiti/resource.rb +12 -1
- data/lib/graphiti/resource_proxy.rb +23 -3
- data/lib/graphiti/runner.rb +5 -5
- data/lib/graphiti/schema.rb +36 -11
- data/lib/graphiti/schema_diff.rb +44 -4
- data/lib/graphiti/scope.rb +8 -10
- data/lib/graphiti/scoping/base.rb +3 -3
- data/lib/graphiti/scoping/filter.rb +36 -15
- data/lib/graphiti/scoping/filter_group_validator.rb +78 -0
- data/lib/graphiti/scoping/paginate.rb +47 -3
- data/lib/graphiti/scoping/sort.rb +5 -7
- data/lib/graphiti/serializer.rb +49 -7
- data/lib/graphiti/sideload/belongs_to.rb +1 -1
- data/lib/graphiti/sideload/has_many.rb +19 -1
- data/lib/graphiti/sideload/many_to_many.rb +11 -4
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +3 -4
- data/lib/graphiti/sideload.rb +47 -23
- data/lib/graphiti/stats/dsl.rb +0 -1
- data/lib/graphiti/stats/payload.rb +12 -9
- data/lib/graphiti/types.rb +15 -15
- data/lib/graphiti/util/attribute_check.rb +1 -1
- data/lib/graphiti/util/class.rb +6 -0
- data/lib/graphiti/util/link.rb +10 -2
- data/lib/graphiti/util/persistence.rb +21 -78
- data/lib/graphiti/util/relationship_payload.rb +4 -4
- data/lib/graphiti/util/remote_params.rb +9 -4
- data/lib/graphiti/util/remote_serializer.rb +1 -0
- data/lib/graphiti/util/serializer_attributes.rb +41 -11
- data/lib/graphiti/util/simple_errors.rb +4 -4
- data/lib/graphiti/util/transaction_hooks_recorder.rb +1 -1
- data/lib/graphiti/version.rb +1 -1
- data/lib/graphiti.rb +6 -3
- metadata +46 -37
- data/.travis.yml +0 -59
data/lib/graphiti/query.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Graphiti
|
2
2
|
class Query
|
3
|
-
attr_reader :resource, :association_name, :params
|
3
|
+
attr_reader :resource, :association_name, :params, :action
|
4
4
|
|
5
|
-
def initialize(resource, params, association_name = nil, nested_include = nil, parents = [])
|
5
|
+
def initialize(resource, params, association_name = nil, nested_include = nil, parents = [], action = nil)
|
6
6
|
@resource = resource
|
7
7
|
@association_name = association_name
|
8
8
|
@params = params
|
@@ -11,6 +11,7 @@ module Graphiti
|
|
11
11
|
@params = @params.deep_symbolize_keys
|
12
12
|
@include_param = nested_include || @params[:include]
|
13
13
|
@parents = parents
|
14
|
+
@action = parse_action(action)
|
14
15
|
end
|
15
16
|
|
16
17
|
def association?
|
@@ -31,7 +32,9 @@ module Graphiti
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def pagination_links?
|
34
|
-
if
|
35
|
+
if action == :find
|
36
|
+
false
|
37
|
+
elsif Graphiti.config.pagination_links_on_demand
|
35
38
|
[true, "true"].include?(@params[:pagination_links])
|
36
39
|
else
|
37
40
|
Graphiti.config.pagination_links
|
@@ -44,16 +47,19 @@ module Graphiti
|
|
44
47
|
|
45
48
|
def hash
|
46
49
|
@hash ||= {}.tap do |h|
|
47
|
-
h[:filter] = filters
|
48
|
-
h[:sort] = sorts
|
49
|
-
h[:page] = pagination
|
50
|
-
|
51
|
-
|
52
|
-
h[:extra_fields] = extra_fields
|
50
|
+
h[:filter] = filters
|
51
|
+
h[:sort] = sorts
|
52
|
+
h[:page] = pagination
|
53
|
+
if association?
|
54
|
+
resource_type = @resource.class.type
|
55
|
+
h[:extra_fields] = {resource_type => extra_fields[resource_type]} if extra_fields.key?(resource_type)
|
56
|
+
else
|
57
|
+
h[:fields] = fields
|
58
|
+
h[:extra_fields] = extra_fields
|
53
59
|
end
|
54
|
-
h[:stats] = stats
|
55
|
-
h[:include] = sideload_hash
|
56
|
-
end
|
60
|
+
h[:stats] = stats
|
61
|
+
h[:include] = sideload_hash
|
62
|
+
end.reject { |_, value| value.empty? }
|
57
63
|
end
|
58
64
|
|
59
65
|
def zero_results?
|
@@ -63,11 +69,9 @@ module Graphiti
|
|
63
69
|
end
|
64
70
|
|
65
71
|
def sideload_hash
|
66
|
-
@sideload_hash =
|
67
|
-
|
68
|
-
|
69
|
-
hash[key] = sideloads[key].hash
|
70
|
-
end
|
72
|
+
@sideload_hash = {}.tap do |hash|
|
73
|
+
sideloads.each_pair do |key, value|
|
74
|
+
hash[key] = sideloads[key].hash
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
@@ -83,19 +87,28 @@ module Graphiti
|
|
83
87
|
end
|
84
88
|
|
85
89
|
def sideloads
|
86
|
-
@sideloads ||=
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
90
|
+
@sideloads ||= {}.tap do |hash|
|
91
|
+
include_hash.each_pair do |key, sub_hash|
|
92
|
+
sideload = @resource.class.sideload(key)
|
93
|
+
|
94
|
+
if sideload || @resource.remote?
|
95
|
+
sl_resource = resource_for_sideload(sideload)
|
96
|
+
query_parents = parents + [self]
|
97
|
+
sub_hash = sub_hash[:include] if sub_hash.key?(:include)
|
98
|
+
|
99
|
+
# NB: To handle on__<type>--<name>
|
100
|
+
# A) relationship_name == :positions
|
101
|
+
# B) key == on__employees.positions
|
102
|
+
# This way A) ensures sideloads are resolved
|
103
|
+
# And B) ensures nested filters, sorts etc still work
|
104
|
+
relationship_name = sideload ? sideload.name : key
|
105
|
+
hash[relationship_name] = Query.new sl_resource,
|
106
|
+
@params,
|
107
|
+
key,
|
108
|
+
sub_hash,
|
109
|
+
query_parents, :all
|
110
|
+
else
|
111
|
+
handle_missing_sideload(key)
|
99
112
|
end
|
100
113
|
end
|
101
114
|
end
|
@@ -120,27 +133,25 @@ module Graphiti
|
|
120
133
|
end
|
121
134
|
|
122
135
|
def filters
|
123
|
-
@filters ||=
|
124
|
-
{}.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
hash[filter_name] = filter_value
|
135
|
-
end
|
136
|
+
@filters ||= {}.tap do |hash|
|
137
|
+
(@params[:filter] || {}).each_pair do |name, value|
|
138
|
+
name = name.to_sym
|
139
|
+
|
140
|
+
if legacy_nested?(name)
|
141
|
+
value.keys.each do |key|
|
142
|
+
filter_name = key.to_sym
|
143
|
+
filter_value = value[key]
|
144
|
+
|
145
|
+
if @resource.get_attr!(filter_name, :filterable, request: true)
|
146
|
+
hash[filter_name] = filter_value
|
136
147
|
end
|
137
|
-
elsif nested?(name)
|
138
|
-
name = name.to_s.split(".").last.to_sym
|
139
|
-
validate!(name, :filterable)
|
140
|
-
hash[name] = value
|
141
|
-
elsif top_level? && validate!(name, :filterable)
|
142
|
-
hash[name] = value
|
143
148
|
end
|
149
|
+
elsif nested?(name)
|
150
|
+
name = name.to_s.split(".").last.to_sym
|
151
|
+
validate!(name, :filterable)
|
152
|
+
hash[name] = value
|
153
|
+
elsif top_level? && validate!(name, :filterable)
|
154
|
+
hash[name] = value
|
144
155
|
end
|
145
156
|
end
|
146
157
|
end
|
@@ -169,18 +180,17 @@ module Graphiti
|
|
169
180
|
end
|
170
181
|
|
171
182
|
def pagination
|
172
|
-
@pagination ||=
|
173
|
-
{}.
|
174
|
-
(
|
175
|
-
|
176
|
-
|
177
|
-
hash[k.to_sym] = v.to_i
|
178
|
-
end
|
179
|
-
elsif nested?(name)
|
180
|
-
hash[name.to_s.split(".").last.to_sym] = value
|
181
|
-
elsif top_level? && [:number, :size].include?(name.to_sym)
|
182
|
-
hash[name.to_sym] = value.to_i
|
183
|
+
@pagination ||= {}.tap do |hash|
|
184
|
+
(@params[:page] || {}).each_pair do |name, value|
|
185
|
+
if legacy_nested?(name)
|
186
|
+
value.each_pair do |k, v|
|
187
|
+
hash[k.to_sym] = cast_page_param(k.to_sym, v)
|
183
188
|
end
|
189
|
+
elsif nested?(name)
|
190
|
+
param_name = name.to_s.split(".").last.to_sym
|
191
|
+
hash[param_name] = cast_page_param(param_name, value)
|
192
|
+
elsif top_level? && Scoping::Paginate::PARAMS.include?(name.to_sym)
|
193
|
+
hash[name.to_sym] = cast_page_param(name.to_sym, value)
|
184
194
|
end
|
185
195
|
end
|
186
196
|
end
|
@@ -203,15 +213,13 @@ module Graphiti
|
|
203
213
|
end
|
204
214
|
|
205
215
|
def stats
|
206
|
-
@stats ||=
|
207
|
-
{}.
|
208
|
-
(
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
hash[k.to_sym] = Array(v).flatten.map(&:to_sym)
|
214
|
-
end
|
216
|
+
@stats ||= {}.tap do |hash|
|
217
|
+
(@params[:stats] || {}).each_pair do |k, v|
|
218
|
+
if legacy_nested?(k)
|
219
|
+
raise NotImplementedError.new("Association statistics are not currently supported")
|
220
|
+
elsif top_level?
|
221
|
+
v = v.split(",") if v.is_a?(String)
|
222
|
+
hash[k.to_sym] = Array(v).flatten.map(&:to_sym)
|
215
223
|
end
|
216
224
|
end
|
217
225
|
end
|
@@ -223,6 +231,18 @@ module Graphiti
|
|
223
231
|
|
224
232
|
private
|
225
233
|
|
234
|
+
def cast_page_param(name, value)
|
235
|
+
if [:before, :after].include?(name)
|
236
|
+
decode_cursor(value)
|
237
|
+
else
|
238
|
+
value.to_i
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def decode_cursor(cursor)
|
243
|
+
JSON.parse(Base64.decode64(cursor)).symbolize_keys
|
244
|
+
end
|
245
|
+
|
226
246
|
# Try to find on this resource
|
227
247
|
# If not there, follow the legacy logic of scalling all other
|
228
248
|
# resource names/types
|
@@ -232,7 +252,7 @@ module Graphiti
|
|
232
252
|
return true if @resource.remote?
|
233
253
|
|
234
254
|
if (att = @resource.get_attr(name, flag, request: true))
|
235
|
-
|
255
|
+
att
|
236
256
|
else
|
237
257
|
not_associated_name = !@resource.class.association_names.include?(name)
|
238
258
|
not_associated_type = !@resource.class.association_types.include?(name)
|
@@ -262,8 +282,8 @@ module Graphiti
|
|
262
282
|
def parse_fieldset(fieldset)
|
263
283
|
{}.tap do |hash|
|
264
284
|
fieldset.each_pair do |type, fields|
|
265
|
-
type
|
266
|
-
fields
|
285
|
+
type = type.to_sym
|
286
|
+
fields = fields.split(",") unless fields.is_a?(Array)
|
267
287
|
hash[type] = fields.map(&:to_sym)
|
268
288
|
end
|
269
289
|
end
|
@@ -282,7 +302,7 @@ module Graphiti
|
|
282
302
|
|
283
303
|
def sort_hash(attr)
|
284
304
|
value = attr[0] == "-" ? :desc : :asc
|
285
|
-
key
|
305
|
+
key = attr.sub("-", "").to_sym
|
286
306
|
|
287
307
|
{key => value}
|
288
308
|
end
|
@@ -312,5 +332,17 @@ module Graphiti
|
|
312
332
|
end
|
313
333
|
end
|
314
334
|
end
|
335
|
+
|
336
|
+
def parse_action(action)
|
337
|
+
action ||= @params.fetch(:action, Graphiti.context[:namespace]).try(:to_sym)
|
338
|
+
case action
|
339
|
+
when :index
|
340
|
+
:all
|
341
|
+
when :show
|
342
|
+
:find
|
343
|
+
else
|
344
|
+
action
|
345
|
+
end
|
346
|
+
end
|
315
347
|
end
|
316
348
|
end
|
data/lib/graphiti/railtie.rb
CHANGED
@@ -6,7 +6,7 @@ module Graphiti
|
|
6
6
|
end
|
7
7
|
|
8
8
|
generators do
|
9
|
-
Dir[File.expand_path("../../deprecated_generators/**/*.rb", __dir__)].each do |f|
|
9
|
+
Dir[File.expand_path("../../deprecated_generators/**/*.rb", __dir__)].sort.each do |f|
|
10
10
|
require f
|
11
11
|
end
|
12
12
|
end
|
@@ -110,10 +110,10 @@ module Graphiti
|
|
110
110
|
end
|
111
111
|
|
112
112
|
route = begin
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
113
|
+
::Rails.application.routes.recognize_path(path, method: method)
|
114
|
+
rescue
|
115
|
+
nil
|
116
|
+
end
|
117
117
|
"#{route[:controller]}_controller".classify.safe_constantize if route
|
118
118
|
}
|
119
119
|
end
|
data/lib/graphiti/renderer.rb
CHANGED
@@ -17,8 +17,20 @@ module Graphiti
|
|
17
17
|
render(self.class.jsonapi_renderer).to_json
|
18
18
|
end
|
19
19
|
|
20
|
+
def as_graphql
|
21
|
+
render(self.class.graphql_renderer(@proxy))
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_graphql
|
25
|
+
as_graphql.to_json
|
26
|
+
end
|
27
|
+
|
20
28
|
def to_json
|
21
|
-
|
29
|
+
as_json.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
def as_json
|
33
|
+
render(self.class.hash_renderer(@proxy))
|
22
34
|
end
|
23
35
|
|
24
36
|
def to_xml
|
@@ -35,6 +47,11 @@ module Graphiti
|
|
35
47
|
JSONAPI::Serializable::Renderer.new(implementation)
|
36
48
|
end
|
37
49
|
|
50
|
+
def self.graphql_renderer(proxy)
|
51
|
+
implementation = Graphiti::HashRenderer.new(proxy.resource, graphql: true)
|
52
|
+
JSONAPI::Serializable::Renderer.new(implementation)
|
53
|
+
end
|
54
|
+
|
38
55
|
private
|
39
56
|
|
40
57
|
def render(renderer)
|
@@ -49,6 +66,7 @@ module Graphiti
|
|
49
66
|
options[:meta] ||= proxy.meta
|
50
67
|
options[:meta][:stats] = proxy.stats unless proxy.stats.empty?
|
51
68
|
options[:meta][:debug] = Debugger.to_a if debug_json?
|
69
|
+
options[:proxy] = proxy
|
52
70
|
|
53
71
|
renderer.render(records, options)
|
54
72
|
end
|
@@ -1,23 +1,23 @@
|
|
1
1
|
module Graphiti
|
2
2
|
class RequestValidator
|
3
3
|
delegate :validate,
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
:validate!,
|
5
|
+
:errors,
|
6
|
+
:deserialized_payload,
|
7
|
+
to: :@validator
|
8
8
|
|
9
|
-
def initialize(root_resource, raw_params)
|
10
|
-
@validator = ValidatorFactory.create(root_resource, raw_params)
|
9
|
+
def initialize(root_resource, raw_params, action)
|
10
|
+
@validator = ValidatorFactory.create(root_resource, raw_params, action)
|
11
11
|
end
|
12
12
|
|
13
13
|
class ValidatorFactory
|
14
|
-
def self.create(root_resource, raw_params)
|
15
|
-
case
|
16
|
-
when
|
14
|
+
def self.create(root_resource, raw_params, action)
|
15
|
+
case action
|
16
|
+
when :update
|
17
17
|
RequestValidators::UpdateValidator
|
18
18
|
else
|
19
19
|
RequestValidators::Validator
|
20
|
-
end.new(root_resource, raw_params)
|
20
|
+
end.new(root_resource, raw_params, action)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -5,7 +5,7 @@ module Graphiti
|
|
5
5
|
if required_payload? && payload_matches_endpoint?
|
6
6
|
super
|
7
7
|
else
|
8
|
-
|
8
|
+
false
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -26,18 +26,17 @@ module Graphiti
|
|
26
26
|
[:data, :type],
|
27
27
|
[:data, :id]
|
28
28
|
].each do |required_attr|
|
29
|
-
attribute_mismatch(required_attr) unless @
|
29
|
+
attribute_mismatch(required_attr) unless @params.dig(*required_attr)
|
30
30
|
end
|
31
31
|
errors.blank?
|
32
32
|
end
|
33
33
|
|
34
34
|
def payload_matches_endpoint?
|
35
|
-
unless @
|
35
|
+
unless @params.dig(:data, :id) == @params.dig(:filter, :id)
|
36
36
|
attribute_mismatch([:data, :id])
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
meta_type = @raw_params.dig(:data, :type)
|
39
|
+
meta_type = @params.dig(:data, :type)
|
41
40
|
|
42
41
|
# NOTE: calling #to_s and comparing 2 strings is slower than
|
43
42
|
# calling #to_sym and comparing 2 symbols. But pre ruby-2.2
|
@@ -3,43 +3,47 @@ module Graphiti
|
|
3
3
|
class Validator
|
4
4
|
attr_reader :errors
|
5
5
|
|
6
|
-
def initialize(root_resource, raw_params)
|
6
|
+
def initialize(root_resource, raw_params, action)
|
7
7
|
@root_resource = root_resource
|
8
|
-
@
|
8
|
+
@params = normalized_params(raw_params)
|
9
9
|
@errors = Graphiti::Util::SimpleErrors.new(raw_params)
|
10
|
+
@action = action
|
10
11
|
end
|
11
12
|
|
12
13
|
def validate
|
14
|
+
# Right now, all requests - even reads - go through the validator
|
15
|
+
# In the future these should have their own validation logic, but
|
16
|
+
# for now we can just bypass
|
17
|
+
return true unless @params.has_key?(:data)
|
18
|
+
|
13
19
|
resource = @root_resource
|
14
|
-
|
15
|
-
|
16
|
-
|
20
|
+
|
21
|
+
if @params[:data].has_key?(:type)
|
22
|
+
if (meta_type = deserialized_payload.meta[:type].try(:to_sym))
|
23
|
+
if @root_resource.type != meta_type && @root_resource.polymorphic?
|
24
|
+
resource = @root_resource.class.resource_for_type(meta_type).new
|
25
|
+
end
|
17
26
|
end
|
18
|
-
end
|
19
27
|
|
20
|
-
|
21
|
-
|
28
|
+
typecast_attributes(resource, deserialized_payload.attributes, @action, deserialized_payload.meta[:payload_path])
|
29
|
+
process_relationships(resource, deserialized_payload.relationships, deserialized_payload.meta[:payload_path])
|
30
|
+
else
|
31
|
+
errors.add(:"data.type", :missing)
|
32
|
+
end
|
22
33
|
|
23
34
|
errors.blank?
|
24
35
|
end
|
25
36
|
|
26
37
|
def validate!
|
27
38
|
unless validate
|
28
|
-
raise @error_class || Graphiti::Errors::InvalidRequest,
|
39
|
+
raise @error_class || Graphiti::Errors::InvalidRequest, errors
|
29
40
|
end
|
30
41
|
|
31
42
|
true
|
32
43
|
end
|
33
44
|
|
34
45
|
def deserialized_payload
|
35
|
-
@deserialized_payload ||=
|
36
|
-
payload = normalized_params
|
37
|
-
if payload[:data] && payload[:data][:type]
|
38
|
-
Graphiti::Deserializer.new(payload)
|
39
|
-
else
|
40
|
-
Graphiti::Deserializer.new({})
|
41
|
-
end
|
42
|
-
end
|
46
|
+
@deserialized_payload ||= Graphiti::Deserializer.new(@params)
|
43
47
|
end
|
44
48
|
|
45
49
|
private
|
@@ -47,10 +51,10 @@ module Graphiti
|
|
47
51
|
def process_relationships(resource, relationships, payload_path)
|
48
52
|
opts = {
|
49
53
|
resource: resource,
|
50
|
-
relationships: relationships
|
54
|
+
relationships: relationships
|
51
55
|
}
|
52
56
|
|
53
|
-
Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
|
57
|
+
Graphiti::Util::RelationshipPayload.iterate(**opts) do |x|
|
54
58
|
sideload_def = x[:sideload]
|
55
59
|
|
56
60
|
unless sideload_def.writable?
|
@@ -61,13 +65,23 @@ module Graphiti
|
|
61
65
|
next
|
62
66
|
end
|
63
67
|
|
64
|
-
|
65
|
-
|
68
|
+
resource = x[:resource]
|
69
|
+
attributes = x[:attributes]
|
70
|
+
relationships = x[:relationships]
|
71
|
+
payload_path = x[:meta][:payload_path]
|
72
|
+
action = x[:meta][:method]
|
73
|
+
typecast_attributes(resource, attributes, action, payload_path)
|
74
|
+
process_relationships(resource, relationships, payload_path)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
|
69
|
-
def typecast_attributes(resource, attributes, payload_path)
|
78
|
+
def typecast_attributes(resource, attributes, action, payload_path)
|
70
79
|
attributes.each_pair do |key, value|
|
80
|
+
# Only validate id if create action, otherwise it's only used for lookup
|
81
|
+
next if action != :create &&
|
82
|
+
key == :id &&
|
83
|
+
resource.class.config[:attributes][:id][:writable] == false
|
84
|
+
|
71
85
|
begin
|
72
86
|
attributes[key] = resource.typecast(key, value, :writable)
|
73
87
|
rescue Graphiti::Errors::UnknownAttribute
|
@@ -80,8 +94,8 @@ module Graphiti
|
|
80
94
|
end
|
81
95
|
end
|
82
96
|
|
83
|
-
def normalized_params
|
84
|
-
normalized =
|
97
|
+
def normalized_params(raw_params)
|
98
|
+
normalized = raw_params
|
85
99
|
if normalized.respond_to?(:to_unsafe_h)
|
86
100
|
normalized = normalized.to_unsafe_h.deep_symbolize_keys
|
87
101
|
end
|
@@ -22,12 +22,20 @@ module Graphiti
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def type=(val)
|
25
|
-
val = val
|
25
|
+
val = val&.to_sym
|
26
26
|
if (val = super)
|
27
27
|
serializer.type(val)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
def graphql_entrypoint=(val)
|
32
|
+
if val
|
33
|
+
super(val.to_s.camelize(:lower).to_sym)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
31
39
|
# The .stat call stores a proc based on adapter
|
32
40
|
# So if we assign a new adapter, reconfigure
|
33
41
|
def adapter=(val)
|
@@ -42,7 +50,7 @@ module Graphiti
|
|
42
50
|
path: val,
|
43
51
|
full_path: val,
|
44
52
|
url: val,
|
45
|
-
actions: [:index, :show]
|
53
|
+
actions: [:index, :show]
|
46
54
|
}
|
47
55
|
end
|
48
56
|
|
@@ -82,7 +90,10 @@ module Graphiti
|
|
82
90
|
:attributes_schema_by_default,
|
83
91
|
:relationships_readable_by_default,
|
84
92
|
:relationships_writable_by_default,
|
85
|
-
:filters_accept_nil_by_default
|
93
|
+
:filters_accept_nil_by_default,
|
94
|
+
:filters_deny_empty_by_default,
|
95
|
+
:graphql_entrypoint,
|
96
|
+
:cursor_paginatable
|
86
97
|
|
87
98
|
class << self
|
88
99
|
prepend Overrides
|
@@ -96,6 +107,7 @@ module Graphiti
|
|
96
107
|
# re-assigning causes a new Class.new
|
97
108
|
klass.serializer = (klass.serializer || klass.infer_serializer_superclass)
|
98
109
|
klass.type ||= klass.infer_type
|
110
|
+
klass.graphql_entrypoint = klass.type.to_s.pluralize.to_sym
|
99
111
|
default(klass, :attributes_readable_by_default, true)
|
100
112
|
default(klass, :attributes_writable_by_default, true)
|
101
113
|
default(klass, :attributes_sortable_by_default, true)
|
@@ -104,6 +116,7 @@ module Graphiti
|
|
104
116
|
default(klass, :relationships_readable_by_default, true)
|
105
117
|
default(klass, :relationships_writable_by_default, true)
|
106
118
|
default(klass, :filters_accept_nil_by_default, false)
|
119
|
+
default(klass, :filters_deny_empty_by_default, false)
|
107
120
|
|
108
121
|
unless klass.config[:attributes][:id]
|
109
122
|
klass.attribute :id, :integer_id
|
@@ -127,7 +140,7 @@ module Graphiti
|
|
127
140
|
def get_attr(name, flag, opts = {})
|
128
141
|
defaults = {request: false}
|
129
142
|
opts = defaults.merge(opts)
|
130
|
-
new.get_attr(name, flag, opts)
|
143
|
+
new.get_attr(name, flag, **opts)
|
131
144
|
end
|
132
145
|
|
133
146
|
def abstract_class?
|
@@ -142,19 +155,20 @@ module Graphiti
|
|
142
155
|
if (@abstract_class = val)
|
143
156
|
self.serializer = nil
|
144
157
|
self.type = nil
|
158
|
+
self.graphql_entrypoint = nil
|
145
159
|
end
|
146
160
|
end
|
147
161
|
|
148
162
|
def infer_type
|
149
163
|
if name.present?
|
150
|
-
name.demodulize.
|
164
|
+
name.demodulize.sub(/.*\KResource/, "").underscore.pluralize.to_sym
|
151
165
|
else
|
152
166
|
:undefined_jsonapi_type
|
153
167
|
end
|
154
168
|
end
|
155
169
|
|
156
170
|
def infer_model
|
157
|
-
name&.
|
171
|
+
name&.sub(/.*\KResource/, "")&.safe_constantize
|
158
172
|
end
|
159
173
|
|
160
174
|
# @api private
|
@@ -186,6 +200,7 @@ module Graphiti
|
|
186
200
|
@config ||=
|
187
201
|
{
|
188
202
|
filters: {},
|
203
|
+
grouped_filters: {},
|
189
204
|
default_filters: {},
|
190
205
|
stats: {},
|
191
206
|
sort_all: nil,
|
@@ -198,6 +213,7 @@ module Graphiti
|
|
198
213
|
extra_attributes: {},
|
199
214
|
sideloads: {},
|
200
215
|
callbacks: {},
|
216
|
+
links: {}
|
201
217
|
}
|
202
218
|
end
|
203
219
|
|
@@ -221,6 +237,10 @@ module Graphiti
|
|
221
237
|
config[:filters]
|
222
238
|
end
|
223
239
|
|
240
|
+
def grouped_filters
|
241
|
+
config[:grouped_filters]
|
242
|
+
end
|
243
|
+
|
224
244
|
def sorts
|
225
245
|
config[:sorts]
|
226
246
|
end
|
@@ -236,11 +256,15 @@ module Graphiti
|
|
236
256
|
def default_filters
|
237
257
|
config[:default_filters]
|
238
258
|
end
|
259
|
+
|
260
|
+
def links
|
261
|
+
config[:links]
|
262
|
+
end
|
239
263
|
end
|
240
264
|
|
241
265
|
def get_attr!(name, flag, options = {})
|
242
266
|
options[:raise_error] = true
|
243
|
-
get_attr(name, flag, options)
|
267
|
+
get_attr(name, flag, **options)
|
244
268
|
end
|
245
269
|
|
246
270
|
def get_attr(name, flag, request: false, raise_error: false)
|
@@ -255,6 +279,10 @@ module Graphiti
|
|
255
279
|
self.class.filters
|
256
280
|
end
|
257
281
|
|
282
|
+
def grouped_filters
|
283
|
+
self.class.grouped_filters
|
284
|
+
end
|
285
|
+
|
258
286
|
def sort_all
|
259
287
|
self.class.sort_all
|
260
288
|
end
|