graphiti 1.2.44 → 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 +18 -44
- data/CHANGELOG.md +1 -0
- data/gemfiles/rails_5_2.gemfile +2 -2
- data/gemfiles/rails_5_2_graphiti_rails.gemfile +3 -4
- data/gemfiles/rails_6_graphiti_rails.gemfile +1 -2
- data/gemfiles/{rails_5_1.gemfile → rails_7.gemfile} +2 -2
- data/gemfiles/{rails_4.gemfile → rails_7_graphiti_rails.gemfile} +3 -2
- data/graphiti.gemspec +5 -5
- data/lib/graphiti/adapters/abstract.rb +5 -1
- data/lib/graphiti/adapters/active_record.rb +42 -34
- data/lib/graphiti/adapters/persistence/associations.rb +13 -15
- data/lib/graphiti/delegates/pagination.rb +14 -6
- data/lib/graphiti/errors.rb +17 -11
- data/lib/graphiti/extensions/temp_id.rb +1 -1
- data/lib/graphiti/filter_operators.rb +0 -1
- data/lib/graphiti/hash_renderer.rb +40 -2
- data/lib/graphiti/query.rb +71 -68
- data/lib/graphiti/railtie.rb +4 -4
- data/lib/graphiti/renderer.rb +1 -0
- data/lib/graphiti/request_validator.rb +1 -1
- data/lib/graphiti/resource/configuration.rb +2 -1
- data/lib/graphiti/resource/dsl.rb +14 -6
- data/lib/graphiti/resource/polymorphism.rb +1 -1
- data/lib/graphiti/resource/sideloading.rb +4 -4
- data/lib/graphiti/resource.rb +2 -1
- data/lib/graphiti/resource_proxy.rb +8 -2
- data/lib/graphiti/schema.rb +6 -4
- data/lib/graphiti/scope.rb +2 -2
- data/lib/graphiti/scoping/paginate.rb +28 -2
- data/lib/graphiti/scoping/sort.rb +4 -6
- data/lib/graphiti/serializer.rb +19 -1
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +3 -4
- data/lib/graphiti/stats/dsl.rb +0 -1
- data/lib/graphiti/util/serializer_attributes.rb +6 -0
- data/lib/graphiti/util/simple_errors.rb +3 -3
- data/lib/graphiti/version.rb +1 -1
- data/lib/graphiti.rb +1 -0
- metadata +17 -23
- data/.travis.yml +0 -94
- data/gemfiles/rails_5_0.gemfile +0 -18
- data/gemfiles/rails_5_0_graphiti_rails.gemfile +0 -20
- data/gemfiles/rails_5_1_graphiti_rails.gemfile +0 -20
@@ -101,6 +101,10 @@ module Graphiti
|
|
101
101
|
hash[:_type] = jsonapi_type.to_s
|
102
102
|
end
|
103
103
|
|
104
|
+
if (fields_list || []).include?(:_cursor)
|
105
|
+
hash[:_cursor] = cursor
|
106
|
+
end
|
107
|
+
|
104
108
|
if (fields_list || []).include?(:__typename)
|
105
109
|
resource_class = @resource.class
|
106
110
|
if polymorphic_subclass?
|
@@ -142,6 +146,10 @@ module Graphiti
|
|
142
146
|
nodes = get_nodes(serializers, opts)
|
143
147
|
add_nodes(hash, top_level_key, options, nodes, @graphql)
|
144
148
|
add_stats(hash, top_level_key, options, @graphql)
|
149
|
+
if @graphql
|
150
|
+
add_page_info(hash, serializers, top_level_key, options)
|
151
|
+
end
|
152
|
+
|
145
153
|
hash
|
146
154
|
end
|
147
155
|
|
@@ -160,7 +168,7 @@ module Graphiti
|
|
160
168
|
|
161
169
|
def get_nodes(serializers, opts)
|
162
170
|
if serializers.is_a?(Array)
|
163
|
-
serializers.map do |s|
|
171
|
+
serializers.each_with_index.map do |s, index|
|
164
172
|
s.to_hash(**opts)
|
165
173
|
end
|
166
174
|
else
|
@@ -184,12 +192,42 @@ module Graphiti
|
|
184
192
|
if options[:meta] && !options[:meta].empty?
|
185
193
|
if @graphql
|
186
194
|
if (stats = options[:meta][:stats])
|
187
|
-
|
195
|
+
camelized = {}
|
196
|
+
stats.each_pair do |key, value|
|
197
|
+
camelized[key.to_s.camelize(:lower).to_sym] = value
|
198
|
+
end
|
199
|
+
hash[top_level_key][:stats] = camelized
|
188
200
|
end
|
189
201
|
else
|
190
202
|
hash.merge!(options.slice(:meta))
|
191
203
|
end
|
192
204
|
end
|
193
205
|
end
|
206
|
+
|
207
|
+
# NB - this is only for top-level right now
|
208
|
+
# The casing here is GQL-specific, we can update later if needed.
|
209
|
+
def add_page_info(hash, serializers, top_level_key, options)
|
210
|
+
if (fields = options[:fields].try(:[], :page_info))
|
211
|
+
info = {}
|
212
|
+
|
213
|
+
if fields.include?(:has_next_page)
|
214
|
+
info[:hasNextPage] = options[:proxy].pagination.has_next_page?
|
215
|
+
end
|
216
|
+
|
217
|
+
if fields.include?(:has_previous_page)
|
218
|
+
info[:hasPreviousPage] = options[:proxy].pagination.has_previous_page?
|
219
|
+
end
|
220
|
+
|
221
|
+
if fields.include?(:start_cursor)
|
222
|
+
info[:startCursor] = serializers.first.try(:cursor)
|
223
|
+
end
|
224
|
+
|
225
|
+
if fields.include?(:end_cursor)
|
226
|
+
info[:endCursor] = serializers.last.try(:cursor)
|
227
|
+
end
|
228
|
+
|
229
|
+
hash[top_level_key][:pageInfo] = info
|
230
|
+
end
|
231
|
+
end
|
194
232
|
end
|
195
233
|
end
|
data/lib/graphiti/query.rb
CHANGED
@@ -69,11 +69,9 @@ module Graphiti
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def sideload_hash
|
72
|
-
@sideload_hash =
|
73
|
-
|
74
|
-
|
75
|
-
hash[key] = sideloads[key].hash
|
76
|
-
end
|
72
|
+
@sideload_hash = {}.tap do |hash|
|
73
|
+
sideloads.each_pair do |key, value|
|
74
|
+
hash[key] = sideloads[key].hash
|
77
75
|
end
|
78
76
|
end
|
79
77
|
end
|
@@ -89,30 +87,28 @@ module Graphiti
|
|
89
87
|
end
|
90
88
|
|
91
89
|
def sideloads
|
92
|
-
@sideloads ||=
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
handle_missing_sideload(key)
|
115
|
-
end
|
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)
|
116
112
|
end
|
117
113
|
end
|
118
114
|
end
|
@@ -137,27 +133,25 @@ module Graphiti
|
|
137
133
|
end
|
138
134
|
|
139
135
|
def filters
|
140
|
-
@filters ||=
|
141
|
-
{}.
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
hash[filter_name] = filter_value
|
152
|
-
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
|
153
147
|
end
|
154
|
-
elsif nested?(name)
|
155
|
-
name = name.to_s.split(".").last.to_sym
|
156
|
-
validate!(name, :filterable)
|
157
|
-
hash[name] = value
|
158
|
-
elsif top_level? && validate!(name, :filterable)
|
159
|
-
hash[name] = value
|
160
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
|
161
155
|
end
|
162
156
|
end
|
163
157
|
end
|
@@ -186,18 +180,17 @@ module Graphiti
|
|
186
180
|
end
|
187
181
|
|
188
182
|
def pagination
|
189
|
-
@pagination ||=
|
190
|
-
{}.
|
191
|
-
(
|
192
|
-
|
193
|
-
|
194
|
-
hash[k.to_sym] = v.to_i
|
195
|
-
end
|
196
|
-
elsif nested?(name)
|
197
|
-
hash[name.to_s.split(".").last.to_sym] = value
|
198
|
-
elsif top_level? && [:number, :size, :offset].include?(name.to_sym)
|
199
|
-
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)
|
200
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)
|
201
194
|
end
|
202
195
|
end
|
203
196
|
end
|
@@ -220,15 +213,13 @@ module Graphiti
|
|
220
213
|
end
|
221
214
|
|
222
215
|
def stats
|
223
|
-
@stats ||=
|
224
|
-
{}.
|
225
|
-
(
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
hash[k.to_sym] = Array(v).flatten.map(&:to_sym)
|
231
|
-
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)
|
232
223
|
end
|
233
224
|
end
|
234
225
|
end
|
@@ -240,6 +231,18 @@ module Graphiti
|
|
240
231
|
|
241
232
|
private
|
242
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
|
+
|
243
246
|
# Try to find on this resource
|
244
247
|
# If not there, follow the legacy logic of scalling all other
|
245
248
|
# resource names/types
|
data/lib/graphiti/railtie.rb
CHANGED
@@ -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
@@ -9,7 +9,11 @@ module Graphiti
|
|
9
9
|
opts = args.extract_options!
|
10
10
|
type_override = args[0]
|
11
11
|
|
12
|
-
if (att =
|
12
|
+
if (att = (attributes[name] || extra_attributes[name]))
|
13
|
+
# We're opting in to filtering, so force this
|
14
|
+
# UNLESS the filter is guarded at the attribute level
|
15
|
+
att[:filterable] = true if att[:filterable] == false
|
16
|
+
|
13
17
|
aliases = [name, opts[:aliases]].flatten.compact
|
14
18
|
operators = FilterOperators.build(self, att[:type], opts, &blk)
|
15
19
|
|
@@ -23,6 +27,8 @@ module Graphiti
|
|
23
27
|
end
|
24
28
|
|
25
29
|
required = att[:filterable] == :required || !!opts[:required]
|
30
|
+
schema = !!opts[:via_attribute_dsl] ? att[:schema] : opts[:schema] != false
|
31
|
+
|
26
32
|
config[:filters][name.to_sym] = {
|
27
33
|
aliases: aliases,
|
28
34
|
name: name.to_sym,
|
@@ -32,6 +38,7 @@ module Graphiti
|
|
32
38
|
single: !!opts[:single],
|
33
39
|
dependencies: opts[:dependent],
|
34
40
|
required: required,
|
41
|
+
schema: schema,
|
35
42
|
operators: operators.to_hash,
|
36
43
|
allow_nil: opts.fetch(:allow_nil, filters_accept_nil_by_default),
|
37
44
|
deny_empty: opts.fetch(:deny_empty, filters_deny_empty_by_default)
|
@@ -56,7 +63,7 @@ module Graphiti
|
|
56
63
|
end
|
57
64
|
|
58
65
|
def sort_all(&blk)
|
59
|
-
if
|
66
|
+
if blk
|
60
67
|
config[:_sort_all] = blk
|
61
68
|
else
|
62
69
|
config[:_sort_all]
|
@@ -130,7 +137,7 @@ module Graphiti
|
|
130
137
|
options[:sortable] ? sort(name) : config[:sorts].delete(name)
|
131
138
|
|
132
139
|
if options[:filterable]
|
133
|
-
filter(name, allow: options[:allow])
|
140
|
+
filter(name, allow: options[:allow], via_attribute_dsl: true)
|
134
141
|
else
|
135
142
|
config[:filters].delete(name)
|
136
143
|
end
|
@@ -144,7 +151,8 @@ module Graphiti
|
|
144
151
|
readable: true,
|
145
152
|
writable: false,
|
146
153
|
sortable: false,
|
147
|
-
filterable: false
|
154
|
+
filterable: false,
|
155
|
+
schema: true
|
148
156
|
}
|
149
157
|
options = defaults.merge(options)
|
150
158
|
attribute_option(options, :readable)
|
@@ -181,9 +189,9 @@ module Graphiti
|
|
181
189
|
def attribute_option(options, name, exclusive = false)
|
182
190
|
if options[name] != false
|
183
191
|
default = if (only = options[:only]) && !exclusive
|
184
|
-
Array(only).include?(name)
|
192
|
+
Array(only).include?(name)
|
185
193
|
elsif (except = options[:except]) && !exclusive
|
186
|
-
Array(except).include?(name)
|
194
|
+
!Array(except).include?(name)
|
187
195
|
else
|
188
196
|
send(:"attributes_#{name}_by_default")
|
189
197
|
end
|
@@ -72,7 +72,7 @@ module Graphiti
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def resource_for_model(model)
|
75
|
-
resource = children.find { |c| model.
|
75
|
+
resource = children.find { |c| model.instance_of?(c.model) } ||
|
76
76
|
children.find { |c| model.is_a?(c.model) }
|
77
77
|
if resource.nil?
|
78
78
|
raise Errors::PolymorphicResourceChildNotFound.new(self, model: model)
|
@@ -68,10 +68,10 @@ module Graphiti
|
|
68
68
|
model_ref = model
|
69
69
|
has_many name, opts do
|
70
70
|
params do |hash|
|
71
|
-
hash[:filter][:"#{as}_type"] = model_ref.name
|
71
|
+
hash[:filter][:"#{as}_type"] = { eql: model_ref.name }
|
72
72
|
end
|
73
73
|
|
74
|
-
instance_eval(&blk) if
|
74
|
+
instance_eval(&blk) if blk
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -82,10 +82,10 @@ module Graphiti
|
|
82
82
|
model_ref = model
|
83
83
|
has_one name, opts do
|
84
84
|
params do |hash|
|
85
|
-
hash[:filter][:"#{as}_type"] = model_ref.name
|
85
|
+
hash[:filter][:"#{as}_type"] = { eql: model_ref.name }
|
86
86
|
end
|
87
87
|
|
88
|
-
instance_eval(&blk) if
|
88
|
+
instance_eval(&blk) if blk
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
data/lib/graphiti/resource.rb
CHANGED
@@ -28,11 +28,12 @@ module Graphiti
|
|
28
28
|
serializer
|
29
29
|
end
|
30
30
|
|
31
|
-
def decorate_record(record)
|
31
|
+
def decorate_record(record, index = nil)
|
32
32
|
unless record.instance_variable_get(:@__graphiti_serializer)
|
33
33
|
serializer = serializer_for(record)
|
34
34
|
record.instance_variable_set(:@__graphiti_serializer, serializer)
|
35
35
|
record.instance_variable_set(:@__graphiti_resource, self)
|
36
|
+
record.instance_variable_set(:@__graphiti_index, index) if index
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -73,7 +73,7 @@ module Graphiti
|
|
73
73
|
records
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
76
|
+
alias_method :to_a, :data
|
77
77
|
|
78
78
|
def meta
|
79
79
|
@meta ||= data.respond_to?(:meta) ? data.meta : {}
|
@@ -85,9 +85,15 @@ module Graphiti
|
|
85
85
|
|
86
86
|
def stats
|
87
87
|
@stats ||= if @query.hash[:stats]
|
88
|
+
scope = @scope.unpaginated_object
|
89
|
+
if resource.adapter.can_group?
|
90
|
+
if (group = @query.hash[:stats].delete(:group_by))
|
91
|
+
scope = resource.adapter.group(scope, group[0])
|
92
|
+
end
|
93
|
+
end
|
88
94
|
payload = Stats::Payload.new @resource,
|
89
95
|
@query,
|
90
|
-
|
96
|
+
scope,
|
91
97
|
data
|
92
98
|
payload.generate
|
93
99
|
else
|
data/lib/graphiti/schema.rb
CHANGED
@@ -153,6 +153,8 @@ module Graphiti
|
|
153
153
|
def extra_attributes(resource)
|
154
154
|
{}.tap do |attrs|
|
155
155
|
resource.extra_attributes.each_pair do |name, config|
|
156
|
+
next unless config[:schema]
|
157
|
+
|
156
158
|
attrs[name] = {
|
157
159
|
type: config[:type].to_s,
|
158
160
|
readable: flag(config[:readable]),
|
@@ -181,11 +183,11 @@ module Graphiti
|
|
181
183
|
def sorts(resource)
|
182
184
|
{}.tap do |s|
|
183
185
|
resource.sorts.each_pair do |name, sort|
|
184
|
-
|
186
|
+
attr = resource.all_attributes[name]
|
187
|
+
next unless attr[:schema]
|
185
188
|
|
186
189
|
config = {}
|
187
190
|
config[:only] = sort[:only] if sort[:only]
|
188
|
-
attr = resource.attributes[name]
|
189
191
|
if attr[:sortable].is_a?(Symbol)
|
190
192
|
config[:guard] = true
|
191
193
|
end
|
@@ -197,7 +199,7 @@ module Graphiti
|
|
197
199
|
def filters(resource)
|
198
200
|
{}.tap do |f|
|
199
201
|
resource.filters.each_pair do |name, filter|
|
200
|
-
next unless resource.
|
202
|
+
next unless resource.filters[name][:schema]
|
201
203
|
|
202
204
|
config = {
|
203
205
|
type: filter[:type].to_s,
|
@@ -209,7 +211,7 @@ module Graphiti
|
|
209
211
|
config[:deny] = filter[:deny].map(&:to_s) if filter[:deny]
|
210
212
|
config[:dependencies] = filter[:dependencies].map(&:to_s) if filter[:dependencies]
|
211
213
|
|
212
|
-
attr = resource.
|
214
|
+
attr = resource.all_attributes[name]
|
213
215
|
if attr[:filterable].is_a?(Symbol)
|
214
216
|
if attr[:filterable] == :required
|
215
217
|
config[:required] = true
|
data/lib/graphiti/scope.rb
CHANGED
@@ -85,8 +85,8 @@ module Graphiti
|
|
85
85
|
# Used to ensure the resource's serializer is used
|
86
86
|
# Not one derived through the usual jsonapi-rb logic
|
87
87
|
def assign_serializer(records)
|
88
|
-
records.
|
89
|
-
@resource.decorate_record(r)
|
88
|
+
records.each_with_index do |r, index|
|
89
|
+
@resource.decorate_record(r, index)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Graphiti
|
2
2
|
class Scoping::Paginate < Scoping::Base
|
3
3
|
DEFAULT_PAGE_SIZE = 20
|
4
|
+
PARAMS = [:number, :size, :offset, :before, :after]
|
4
5
|
|
5
6
|
def apply
|
6
7
|
if size > resource.max_page_size
|
@@ -56,7 +57,7 @@ module Graphiti
|
|
56
57
|
private
|
57
58
|
|
58
59
|
def requested?
|
59
|
-
!
|
60
|
+
!PARAMS.map { |p| page_param[p] }.all?(&:nil?)
|
60
61
|
end
|
61
62
|
|
62
63
|
def page_param
|
@@ -64,9 +65,34 @@ module Graphiti
|
|
64
65
|
end
|
65
66
|
|
66
67
|
def offset
|
68
|
+
offset = nil
|
69
|
+
|
67
70
|
if (value = page_param[:offset])
|
68
|
-
value.to_i
|
71
|
+
offset = value.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
if before_cursor&.key?(:offset)
|
75
|
+
if page_param.key?(:number)
|
76
|
+
raise Errors::UnsupportedBeforeCursor
|
77
|
+
end
|
78
|
+
|
79
|
+
offset = before_cursor[:offset] - (size * number) - 1
|
80
|
+
offset = 0 if offset.negative?
|
81
|
+
end
|
82
|
+
|
83
|
+
if after_cursor&.key?(:offset)
|
84
|
+
offset = after_cursor[:offset]
|
69
85
|
end
|
86
|
+
|
87
|
+
offset
|
88
|
+
end
|
89
|
+
|
90
|
+
def after_cursor
|
91
|
+
page_param[:after]
|
92
|
+
end
|
93
|
+
|
94
|
+
def before_cursor
|
95
|
+
page_param[:before]
|
70
96
|
end
|
71
97
|
|
72
98
|
def number
|
@@ -59,12 +59,10 @@ module Graphiti
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def sort_param
|
62
|
-
@sort_param ||=
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
normalize(query_hash[:sort])
|
67
|
-
end
|
62
|
+
@sort_param ||= if query_hash[:sort].blank?
|
63
|
+
resource.default_sort || []
|
64
|
+
else
|
65
|
+
normalize(query_hash[:sort])
|
68
66
|
end
|
69
67
|
end
|
70
68
|
|
data/lib/graphiti/serializer.rb
CHANGED
@@ -25,8 +25,9 @@ module Graphiti
|
|
25
25
|
|
26
26
|
# See #requested_relationships
|
27
27
|
def self.relationship(name, options = {}, &block)
|
28
|
+
prev = Util::Hash.deep_dup(field_condition_blocks)
|
28
29
|
super
|
29
|
-
field_condition_blocks
|
30
|
+
self.field_condition_blocks = prev
|
30
31
|
_register_condition(relationship_condition_blocks, name, options)
|
31
32
|
end
|
32
33
|
|
@@ -45,6 +46,23 @@ module Graphiti
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
def cursor
|
50
|
+
starting_offset = 0
|
51
|
+
page_param = @proxy.query.pagination
|
52
|
+
if (page_number = page_param[:number])
|
53
|
+
page_size = page_param[:size] || @resource.default_page_size
|
54
|
+
starting_offset = (page_number - 1) * page_size
|
55
|
+
end
|
56
|
+
|
57
|
+
if (cursor = page_param[:after])
|
58
|
+
starting_offset = cursor[:offset]
|
59
|
+
end
|
60
|
+
|
61
|
+
current_offset = @object.instance_variable_get(:@__graphiti_index)
|
62
|
+
offset = starting_offset + current_offset + 1 # (+ 1 b/c o-base index)
|
63
|
+
Base64.encode64({offset: offset}.to_json).chomp
|
64
|
+
end
|
65
|
+
|
48
66
|
def as_jsonapi(kwargs = {})
|
49
67
|
super(**kwargs).tap do |hash|
|
50
68
|
strip_relationships!(hash) if strip_relationships?
|
@@ -7,7 +7,6 @@ class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo
|
|
7
7
|
@calls = []
|
8
8
|
end
|
9
9
|
|
10
|
-
# rubocop: disable Style/MethodMissingSuper
|
11
10
|
def method_missing(name, *args, &blk)
|
12
11
|
@calls << [name, args, blk]
|
13
12
|
end
|
@@ -56,9 +55,9 @@ class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo
|
|
56
55
|
args = call[1]
|
57
56
|
opts = args.extract_options!
|
58
57
|
opts.merge! as: sideload.name,
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
parent: sideload,
|
59
|
+
group_name: group.name,
|
60
|
+
polymorphic_child: true
|
62
61
|
unless sideload.resource.class.abstract_class?
|
63
62
|
opts[:foreign_key] ||= sideload.foreign_key
|
64
63
|
opts[:primary_key] ||= sideload.primary_key
|
data/lib/graphiti/stats/dsl.rb
CHANGED
@@ -28,6 +28,12 @@ module Graphiti
|
|
28
28
|
|
29
29
|
existing = @serializer.send(applied_method)
|
30
30
|
@serializer.send(:"#{applied_method}=", [@name] | existing)
|
31
|
+
|
32
|
+
@serializer.meta do
|
33
|
+
if !!@resource.try(:cursor_paginatable?) && !Graphiti.context[:graphql]
|
34
|
+
{cursor: cursor}
|
35
|
+
end
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
private
|