graphiti 1.2.43 → 1.3.2
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 +1 -0
- data/lib/graphiti/delegates/pagination.rb +12 -2
- data/lib/graphiti/errors.rb +17 -11
- data/lib/graphiti/hash_renderer.rb +35 -1
- data/lib/graphiti/query.rb +17 -4
- data/lib/graphiti/renderer.rb +1 -0
- data/lib/graphiti/resource/configuration.rb +2 -1
- data/lib/graphiti/resource/dsl.rb +4 -1
- data/lib/graphiti/resource.rb +2 -1
- data/lib/graphiti/schema.rb +1 -1
- data/lib/graphiti/scope.rb +2 -2
- data/lib/graphiti/scoping/paginate.rb +32 -6
- data/lib/graphiti/serializer.rb +17 -0
- data/lib/graphiti/util/serializer_attributes.rb +6 -0
- data/lib/graphiti/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e07d725924af0735b8871f3f03b3164a0da894a3b23162fc8dc4e1fee94dcaf9
|
4
|
+
data.tar.gz: b5ea126cb269428beb5f9d52383f4dfc643133871a1fd4e7f72eff45e08e69cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39317b810fbf79762dfce8350c620ade336b6a6b8f94e643e02a70cbba6e58d498a3a518672ea70dc906b3e269a9ae6c35bb4c309c15253e65c7bd7190c9d2fc
|
7
|
+
data.tar.gz: eb010670119a2361e2f468a8201e8f2ceef564a63e2af14ca3ca2565d3c461e65f0026147fbf25b1abfc5a093a6d9111fc00772d01830f9fbec206e47d066854
|
data/CHANGELOG.md
CHANGED
@@ -12,6 +12,7 @@ Features:
|
|
12
12
|
Fixes:
|
13
13
|
- [282] Support model names including "Resource"
|
14
14
|
- [313](https://github.com/graphiti-api/graphiti/pull/313) Sort remote resources in schema generation
|
15
|
+
- [374](https://github.com/graphiti-api/graphiti/pull/374) Trim leading spaces from error messages
|
15
16
|
|
16
17
|
## 1.1.0
|
17
18
|
|
@@ -14,11 +14,21 @@ module Graphiti
|
|
14
14
|
links[:self] = pagination_link(current_page)
|
15
15
|
links[:first] = pagination_link(1)
|
16
16
|
links[:last] = pagination_link(last_page)
|
17
|
-
links[:prev] = pagination_link(current_page - 1)
|
18
|
-
links[:next] = pagination_link(current_page + 1)
|
17
|
+
links[:prev] = pagination_link(current_page - 1) if has_previous_page?
|
18
|
+
links[:next] = pagination_link(current_page + 1) if has_next_page?
|
19
19
|
end.select { |k, v| !v.nil? }
|
20
20
|
end
|
21
21
|
|
22
|
+
def has_next_page?
|
23
|
+
current_page != last_page && last_page.present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_previous_page?
|
27
|
+
current_page != 1 ||
|
28
|
+
!!pagination_params.try(:[], :page).try(:[], :after) ||
|
29
|
+
!!pagination_params.try(:[], :page).try(:[], :offset)
|
30
|
+
end
|
31
|
+
|
22
32
|
private
|
23
33
|
|
24
34
|
def pagination_params
|
data/lib/graphiti/errors.rb
CHANGED
@@ -35,7 +35,7 @@ module Graphiti
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def message
|
38
|
-
|
38
|
+
<<~MSG
|
39
39
|
The adapter #{@adapter.class} does not implement method '#{@method}', which was requested for attribute '#{@attribute}'. Add this method to your adapter to support this filter operator.
|
40
40
|
MSG
|
41
41
|
end
|
@@ -49,7 +49,7 @@ module Graphiti
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def message
|
52
|
-
|
52
|
+
<<~MSG
|
53
53
|
#{@parent_resource_class} sideload :#{@name} - #{@message}
|
54
54
|
MSG
|
55
55
|
end
|
@@ -78,7 +78,7 @@ module Graphiti
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def message
|
81
|
-
|
81
|
+
<<~MSG
|
82
82
|
#{@resource_class}: Tried to pass block to .#{@method_name}, which only accepts a method name.
|
83
83
|
MSG
|
84
84
|
end
|
@@ -90,7 +90,7 @@ module Graphiti
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def message
|
93
|
-
|
93
|
+
<<~MSG
|
94
94
|
#{@resource_class}: Tried to perform write operation. Writes are not supported for remote resources - hit the endpoint directly.
|
95
95
|
MSG
|
96
96
|
end
|
@@ -105,7 +105,7 @@ module Graphiti
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def message
|
108
|
-
|
108
|
+
<<~MSG
|
109
109
|
#{@resource.class}: Tried to filter #{@filter_name.inspect} on operator #{@operator.inspect}, but not supported! Supported operators are #{@supported}.
|
110
110
|
MSG
|
111
111
|
end
|
@@ -118,7 +118,7 @@ module Graphiti
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def message
|
121
|
-
|
121
|
+
<<~MSG
|
122
122
|
#{@sideload.parent_resource.class.name}: tried to sideload #{@sideload.name.inspect}, but more than one #{@sideload.parent_resource.model.name} was passed!
|
123
123
|
|
124
124
|
This is because you marked the sideload #{@sideload.name.inspect} with single: true
|
@@ -139,7 +139,7 @@ module Graphiti
|
|
139
139
|
end
|
140
140
|
|
141
141
|
def message
|
142
|
-
|
142
|
+
<<~MSG
|
143
143
|
#{@resource.class.name}: tried to sort on attribute #{@attribute.inspect}, but passed #{@direction.inspect} when only #{@allowlist.inspect} is supported.
|
144
144
|
MSG
|
145
145
|
end
|
@@ -152,7 +152,7 @@ module Graphiti
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def message
|
155
|
-
|
155
|
+
<<~MSG
|
156
156
|
#{@resource_class.name}: called .on_extra_attribute #{@name.inspect}, but extra attribute #{@name.inspect} does not exist!
|
157
157
|
MSG
|
158
158
|
end
|
@@ -173,7 +173,7 @@ module Graphiti
|
|
173
173
|
else
|
174
174
|
"value #{@value.inspect}"
|
175
175
|
end
|
176
|
-
msg =
|
176
|
+
msg = <<~MSG
|
177
177
|
#{@resource.class.name}: tried to filter on #{@filter.keys[0].inspect}, but passed invalid #{value_string}.
|
178
178
|
MSG
|
179
179
|
msg << "\nAllowlist: #{allow.inspect}" if allow
|
@@ -190,7 +190,7 @@ module Graphiti
|
|
190
190
|
end
|
191
191
|
|
192
192
|
def message
|
193
|
-
|
193
|
+
<<~MSG
|
194
194
|
#{@resource_class.name} You declared an attribute or filter of type "#{@enum_type}" without providing a list of permitted values, which is required.
|
195
195
|
|
196
196
|
When declaring an attribute:
|
@@ -214,7 +214,7 @@ module Graphiti
|
|
214
214
|
end
|
215
215
|
|
216
216
|
def message
|
217
|
-
|
217
|
+
<<~MSG
|
218
218
|
#{@resource_class.name}: Cannot link to sideload #{@sideload.name.inspect}!
|
219
219
|
|
220
220
|
Make sure the endpoint "#{@sideload.resource.endpoint[:full_path]}" exists with action #{@action.inspect}, or customize the endpoint for #{@sideload.resource.class.name}.
|
@@ -733,6 +733,12 @@ module Graphiti
|
|
733
733
|
end
|
734
734
|
end
|
735
735
|
|
736
|
+
class UnsupportedBeforeCursor < Base
|
737
|
+
def message
|
738
|
+
"Passing in page[before] and page[number] is not supported. Please create an issue if you need it!"
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
736
742
|
class InvalidInclude < Base
|
737
743
|
def initialize(resource, relationship)
|
738
744
|
@resource = resource
|
@@ -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
|
@@ -191,5 +199,31 @@ module Graphiti
|
|
191
199
|
end
|
192
200
|
end
|
193
201
|
end
|
202
|
+
|
203
|
+
# NB - this is only for top-level right now
|
204
|
+
# The casing here is GQL-specific, we can update later if needed.
|
205
|
+
def add_page_info(hash, serializers, top_level_key, options)
|
206
|
+
if (fields = options[:fields].try(:[], :page_info))
|
207
|
+
info = {}
|
208
|
+
|
209
|
+
if fields.include?(:has_next_page)
|
210
|
+
info[:hasNextPage] = options[:proxy].pagination.has_next_page?
|
211
|
+
end
|
212
|
+
|
213
|
+
if fields.include?(:has_previous_page)
|
214
|
+
info[:hasPreviousPage] = options[:proxy].pagination.has_previous_page?
|
215
|
+
end
|
216
|
+
|
217
|
+
if fields.include?(:start_cursor)
|
218
|
+
info[:startCursor] = serializers.first.try(:cursor)
|
219
|
+
end
|
220
|
+
|
221
|
+
if fields.include?(:end_cursor)
|
222
|
+
info[:endCursor] = serializers.last.try(:cursor)
|
223
|
+
end
|
224
|
+
|
225
|
+
hash[top_level_key][:pageInfo] = info
|
226
|
+
end
|
227
|
+
end
|
194
228
|
end
|
195
229
|
end
|
data/lib/graphiti/query.rb
CHANGED
@@ -191,12 +191,13 @@ module Graphiti
|
|
191
191
|
(@params[:page] || {}).each_pair do |name, value|
|
192
192
|
if legacy_nested?(name)
|
193
193
|
value.each_pair do |k, v|
|
194
|
-
hash[k.to_sym] = v
|
194
|
+
hash[k.to_sym] = cast_page_param(k.to_sym, v)
|
195
195
|
end
|
196
196
|
elsif nested?(name)
|
197
|
-
|
198
|
-
|
199
|
-
|
197
|
+
param_name = name.to_s.split(".").last.to_sym
|
198
|
+
hash[param_name] = cast_page_param(param_name, value)
|
199
|
+
elsif top_level? && Scoping::Paginate::PARAMS.include?(name.to_sym)
|
200
|
+
hash[name.to_sym] = cast_page_param(name.to_sym, value)
|
200
201
|
end
|
201
202
|
end
|
202
203
|
end
|
@@ -240,6 +241,18 @@ module Graphiti
|
|
240
241
|
|
241
242
|
private
|
242
243
|
|
244
|
+
def cast_page_param(name, value)
|
245
|
+
if [:before, :after].include?(name)
|
246
|
+
decode_cursor(value)
|
247
|
+
else
|
248
|
+
value.to_i
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def decode_cursor(cursor)
|
253
|
+
JSON.parse(Base64.decode64(cursor)).symbolize_keys
|
254
|
+
end
|
255
|
+
|
243
256
|
# Try to find on this resource
|
244
257
|
# If not there, follow the legacy logic of scalling all other
|
245
258
|
# resource names/types
|
data/lib/graphiti/renderer.rb
CHANGED
@@ -23,6 +23,8 @@ module Graphiti
|
|
23
23
|
end
|
24
24
|
|
25
25
|
required = att[:filterable] == :required || !!opts[:required]
|
26
|
+
schema = !!opts[:via_attribute_dsl] ? att[:schema] : opts[:schema] != false
|
27
|
+
|
26
28
|
config[:filters][name.to_sym] = {
|
27
29
|
aliases: aliases,
|
28
30
|
name: name.to_sym,
|
@@ -32,6 +34,7 @@ module Graphiti
|
|
32
34
|
single: !!opts[:single],
|
33
35
|
dependencies: opts[:dependent],
|
34
36
|
required: required,
|
37
|
+
schema: schema,
|
35
38
|
operators: operators.to_hash,
|
36
39
|
allow_nil: opts.fetch(:allow_nil, filters_accept_nil_by_default),
|
37
40
|
deny_empty: opts.fetch(:deny_empty, filters_deny_empty_by_default)
|
@@ -130,7 +133,7 @@ module Graphiti
|
|
130
133
|
options[:sortable] ? sort(name) : config[:sorts].delete(name)
|
131
134
|
|
132
135
|
if options[:filterable]
|
133
|
-
filter(name, allow: options[:allow])
|
136
|
+
filter(name, allow: options[:allow], via_attribute_dsl: true)
|
134
137
|
else
|
135
138
|
config[:filters].delete(name)
|
136
139
|
end
|
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
|
|
data/lib/graphiti/schema.rb
CHANGED
@@ -197,7 +197,7 @@ module Graphiti
|
|
197
197
|
def filters(resource)
|
198
198
|
{}.tap do |f|
|
199
199
|
resource.filters.each_pair do |name, filter|
|
200
|
-
next unless resource.
|
200
|
+
next unless resource.filters[name][:schema]
|
201
201
|
|
202
202
|
config = {
|
203
203
|
type: filter[:type].to_s,
|
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
|
@@ -33,12 +34,12 @@ module Graphiti
|
|
33
34
|
|
34
35
|
# Apply default pagination proc via the Resource adapter
|
35
36
|
def apply_standard_scope
|
36
|
-
|
37
|
+
meth = resource.adapter.method(:paginate)
|
37
38
|
|
38
|
-
if arity == 4 # backwards-compat
|
39
|
-
resource.adapter.paginate(@scope, number, size)
|
40
|
-
else
|
39
|
+
if meth.arity == 4 # backwards-compat
|
41
40
|
resource.adapter.paginate(@scope, number, size, offset)
|
41
|
+
else
|
42
|
+
resource.adapter.paginate(@scope, number, size)
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
@@ -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?
|
69
81
|
end
|
82
|
+
|
83
|
+
if after_cursor&.key?(:offset)
|
84
|
+
offset = after_cursor[:offset]
|
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
|
data/lib/graphiti/serializer.rb
CHANGED
@@ -45,6 +45,23 @@ module Graphiti
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
def cursor
|
49
|
+
starting_offset = 0
|
50
|
+
page_param = @proxy.query.pagination
|
51
|
+
if (page_number = page_param[:number])
|
52
|
+
page_size = page_param[:size] || @resource.default_page_size
|
53
|
+
starting_offset = (page_number - 1) * page_size
|
54
|
+
end
|
55
|
+
|
56
|
+
if (cursor = page_param[:after])
|
57
|
+
starting_offset = cursor[:offset]
|
58
|
+
end
|
59
|
+
|
60
|
+
current_offset = @object.instance_variable_get(:@__graphiti_index)
|
61
|
+
offset = starting_offset + current_offset + 1 # (+ 1 b/c o-base index)
|
62
|
+
Base64.encode64({offset: offset}.to_json).chomp
|
63
|
+
end
|
64
|
+
|
48
65
|
def as_jsonapi(kwargs = {})
|
49
66
|
super(**kwargs).tap do |hash|
|
50
67
|
strip_relationships!(hash) if strip_relationships?
|
@@ -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
|
data/lib/graphiti/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphiti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-serializable
|
@@ -361,7 +361,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
361
361
|
- !ruby/object:Gem::Version
|
362
362
|
version: '0'
|
363
363
|
requirements: []
|
364
|
-
rubygems_version: 3.
|
364
|
+
rubygems_version: 3.2.22
|
365
365
|
signing_key:
|
366
366
|
specification_version: 4
|
367
367
|
summary: Easily build jsonapi.org-compatible APIs
|