forestadmin-jsonapi-serializers 2.0.0.pre.beta.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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +868 -0
- data/Rakefile +2 -0
- data/forestadmin-jsonapi-serializers.gemspec +28 -0
- data/lib/jsonapi-serializers.rb +13 -0
- data/lib/jsonapi-serializers/attributes.rb +79 -0
- data/lib/jsonapi-serializers/serializer.rb +589 -0
- data/lib/jsonapi-serializers/version.rb +7 -0
- data/spec/serializer_spec.rb +1337 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/factory.rb +44 -0
- data/spec/support/serializers.rb +264 -0
- metadata +160 -0
@@ -0,0 +1,1337 @@
|
|
1
|
+
require 'active_model/errors'
|
2
|
+
require 'active_model/naming'
|
3
|
+
require 'active_model/translation'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
describe ForestAdmin::JSONAPI::Serializer do
|
7
|
+
def serialize_primary(object, options = {})
|
8
|
+
# Note: intentional high-coupling to protected method for tests.
|
9
|
+
ForestAdmin::JSONAPI::Serializer.send(:serialize_primary, object, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'internal-only serialize_primary' do
|
13
|
+
it 'serializes nil to nil' do
|
14
|
+
# Spec: Primary data MUST be either:
|
15
|
+
# - a single resource object or null, for requests that target single resources
|
16
|
+
# http://jsonapi.org/format/#document-structure-top-level
|
17
|
+
primary_data = serialize_primary(nil, {serializer: MyApp::PostSerializer})
|
18
|
+
expect(primary_data).to be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can serialize primary data for a simple object' do
|
22
|
+
post = create(:post)
|
23
|
+
primary_data = serialize_primary(post, {serializer: MyApp::SimplestPostSerializer})
|
24
|
+
expect(primary_data).to eq({
|
25
|
+
'id' => '1',
|
26
|
+
'type' => 'posts',
|
27
|
+
'attributes' => {
|
28
|
+
'title' => 'Title for Post 1',
|
29
|
+
'long-content' => 'Body for Post 1',
|
30
|
+
},
|
31
|
+
'links' => {
|
32
|
+
'self' => '/posts/1',
|
33
|
+
},
|
34
|
+
})
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can serialize primary data for a simple object with a long name' do
|
38
|
+
long_comment = create(:long_comment, post: create(:post))
|
39
|
+
primary_data = serialize_primary(long_comment, {serializer: MyApp::LongCommentSerializer})
|
40
|
+
expect(primary_data).to eq({
|
41
|
+
'id' => '1',
|
42
|
+
'type' => 'long-comments',
|
43
|
+
'attributes' => {
|
44
|
+
'body' => 'Body for LongComment 1',
|
45
|
+
},
|
46
|
+
'links' => {
|
47
|
+
'self' => '/long-comments/1',
|
48
|
+
},
|
49
|
+
'relationships' => {
|
50
|
+
'user' => {
|
51
|
+
'links' => {
|
52
|
+
'self' => '/long-comments/1/relationships/user',
|
53
|
+
'related' => '/long-comments/1/user',
|
54
|
+
},
|
55
|
+
},
|
56
|
+
'post' => {
|
57
|
+
'links' => {
|
58
|
+
'self' => '/long-comments/1/relationships/post',
|
59
|
+
'related' => '/long-comments/1/post',
|
60
|
+
},
|
61
|
+
},
|
62
|
+
},
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'can serialize primary data for a simple object with resource-level metadata' do
|
67
|
+
post = create(:post)
|
68
|
+
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializerWithMetadata})
|
69
|
+
expect(primary_data).to eq({
|
70
|
+
'id' => '1',
|
71
|
+
'type' => 'posts',
|
72
|
+
'attributes' => {
|
73
|
+
'title' => 'Title for Post 1',
|
74
|
+
'long-content' => 'Body for Post 1',
|
75
|
+
},
|
76
|
+
'links' => {
|
77
|
+
'self' => '/posts/1',
|
78
|
+
},
|
79
|
+
'meta' => {
|
80
|
+
'copyright' => 'Copyright 2015 Example Corp.',
|
81
|
+
'authors' => [
|
82
|
+
'Aliens',
|
83
|
+
],
|
84
|
+
},
|
85
|
+
})
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'without any linkage includes (default)' do
|
89
|
+
it 'can serialize primary data for an object with to-one and to-many relationships' do
|
90
|
+
post = create(:post)
|
91
|
+
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializer})
|
92
|
+
expect(primary_data).to eq({
|
93
|
+
'id' => '1',
|
94
|
+
'type' => 'posts',
|
95
|
+
'attributes' => {
|
96
|
+
'title' => 'Title for Post 1',
|
97
|
+
'long-content' => 'Body for Post 1',
|
98
|
+
},
|
99
|
+
'links' => {
|
100
|
+
'self' => '/posts/1',
|
101
|
+
},
|
102
|
+
'relationships' => {
|
103
|
+
# Both to-one and to-many links are present, but neither include linkage:
|
104
|
+
'author' => {
|
105
|
+
'links' => {
|
106
|
+
'self' => '/posts/1/relationships/author',
|
107
|
+
'related' => '/posts/1/author',
|
108
|
+
},
|
109
|
+
},
|
110
|
+
'long-comments' => {
|
111
|
+
'links' => {
|
112
|
+
'self' => '/posts/1/relationships/long-comments',
|
113
|
+
'related' => '/posts/1/long-comments',
|
114
|
+
},
|
115
|
+
},
|
116
|
+
},
|
117
|
+
})
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'does not include relationship links if relationship_{self_link,_related_link} are nil' do
|
121
|
+
post = create(:post)
|
122
|
+
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializerWithoutLinks})
|
123
|
+
expect(primary_data).to eq({
|
124
|
+
'id' => '1',
|
125
|
+
'type' => 'posts',
|
126
|
+
'attributes' => {
|
127
|
+
'title' => 'Title for Post 1',
|
128
|
+
'long-content' => 'Body for Post 1',
|
129
|
+
},
|
130
|
+
# This is technically invalid since relationships MUST contain at least one of links,
|
131
|
+
# data, or meta, but we leave that up to the user.
|
132
|
+
'relationships' => {
|
133
|
+
'author' => {},
|
134
|
+
'long-comments' => {},
|
135
|
+
},
|
136
|
+
})
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'does not include id when it is nil' do
|
140
|
+
post = create(:post)
|
141
|
+
post.id = nil
|
142
|
+
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializerWithoutLinks})
|
143
|
+
expect(primary_data).to eq({
|
144
|
+
'type' => 'posts',
|
145
|
+
'attributes' => {
|
146
|
+
'title' => 'Title for Post 1',
|
147
|
+
'long-content' => 'Body for Post 1',
|
148
|
+
},
|
149
|
+
'relationships' => {
|
150
|
+
'author' => {},
|
151
|
+
'long-comments' => {},
|
152
|
+
},
|
153
|
+
})
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'serializes object when multiple attributes are declared once' do
|
157
|
+
post = create(:post)
|
158
|
+
primary_data = serialize_primary(post, {serializer: MyApp::MultipleAttributesSerializer})
|
159
|
+
expect(primary_data).to eq({
|
160
|
+
'id' => '1',
|
161
|
+
'type' => 'posts',
|
162
|
+
'attributes' => {
|
163
|
+
'title' => 'Title for Post 1',
|
164
|
+
'body' => 'Body for Post 1',
|
165
|
+
},
|
166
|
+
'links' => {
|
167
|
+
'self' => '/posts/1',
|
168
|
+
}
|
169
|
+
})
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'with linkage includes' do
|
174
|
+
it 'can serialize primary data for a null to-one relationship' do
|
175
|
+
post = create(:post, author: nil)
|
176
|
+
options = {
|
177
|
+
serializer: MyApp::PostSerializer,
|
178
|
+
include_linkages: ['author', 'long-comments'],
|
179
|
+
}
|
180
|
+
primary_data = serialize_primary(post, options)
|
181
|
+
expect(primary_data).to eq({
|
182
|
+
'id' => '1',
|
183
|
+
'type' => 'posts',
|
184
|
+
'attributes' => {
|
185
|
+
'title' => 'Title for Post 1',
|
186
|
+
'long-content' => 'Body for Post 1',
|
187
|
+
},
|
188
|
+
'links' => {
|
189
|
+
'self' => '/posts/1',
|
190
|
+
},
|
191
|
+
'relationships' => {
|
192
|
+
'author' => {
|
193
|
+
'links' => {
|
194
|
+
'self' => '/posts/1/relationships/author',
|
195
|
+
'related' => '/posts/1/author',
|
196
|
+
},
|
197
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
198
|
+
# - null for empty to-one relationships.
|
199
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
200
|
+
'data' => nil,
|
201
|
+
},
|
202
|
+
'long-comments' => {
|
203
|
+
'links' => {
|
204
|
+
'self' => '/posts/1/relationships/long-comments',
|
205
|
+
'related' => '/posts/1/long-comments',
|
206
|
+
},
|
207
|
+
'data' => [],
|
208
|
+
},
|
209
|
+
},
|
210
|
+
})
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'can serialize primary data for a simple to-one relationship' do
|
214
|
+
post = create(:post, :with_author)
|
215
|
+
options = {
|
216
|
+
serializer: MyApp::PostSerializer,
|
217
|
+
include_linkages: ['author', 'long-comments'],
|
218
|
+
}
|
219
|
+
primary_data = serialize_primary(post, options)
|
220
|
+
expect(primary_data).to eq({
|
221
|
+
'id' => '1',
|
222
|
+
'type' => 'posts',
|
223
|
+
'attributes' => {
|
224
|
+
'title' => 'Title for Post 1',
|
225
|
+
'long-content' => 'Body for Post 1',
|
226
|
+
},
|
227
|
+
'links' => {
|
228
|
+
'self' => '/posts/1',
|
229
|
+
},
|
230
|
+
'relationships' => {
|
231
|
+
'author' => {
|
232
|
+
'links' => {
|
233
|
+
'self' => '/posts/1/relationships/author',
|
234
|
+
'related' => '/posts/1/author',
|
235
|
+
},
|
236
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
237
|
+
# - a 'linkage object' (defined below) for non-empty to-one relationships.
|
238
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
239
|
+
'data' => {
|
240
|
+
'type' => 'users',
|
241
|
+
'id' => '1',
|
242
|
+
},
|
243
|
+
},
|
244
|
+
'long-comments' => {
|
245
|
+
'links' => {
|
246
|
+
'self' => '/posts/1/relationships/long-comments',
|
247
|
+
'related' => '/posts/1/long-comments',
|
248
|
+
},
|
249
|
+
'data' => [],
|
250
|
+
},
|
251
|
+
},
|
252
|
+
})
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'can serialize primary data for an empty to-many relationship' do
|
256
|
+
post = create(:post, long_comments: [])
|
257
|
+
options = {
|
258
|
+
serializer: MyApp::PostSerializer,
|
259
|
+
include_linkages: ['author', 'long-comments'],
|
260
|
+
}
|
261
|
+
primary_data = serialize_primary(post, options)
|
262
|
+
expect(primary_data).to eq({
|
263
|
+
'id' => '1',
|
264
|
+
'type' => 'posts',
|
265
|
+
'attributes' => {
|
266
|
+
'title' => 'Title for Post 1',
|
267
|
+
'long-content' => 'Body for Post 1',
|
268
|
+
},
|
269
|
+
'links' => {
|
270
|
+
'self' => '/posts/1',
|
271
|
+
},
|
272
|
+
'relationships' => {
|
273
|
+
'author' => {
|
274
|
+
'links' => {
|
275
|
+
'self' => '/posts/1/relationships/author',
|
276
|
+
'related' => '/posts/1/author',
|
277
|
+
},
|
278
|
+
'data' => nil,
|
279
|
+
},
|
280
|
+
'long-comments' => {
|
281
|
+
'links' => {
|
282
|
+
'self' => '/posts/1/relationships/long-comments',
|
283
|
+
'related' => '/posts/1/long-comments',
|
284
|
+
},
|
285
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
286
|
+
# - an empty array ([]) for empty to-many relationships.
|
287
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
288
|
+
'data' => [],
|
289
|
+
},
|
290
|
+
},
|
291
|
+
})
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'can serialize primary data for a simple to-many relationship' do
|
295
|
+
long_comments = create_list(:long_comment, 2)
|
296
|
+
post = create(:post, long_comments: long_comments)
|
297
|
+
options = {
|
298
|
+
serializer: MyApp::PostSerializer,
|
299
|
+
include_linkages: ['author', 'long-comments'],
|
300
|
+
}
|
301
|
+
primary_data = serialize_primary(post, options)
|
302
|
+
expect(primary_data).to eq({
|
303
|
+
'id' => '1',
|
304
|
+
'type' => 'posts',
|
305
|
+
'attributes' => {
|
306
|
+
'title' => 'Title for Post 1',
|
307
|
+
'long-content' => 'Body for Post 1',
|
308
|
+
},
|
309
|
+
'links' => {
|
310
|
+
'self' => '/posts/1',
|
311
|
+
},
|
312
|
+
'relationships' => {
|
313
|
+
'author' => {
|
314
|
+
'links' => {
|
315
|
+
'self' => '/posts/1/relationships/author',
|
316
|
+
'related' => '/posts/1/author',
|
317
|
+
},
|
318
|
+
'data' => nil,
|
319
|
+
},
|
320
|
+
'long-comments' => {
|
321
|
+
'links' => {
|
322
|
+
'self' => '/posts/1/relationships/long-comments',
|
323
|
+
'related' => '/posts/1/long-comments',
|
324
|
+
},
|
325
|
+
# Spec: Resource linkage MUST be represented as one of the following:
|
326
|
+
# - an array of linkage objects for non-empty to-many relationships.
|
327
|
+
# http://jsonapi.org/format/#document-structure-resource-relationships
|
328
|
+
'data' => [
|
329
|
+
{
|
330
|
+
'type' => 'long-comments',
|
331
|
+
'id' => '1',
|
332
|
+
},
|
333
|
+
{
|
334
|
+
'type' => 'long-comments',
|
335
|
+
'id' => '2',
|
336
|
+
},
|
337
|
+
],
|
338
|
+
},
|
339
|
+
},
|
340
|
+
})
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'can serialize primary data for an empty serializer with no attributes' do
|
345
|
+
post = create(:post)
|
346
|
+
primary_data = serialize_primary(post, {serializer: MyApp::EmptySerializer})
|
347
|
+
expect(primary_data).to eq({
|
348
|
+
'id' => '1',
|
349
|
+
'type' => 'posts',
|
350
|
+
'links' => {
|
351
|
+
'self' => '/posts/1',
|
352
|
+
},
|
353
|
+
})
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'can find the correct serializer by object class name' do
|
357
|
+
post = create(:post)
|
358
|
+
primary_data = serialize_primary(post)
|
359
|
+
expect(primary_data).to eq({
|
360
|
+
'id' => '1',
|
361
|
+
'type' => 'posts',
|
362
|
+
'attributes' => {
|
363
|
+
'title' => 'Title for Post 1',
|
364
|
+
'long-content' => 'Body for Post 1',
|
365
|
+
},
|
366
|
+
'links' => {
|
367
|
+
'self' => '/posts/1',
|
368
|
+
},
|
369
|
+
'relationships' => {
|
370
|
+
'author' => {
|
371
|
+
'links' => {
|
372
|
+
'self' => '/posts/1/relationships/author',
|
373
|
+
'related' => '/posts/1/author',
|
374
|
+
},
|
375
|
+
},
|
376
|
+
'long-comments' => {
|
377
|
+
'links' => {
|
378
|
+
'self' => '/posts/1/relationships/long-comments',
|
379
|
+
'related' => '/posts/1/long-comments',
|
380
|
+
},
|
381
|
+
},
|
382
|
+
},
|
383
|
+
})
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'allows to exclude linkages for relationships' do
|
387
|
+
long_comments = create_list(:long_comment, 2)
|
388
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
389
|
+
primary_data = serialize_primary(post, { serializer: MyApp::PostSerializerWithoutIncludeLinks })
|
390
|
+
expect(primary_data).to eq({
|
391
|
+
'id' => '1',
|
392
|
+
'type' => 'posts',
|
393
|
+
'attributes' => {
|
394
|
+
'title' => 'Title for Post 1'
|
395
|
+
},
|
396
|
+
'links' => {
|
397
|
+
'self' => '/posts/1',
|
398
|
+
},
|
399
|
+
'relationships' => {
|
400
|
+
'author' => {
|
401
|
+
},
|
402
|
+
'long-comments' => {
|
403
|
+
},
|
404
|
+
}
|
405
|
+
})
|
406
|
+
end
|
407
|
+
|
408
|
+
it 'allows to include data for relationships' do
|
409
|
+
long_comments = create_list(:long_comment, 2)
|
410
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
411
|
+
primary_data = serialize_primary(post, { serializer: MyApp::PostSerializerWithIncludeData })
|
412
|
+
expect(primary_data).to eq({
|
413
|
+
'id' => '1',
|
414
|
+
'type' => 'posts',
|
415
|
+
'attributes' => {
|
416
|
+
'title' => 'Title for Post 1'
|
417
|
+
},
|
418
|
+
'links' => {
|
419
|
+
'self' => '/posts/1',
|
420
|
+
},
|
421
|
+
'relationships' => {
|
422
|
+
'author' => {
|
423
|
+
'links' => {
|
424
|
+
'self' => '/posts/1/relationships/author',
|
425
|
+
'related' => '/posts/1/author'
|
426
|
+
},
|
427
|
+
'data' => {
|
428
|
+
'type' => 'users',
|
429
|
+
'id' => '1'
|
430
|
+
}
|
431
|
+
},
|
432
|
+
'long-comments' => {
|
433
|
+
'links' => {
|
434
|
+
'self' => '/posts/1/relationships/long-comments',
|
435
|
+
'related' => '/posts/1/long-comments'
|
436
|
+
},
|
437
|
+
'data' => [
|
438
|
+
{
|
439
|
+
'type' => 'long-comments',
|
440
|
+
'id' => '1'
|
441
|
+
},
|
442
|
+
{
|
443
|
+
'type' => 'long-comments',
|
444
|
+
'id' => '2'
|
445
|
+
}
|
446
|
+
]
|
447
|
+
},
|
448
|
+
}
|
449
|
+
})
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
# The members data and errors MUST NOT coexist in the same document.
|
454
|
+
describe 'ForestAdmin::JSONAPI::Serializer.serialize_errors' do
|
455
|
+
it 'can include a top level errors node' do
|
456
|
+
errors = [
|
457
|
+
{
|
458
|
+
'source' => {'pointer' => '/data/attributes/first-name'},
|
459
|
+
'title' => 'Invalid Attribute',
|
460
|
+
'detail' => 'First name must contain at least three characters.'
|
461
|
+
},
|
462
|
+
{
|
463
|
+
'source' => {'pointer' => '/data/attributes/first-name'},
|
464
|
+
'title' => 'Invalid Attribute',
|
465
|
+
'detail' => 'First name must contain an emoji.'
|
466
|
+
}
|
467
|
+
]
|
468
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize_errors(errors)).to eq({'errors' => errors})
|
469
|
+
end
|
470
|
+
|
471
|
+
it 'works for active_record' do
|
472
|
+
# DummyUser exists so we can test calling user.errors.to_hash(full_messages: true)
|
473
|
+
class DummyUser
|
474
|
+
extend ActiveModel::Naming
|
475
|
+
extend ActiveModel::Translation
|
476
|
+
|
477
|
+
def initialize
|
478
|
+
@errors = ActiveModel::Errors.new(self)
|
479
|
+
@errors.add(:email, :invalid, message: 'is invalid')
|
480
|
+
@errors.add(:email, :blank, message: "can't be blank")
|
481
|
+
@errors.add(:first_name, :blank, message: "can't be blank")
|
482
|
+
end
|
483
|
+
|
484
|
+
attr_accessor :first_name, :email
|
485
|
+
attr_reader :errors
|
486
|
+
|
487
|
+
def read_attribute_for_validation(attr)
|
488
|
+
send(attr)
|
489
|
+
end
|
490
|
+
end
|
491
|
+
user = DummyUser.new
|
492
|
+
jsonapi_errors = [
|
493
|
+
{
|
494
|
+
'source' => {'pointer' => '/data/attributes/email'},
|
495
|
+
'detail' => 'Email is invalid'
|
496
|
+
},
|
497
|
+
{
|
498
|
+
'source' => {'pointer' => '/data/attributes/email'},
|
499
|
+
'detail' => "Email can't be blank"
|
500
|
+
},
|
501
|
+
{
|
502
|
+
'source' => {'pointer' => '/data/attributes/first-name'},
|
503
|
+
'detail' => "First name can't be blank"
|
504
|
+
}
|
505
|
+
]
|
506
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize_errors(user.errors)).to eq({
|
507
|
+
'errors' => jsonapi_errors,
|
508
|
+
})
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
describe 'ForestAdmin::JSONAPI::Serializer.serialize' do
|
513
|
+
# The following tests rely on the fact that serialize_primary has been tested above, so object
|
514
|
+
# primary data is not explicitly tested here. If things are broken, look above here first.
|
515
|
+
|
516
|
+
it 'can serialize a nil object' do
|
517
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(nil)).to eq({'data' => nil})
|
518
|
+
end
|
519
|
+
|
520
|
+
it 'can serialize a nil object with includes' do
|
521
|
+
# Also, the include argument is not validated in this case because we don't know the type.
|
522
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(nil, include: ['fake'])
|
523
|
+
expect(data).to eq({'data' => nil, 'included' => []})
|
524
|
+
end
|
525
|
+
|
526
|
+
it 'can serialize an empty array' do
|
527
|
+
# Also, the include argument is not validated in this case because we don't know the type.
|
528
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize([], is_collection: true, include: ['fake'])
|
529
|
+
expect(data).to eq({'data' => [], 'included' => []})
|
530
|
+
end
|
531
|
+
|
532
|
+
it 'can serialize a simple object' do
|
533
|
+
post = create(:post)
|
534
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post)).to eq({
|
535
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
536
|
+
})
|
537
|
+
end
|
538
|
+
|
539
|
+
it 'can include a top level jsonapi node' do
|
540
|
+
post = create(:post)
|
541
|
+
jsonapi_version = {'version' => '1.0'}
|
542
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, jsonapi: jsonapi_version)).to eq({
|
543
|
+
'jsonapi' => {'version' => '1.0'},
|
544
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
545
|
+
})
|
546
|
+
end
|
547
|
+
|
548
|
+
it 'can include a top level meta node' do
|
549
|
+
post = create(:post)
|
550
|
+
meta = {authors: ['Yehuda Katz', 'Steve Klabnik'], copyright: 'Copyright 2015 Example Corp.'}
|
551
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, meta: meta)).to eq({
|
552
|
+
'meta' => meta,
|
553
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
554
|
+
})
|
555
|
+
end
|
556
|
+
|
557
|
+
it 'can include a top level links node' do
|
558
|
+
post = create(:post)
|
559
|
+
links = {self: 'http://example.com/posts'}
|
560
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, links: links)).to eq({
|
561
|
+
'links' => links,
|
562
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
563
|
+
})
|
564
|
+
end
|
565
|
+
|
566
|
+
# TODO: remove this code on next major release
|
567
|
+
it 'can include a top level errors node - deprecated' do
|
568
|
+
post = create(:post)
|
569
|
+
errors = [
|
570
|
+
{
|
571
|
+
"source" => { "pointer" => "/data/attributes/first-name" },
|
572
|
+
"title" => "Invalid Attribute",
|
573
|
+
"detail" => "First name must contain at least three characters."
|
574
|
+
},
|
575
|
+
{
|
576
|
+
"source" => { "pointer" => "/data/attributes/first-name" },
|
577
|
+
"title" => "Invalid Attribute",
|
578
|
+
"detail" => "First name must contain an emoji."
|
579
|
+
}
|
580
|
+
]
|
581
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, errors: errors)).to eq({
|
582
|
+
'errors' => errors,
|
583
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
584
|
+
})
|
585
|
+
end
|
586
|
+
|
587
|
+
it 'can serialize a single object with an `each` method by passing skip_collection_check: true' do
|
588
|
+
post = create(:post)
|
589
|
+
post.define_singleton_method(:each) do
|
590
|
+
"defining this just to defeat the duck-type check"
|
591
|
+
end
|
592
|
+
|
593
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, skip_collection_check: true)).to eq({
|
594
|
+
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
|
595
|
+
})
|
596
|
+
end
|
597
|
+
|
598
|
+
it 'can serialize a collection' do
|
599
|
+
posts = create_list(:post, 2)
|
600
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(posts, is_collection: true)).to eq({
|
601
|
+
'data' => [
|
602
|
+
serialize_primary(posts.first, {serializer: MyApp::PostSerializer}),
|
603
|
+
serialize_primary(posts.last, {serializer: MyApp::PostSerializer}),
|
604
|
+
],
|
605
|
+
})
|
606
|
+
end
|
607
|
+
|
608
|
+
it 'raises AmbiguousCollectionError if is_collection is not passed' do
|
609
|
+
posts = create_list(:post, 2)
|
610
|
+
error = ForestAdmin::JSONAPI::Serializer::AmbiguousCollectionError
|
611
|
+
expect { ForestAdmin::JSONAPI::Serializer.serialize(posts) }.to raise_error(error)
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'raises error if include is not named correctly' do
|
615
|
+
post = create(:post)
|
616
|
+
error = ForestAdmin::JSONAPI::Serializer::InvalidIncludeError
|
617
|
+
expect { ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['long_comments']) }.to raise_error(error)
|
618
|
+
end
|
619
|
+
|
620
|
+
it 'can serialize a nil object when given serializer' do
|
621
|
+
options = {serializer: MyApp::PostSerializer}
|
622
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(nil, options)).to eq({'data' => nil})
|
623
|
+
end
|
624
|
+
|
625
|
+
it 'can serialize an empty array when given serializer' do
|
626
|
+
options = {is_collection: true, serializer: MyApp::PostSerializer}
|
627
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize([], options)).to eq({'data' => []})
|
628
|
+
end
|
629
|
+
|
630
|
+
it 'can serialize a simple object when given serializer' do
|
631
|
+
post = create(:post)
|
632
|
+
options = {serializer: MyApp::SimplestPostSerializer}
|
633
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, options)).to eq({
|
634
|
+
'data' => serialize_primary(post, {serializer: MyApp::SimplestPostSerializer}),
|
635
|
+
})
|
636
|
+
end
|
637
|
+
|
638
|
+
it 'handles include of nil to-one relationship with compound document' do
|
639
|
+
post = create(:post)
|
640
|
+
|
641
|
+
expected_primary_data = serialize_primary(post, {
|
642
|
+
serializer: MyApp::PostSerializer,
|
643
|
+
include_linkages: ['author'],
|
644
|
+
})
|
645
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['author'])).to eq({
|
646
|
+
'data' => expected_primary_data,
|
647
|
+
'included' => [],
|
648
|
+
})
|
649
|
+
end
|
650
|
+
|
651
|
+
it 'handles include of simple to-one relationship with compound document' do
|
652
|
+
post = create(:post, :with_author)
|
653
|
+
|
654
|
+
expected_primary_data = serialize_primary(post, {
|
655
|
+
serializer: MyApp::PostSerializer,
|
656
|
+
include_linkages: ['author'],
|
657
|
+
})
|
658
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['author'])).to eq({
|
659
|
+
'data' => expected_primary_data,
|
660
|
+
'included' => [
|
661
|
+
serialize_primary(post.author, {serializer: MyAppOtherNamespace::UserSerializer}),
|
662
|
+
],
|
663
|
+
})
|
664
|
+
end
|
665
|
+
|
666
|
+
it 'handles include of empty to-many relationships with compound document' do
|
667
|
+
post = create(:post, :with_author, long_comments: [])
|
668
|
+
|
669
|
+
expected_primary_data = serialize_primary(post, {
|
670
|
+
serializer: MyApp::PostSerializer,
|
671
|
+
include_linkages: ['long-comments'],
|
672
|
+
})
|
673
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
674
|
+
'data' => expected_primary_data,
|
675
|
+
'included' => [],
|
676
|
+
})
|
677
|
+
end
|
678
|
+
|
679
|
+
it 'handles include of to-many relationships with compound document' do
|
680
|
+
long_comments = create_list(:long_comment, 2)
|
681
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
682
|
+
|
683
|
+
expected_primary_data = serialize_primary(post, {
|
684
|
+
serializer: MyApp::PostSerializer,
|
685
|
+
include_linkages: ['long-comments'],
|
686
|
+
})
|
687
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
688
|
+
'data' => expected_primary_data,
|
689
|
+
'included' => [
|
690
|
+
serialize_primary(long_comments.first, {serializer: MyApp::LongCommentSerializer}),
|
691
|
+
serialize_primary(long_comments.last, {serializer: MyApp::LongCommentSerializer}),
|
692
|
+
],
|
693
|
+
})
|
694
|
+
end
|
695
|
+
|
696
|
+
it 'only includes one copy of each referenced relationship' do
|
697
|
+
long_comment = create(:long_comment)
|
698
|
+
long_comments = [long_comment, long_comment]
|
699
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
700
|
+
|
701
|
+
expected_primary_data = serialize_primary(post, {
|
702
|
+
serializer: MyApp::PostSerializer,
|
703
|
+
include_linkages: ['long-comments'],
|
704
|
+
})
|
705
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
706
|
+
'data' => expected_primary_data,
|
707
|
+
'included' => [
|
708
|
+
serialize_primary(long_comment, {serializer: MyApp::LongCommentSerializer}),
|
709
|
+
],
|
710
|
+
})
|
711
|
+
end
|
712
|
+
|
713
|
+
it 'handles circular-referencing relationships with compound document' do
|
714
|
+
long_comments = create_list(:long_comment, 2)
|
715
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
716
|
+
|
717
|
+
# Make sure each long-comment has a circular reference back to the post.
|
718
|
+
long_comments.each { |c| c.post = post }
|
719
|
+
|
720
|
+
expected_primary_data = serialize_primary(post, {
|
721
|
+
serializer: MyApp::PostSerializer,
|
722
|
+
include_linkages: ['long-comments'],
|
723
|
+
})
|
724
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
|
725
|
+
'data' => expected_primary_data,
|
726
|
+
'included' => [
|
727
|
+
serialize_primary(post.long_comments.first, {serializer: MyApp::LongCommentSerializer}),
|
728
|
+
serialize_primary(post.long_comments.last, {serializer: MyApp::LongCommentSerializer}),
|
729
|
+
],
|
730
|
+
})
|
731
|
+
end
|
732
|
+
|
733
|
+
it 'errors if include is not a defined attribute' do
|
734
|
+
user = create(:user)
|
735
|
+
expect { ForestAdmin::JSONAPI::Serializer.serialize(user, include: ['fake-attr']) }.to raise_error
|
736
|
+
end
|
737
|
+
|
738
|
+
it 'handles recursive loading of relationships' do
|
739
|
+
user = create(:user)
|
740
|
+
long_comments = create_list(:long_comment, 2, user: user)
|
741
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
742
|
+
# Make sure each long-comment has a circular reference back to the post.
|
743
|
+
long_comments.each { |c| c.post = post }
|
744
|
+
|
745
|
+
expected_data = {
|
746
|
+
'data' => serialize_primary(post, {
|
747
|
+
serializer: MyApp::PostSerializer,
|
748
|
+
include_linkages: ['long-comments']
|
749
|
+
}),
|
750
|
+
'included' => [
|
751
|
+
# Intermediates are included: long-comments, long-comments.post, and long-comments.post.author
|
752
|
+
# http://jsonapi.org/format/#document-structure-compound-documents
|
753
|
+
serialize_primary(post.long_comments.first, {
|
754
|
+
serializer: MyApp::LongCommentSerializer,
|
755
|
+
include_linkages: ['post']
|
756
|
+
}),
|
757
|
+
serialize_primary(post.long_comments.last, {
|
758
|
+
serializer: MyApp::LongCommentSerializer,
|
759
|
+
include_linkages: ['post']
|
760
|
+
}),
|
761
|
+
serialize_primary(post, {
|
762
|
+
serializer: MyApp::PostSerializer,
|
763
|
+
include_linkages: ['author', 'post.long-comments', ]
|
764
|
+
}),
|
765
|
+
serialize_primary(post.author, {serializer: MyAppOtherNamespace::UserSerializer})
|
766
|
+
],
|
767
|
+
}
|
768
|
+
includes = ['long-comments.post.author']
|
769
|
+
actual_data = ForestAdmin::JSONAPI::Serializer.serialize(post, include: includes)
|
770
|
+
# Multiple expectations for better diff output for debugging.
|
771
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
772
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
773
|
+
expect(actual_data).to eq(expected_data)
|
774
|
+
end
|
775
|
+
|
776
|
+
it 'handles recursive loading of multiple to-one relationships on children' do
|
777
|
+
first_user = create(:user)
|
778
|
+
second_user = create(:user)
|
779
|
+
first_comment = create(:long_comment, user: first_user)
|
780
|
+
second_comment = create(:long_comment, user: second_user)
|
781
|
+
long_comments = [first_comment, second_comment]
|
782
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
783
|
+
# Make sure each long-comment has a circular reference back to the post.
|
784
|
+
long_comments.each { |c| c.post = post }
|
785
|
+
|
786
|
+
expected_data = {
|
787
|
+
'data' => serialize_primary(post, {
|
788
|
+
serializer: MyApp::PostSerializer,
|
789
|
+
include_linkages: ['long-comments']
|
790
|
+
}),
|
791
|
+
'included' => [
|
792
|
+
serialize_primary(first_comment, {
|
793
|
+
serializer: MyApp::LongCommentSerializer,
|
794
|
+
include_linkages: ['user']
|
795
|
+
}),
|
796
|
+
serialize_primary(second_comment, {
|
797
|
+
serializer: MyApp::LongCommentSerializer,
|
798
|
+
include_linkages: ['user']
|
799
|
+
}),
|
800
|
+
serialize_primary(first_user, {serializer: MyAppOtherNamespace::UserSerializer}),
|
801
|
+
serialize_primary(second_user, {serializer: MyAppOtherNamespace::UserSerializer}),
|
802
|
+
],
|
803
|
+
}
|
804
|
+
|
805
|
+
includes = ['long-comments.user']
|
806
|
+
actual_data = ForestAdmin::JSONAPI::Serializer.serialize(post, include: includes)
|
807
|
+
|
808
|
+
# Multiple expectations for better diff output for debugging.
|
809
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
810
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
811
|
+
expect(actual_data).to eq(expected_data)
|
812
|
+
end
|
813
|
+
|
814
|
+
it 'includes linkage in compounded resources only if the immediate parent was also included' do
|
815
|
+
comment_user = create(:user)
|
816
|
+
long_comments = [create(:long_comment, user: comment_user)]
|
817
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
818
|
+
|
819
|
+
expected_primary_data = serialize_primary(post, {
|
820
|
+
serializer: MyApp::PostSerializer,
|
821
|
+
include_linkages: ['long-comments'],
|
822
|
+
})
|
823
|
+
expected_data = {
|
824
|
+
'data' => expected_primary_data,
|
825
|
+
'included' => [
|
826
|
+
serialize_primary(long_comments.first, {
|
827
|
+
serializer: MyApp::LongCommentSerializer,
|
828
|
+
include_linkages: ['user'],
|
829
|
+
}),
|
830
|
+
# Note: post.author does not show up here because it was not included.
|
831
|
+
serialize_primary(comment_user, {serializer: MyAppOtherNamespace::UserSerializer}),
|
832
|
+
],
|
833
|
+
}
|
834
|
+
includes = ['long-comments.user']
|
835
|
+
actual_data = ForestAdmin::JSONAPI::Serializer.serialize(post, include: includes)
|
836
|
+
|
837
|
+
# Multiple expectations for better diff output for debugging.
|
838
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
839
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
840
|
+
expect(actual_data).to eq(expected_data)
|
841
|
+
end
|
842
|
+
|
843
|
+
it 'handles recursive loading of to-many relationships with overlapping include paths' do
|
844
|
+
user = create(:user)
|
845
|
+
long_comments = create_list(:long_comment, 2, user: user)
|
846
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
847
|
+
# Make sure each long-comment has a circular reference back to the post.
|
848
|
+
long_comments.each { |c| c.post = post }
|
849
|
+
|
850
|
+
expected_primary_data = serialize_primary(post, {
|
851
|
+
serializer: MyApp::PostSerializer,
|
852
|
+
include_linkages: ['long-comments'],
|
853
|
+
})
|
854
|
+
expected_data = {
|
855
|
+
'data' => expected_primary_data,
|
856
|
+
'included' => [
|
857
|
+
serialize_primary(long_comments.first, {
|
858
|
+
serializer: MyApp::LongCommentSerializer,
|
859
|
+
include_linkages: ['post'],
|
860
|
+
}),
|
861
|
+
serialize_primary(long_comments.last, {
|
862
|
+
serializer: MyApp::LongCommentSerializer,
|
863
|
+
include_linkages: ['post'],
|
864
|
+
}),
|
865
|
+
serialize_primary(post, {
|
866
|
+
serializer: MyApp::PostSerializer,
|
867
|
+
include_linkages: ['author'],
|
868
|
+
}),
|
869
|
+
serialize_primary(post.author, {serializer: MyAppOtherNamespace::UserSerializer}),
|
870
|
+
],
|
871
|
+
}
|
872
|
+
# Also test that it handles string include arguments.
|
873
|
+
includes = 'long-comments,long-comments.post.author'
|
874
|
+
actual_data = ForestAdmin::JSONAPI::Serializer.serialize(post, include: includes)
|
875
|
+
|
876
|
+
# Multiple expectations for better diff output for debugging.
|
877
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
878
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
879
|
+
expect(actual_data).to eq(expected_data)
|
880
|
+
end
|
881
|
+
|
882
|
+
context 'on collection' do
|
883
|
+
it 'handles include of has_many relationships with compound document' do
|
884
|
+
long_comments = create_list(:long_comment, 2)
|
885
|
+
posts = create_list(:post, 2, :with_author, long_comments: long_comments)
|
886
|
+
|
887
|
+
expected_primary_data = ForestAdmin::JSONAPI::Serializer.send(:serialize_primary_multi, posts, {
|
888
|
+
serializer: MyApp::PostSerializer,
|
889
|
+
include_linkages: ['long-comments'],
|
890
|
+
})
|
891
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(posts, is_collection: true, include: ['long-comments'])
|
892
|
+
expect(data).to eq({
|
893
|
+
'data' => expected_primary_data,
|
894
|
+
'included' => [
|
895
|
+
serialize_primary(long_comments.first, {serializer: MyApp::LongCommentSerializer}),
|
896
|
+
serialize_primary(long_comments.last, {serializer: MyApp::LongCommentSerializer}),
|
897
|
+
],
|
898
|
+
})
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
context 'sparse fieldsets' do
|
903
|
+
it 'allows to limit fields(attributes) for serialized resource' do
|
904
|
+
first_user = create(:user)
|
905
|
+
second_user = create(:user)
|
906
|
+
first_comment = create(:long_comment, user: first_user)
|
907
|
+
second_comment = create(:long_comment, user: second_user)
|
908
|
+
long_comments = [first_comment, second_comment]
|
909
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
910
|
+
|
911
|
+
serialized_data = ForestAdmin::JSONAPI::Serializer.serialize(post, fields: {posts: :title})
|
912
|
+
expect(serialized_data).to eq ({
|
913
|
+
'data' => {
|
914
|
+
'type' => 'posts',
|
915
|
+
'id' => post.id.to_s,
|
916
|
+
'attributes' => {
|
917
|
+
'title' => post.title,
|
918
|
+
},
|
919
|
+
'links' => {
|
920
|
+
'self' => '/posts/1'
|
921
|
+
}
|
922
|
+
}
|
923
|
+
})
|
924
|
+
end
|
925
|
+
|
926
|
+
it 'allows to limit fields(relationships) for serialized resource' do
|
927
|
+
first_user = create(:user)
|
928
|
+
second_user = create(:user)
|
929
|
+
first_comment = create(:long_comment, user: first_user)
|
930
|
+
second_comment = create(:long_comment, user: second_user)
|
931
|
+
long_comments = [first_comment, second_comment]
|
932
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
933
|
+
|
934
|
+
fields = {posts: 'title,author,long-comments'}
|
935
|
+
serialized_data = ForestAdmin::JSONAPI::Serializer.serialize(post, fields: fields)
|
936
|
+
expect(serialized_data['data']['relationships']).to eq ({
|
937
|
+
'author' => {
|
938
|
+
'links' => {
|
939
|
+
'self' => '/posts/1/relationships/author',
|
940
|
+
'related' => '/posts/1/author'
|
941
|
+
}
|
942
|
+
},
|
943
|
+
'long-comments' => {
|
944
|
+
'links' => {
|
945
|
+
'self' => '/posts/1/relationships/long-comments',
|
946
|
+
'related' => '/posts/1/long-comments'
|
947
|
+
}
|
948
|
+
}
|
949
|
+
})
|
950
|
+
end
|
951
|
+
|
952
|
+
it "allows also to pass specific fields as array instead of comma-separates values" do
|
953
|
+
first_user = create(:user)
|
954
|
+
second_user = create(:user)
|
955
|
+
first_comment = create(:long_comment, user: first_user)
|
956
|
+
second_comment = create(:long_comment, user: second_user)
|
957
|
+
long_comments = [first_comment, second_comment]
|
958
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
959
|
+
|
960
|
+
serialized_data = ForestAdmin::JSONAPI::Serializer.serialize(post, fields: {posts: ['title', 'author']})
|
961
|
+
expect(serialized_data['data']['attributes']).to eq ({
|
962
|
+
'title' => post.title
|
963
|
+
})
|
964
|
+
expect(serialized_data['data']['relationships']).to eq ({
|
965
|
+
'author' => {
|
966
|
+
'links' => {
|
967
|
+
'self' => '/posts/1/relationships/author',
|
968
|
+
'related' => '/posts/1/author'
|
969
|
+
}
|
970
|
+
}
|
971
|
+
})
|
972
|
+
end
|
973
|
+
|
974
|
+
it 'allows to limit fields(attributes and relationships) for included resources' do
|
975
|
+
first_user = create(:user)
|
976
|
+
second_user = create(:user)
|
977
|
+
first_comment = create(:long_comment, user: first_user)
|
978
|
+
second_comment = create(:long_comment, user: second_user)
|
979
|
+
long_comments = [first_comment, second_comment]
|
980
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
981
|
+
|
982
|
+
expected_primary_data = serialize_primary(post, {
|
983
|
+
serializer: MyApp::PostSerializer,
|
984
|
+
include_linkages: ['author'],
|
985
|
+
fields: {'posts' => [:title, :author] }
|
986
|
+
})
|
987
|
+
|
988
|
+
fields = {posts: 'title,author', users: ''}
|
989
|
+
serialized_data = ForestAdmin::JSONAPI::Serializer.serialize(post, fields: fields, include: 'author')
|
990
|
+
expect(serialized_data).to eq ({
|
991
|
+
'data' => expected_primary_data,
|
992
|
+
'included' => [
|
993
|
+
serialize_primary(
|
994
|
+
post.author,
|
995
|
+
serializer: MyAppOtherNamespace::UserSerializer,
|
996
|
+
fields: {'users' => []},
|
997
|
+
)
|
998
|
+
]
|
999
|
+
})
|
1000
|
+
|
1001
|
+
fields = {posts: 'title,author'}
|
1002
|
+
serialized_data = ForestAdmin::JSONAPI::Serializer.serialize(post, fields: fields, include: 'author')
|
1003
|
+
expect(serialized_data).to eq ({
|
1004
|
+
'data' => expected_primary_data,
|
1005
|
+
'included' => [
|
1006
|
+
serialize_primary(post.author, serializer: MyAppOtherNamespace::UserSerializer)
|
1007
|
+
]
|
1008
|
+
})
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
describe 'serialize (class method)' do
|
1014
|
+
it 'delegates to module method but overrides serializer' do
|
1015
|
+
post = create(:post)
|
1016
|
+
expect(MyApp::SimplestPostSerializer.serialize(post)).to eq({
|
1017
|
+
'data' => serialize_primary(post, {serializer: MyApp::SimplestPostSerializer}),
|
1018
|
+
})
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
describe 'internal-only parse_relationship_paths' do
|
1023
|
+
it 'correctly handles empty arrays' do
|
1024
|
+
result = ForestAdmin::JSONAPI::Serializer.send(:parse_relationship_paths, [])
|
1025
|
+
expect(result).to eq({})
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
it 'correctly handles single-level relationship paths' do
|
1029
|
+
result = ForestAdmin::JSONAPI::Serializer.send(:parse_relationship_paths, ['foo'])
|
1030
|
+
expect(result).to eq({
|
1031
|
+
'foo' => {_include: true}
|
1032
|
+
})
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
it 'correctly handles multi-level relationship paths' do
|
1036
|
+
result = ForestAdmin::JSONAPI::Serializer.send(:parse_relationship_paths, ['foo.bar'])
|
1037
|
+
expect(result).to eq({
|
1038
|
+
'foo' => {_include: true, 'bar' => {_include: true}}
|
1039
|
+
})
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
it 'correctly handles multi-level relationship paths with same parent' do
|
1043
|
+
paths = ['foo', 'foo.bar']
|
1044
|
+
result = ForestAdmin::JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
1045
|
+
expect(result).to eq({
|
1046
|
+
'foo' => {_include: true, 'bar' => {_include: true}}
|
1047
|
+
})
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
it 'correctly handles multi-level relationship paths with different parent' do
|
1051
|
+
paths = ['foo', 'bar', 'bar.baz']
|
1052
|
+
result = ForestAdmin::JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
1053
|
+
expect(result).to eq({
|
1054
|
+
'foo' => {_include: true},
|
1055
|
+
'bar' => {_include: true, 'baz' => {_include: true}},
|
1056
|
+
})
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
it 'correctly handles three-leveled path' do
|
1060
|
+
paths = ['foo', 'foo.bar', 'foo.bar.baz']
|
1061
|
+
result = ForestAdmin::JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
1062
|
+
expect(result).to eq({
|
1063
|
+
'foo' => {_include: true, 'bar' => {_include: true, 'baz' => {_include: true}}}
|
1064
|
+
})
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
it 'correctly handles three-leveled path with skipped middle' do
|
1068
|
+
paths = ['foo', 'foo.bar.baz']
|
1069
|
+
result = ForestAdmin::JSONAPI::Serializer.send(:parse_relationship_paths, paths)
|
1070
|
+
expect(result).to eq({
|
1071
|
+
'foo' => {_include: true, 'bar' => {_include: true, 'baz' => {_include: true}}}
|
1072
|
+
})
|
1073
|
+
end
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
describe 'if/unless handling with contexts' do
|
1077
|
+
it 'can be used to show/hide attributes' do
|
1078
|
+
post = create(:post)
|
1079
|
+
options = {serializer: MyApp::PostSerializerWithContext}
|
1080
|
+
|
1081
|
+
options[:context] = {show_body: false}
|
1082
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1083
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
1084
|
+
|
1085
|
+
options[:context] = {show_body: true}
|
1086
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1087
|
+
expect(data['data']['attributes']).to have_key('body')
|
1088
|
+
expect(data['data']['attributes']['body']).to eq('Body for Post 1')
|
1089
|
+
|
1090
|
+
options[:context] = {hide_body: true}
|
1091
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1092
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
1093
|
+
|
1094
|
+
options[:context] = {hide_body: false}
|
1095
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1096
|
+
expect(data['data']['attributes']).to have_key('body')
|
1097
|
+
expect(data['data']['attributes']['body']).to eq('Body for Post 1')
|
1098
|
+
|
1099
|
+
options[:context] = {show_body: false, hide_body: false}
|
1100
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1101
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
1102
|
+
|
1103
|
+
options[:context] = {show_body: true, hide_body: false}
|
1104
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1105
|
+
expect(data['data']['attributes']).to have_key('body')
|
1106
|
+
expect(data['data']['attributes']['body']).to eq('Body for Post 1')
|
1107
|
+
|
1108
|
+
# Remember: attribute is configured as if: show_body?, unless: hide_body?
|
1109
|
+
# and the results should be logically AND'd together:
|
1110
|
+
options[:context] = {show_body: false, hide_body: true}
|
1111
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1112
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
1113
|
+
|
1114
|
+
options[:context] = {show_body: true, hide_body: true}
|
1115
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, options)
|
1116
|
+
expect(data['data']['attributes']).to_not have_key('body')
|
1117
|
+
end
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
describe 'context' do
|
1121
|
+
it 'is passed through all relationship serializers' do
|
1122
|
+
# Force long_comments to be serialized by the context-sensitive serializer.
|
1123
|
+
expect_any_instance_of(MyApp::LongComment).to receive(:jsonapi_serializer_class_name)
|
1124
|
+
.at_least(:once)
|
1125
|
+
.and_return('MyApp::LongCommentsSerializerWithContext')
|
1126
|
+
|
1127
|
+
user = create(:user, name: 'Long Comment Author -- Should Not Be Serialized')
|
1128
|
+
long_comment = create(:long_comment, user: user)
|
1129
|
+
post = create(:post, :with_author, long_comments: [long_comment])
|
1130
|
+
|
1131
|
+
context = {show_body: false, show_comments_user: false}
|
1132
|
+
expected_data = {
|
1133
|
+
'data' => serialize_primary(post, {
|
1134
|
+
serializer: MyApp::PostSerializerWithContext,
|
1135
|
+
include_linkages: ['long-comments'],
|
1136
|
+
context: context,
|
1137
|
+
}),
|
1138
|
+
'included' => [
|
1139
|
+
serialize_primary(long_comment, {
|
1140
|
+
serializer: MyApp::LongCommentsSerializerWithContext,
|
1141
|
+
context: context,
|
1142
|
+
}),
|
1143
|
+
],
|
1144
|
+
}
|
1145
|
+
includes = ['long-comments']
|
1146
|
+
actual_data = ForestAdmin::JSONAPI::Serializer.serialize(post, context: context, include: includes)
|
1147
|
+
# Multiple expectations for better diff output for debugging.
|
1148
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
1149
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
1150
|
+
expect(actual_data).to eq(expected_data)
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
describe 'base_url' do
|
1155
|
+
it 'is empty by default' do
|
1156
|
+
long_comments = create_list(:long_comment, 1)
|
1157
|
+
post = create(:post, long_comments: long_comments)
|
1158
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post)
|
1159
|
+
expect(data['data']['links']['self']).to eq('/posts/1')
|
1160
|
+
expect(data['data']['relationships']['author']['links']).to eq({
|
1161
|
+
'self' => '/posts/1/relationships/author',
|
1162
|
+
'related' => '/posts/1/author'
|
1163
|
+
})
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
it 'adds base_url to links if passed' do
|
1167
|
+
long_comments = create_list(:long_comment, 1)
|
1168
|
+
post = create(:post, long_comments: long_comments)
|
1169
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, base_url: 'http://example.com')
|
1170
|
+
expect(data['data']['links']['self']).to eq('http://example.com/posts/1')
|
1171
|
+
expect(data['data']['relationships']['author']['links']).to eq({
|
1172
|
+
'self' => 'http://example.com/posts/1/relationships/author',
|
1173
|
+
'related' => 'http://example.com/posts/1/author'
|
1174
|
+
})
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
it 'uses overriden base_url method if it exists' do
|
1178
|
+
long_comments = create_list(:long_comment, 1)
|
1179
|
+
post = create(:post, long_comments: long_comments)
|
1180
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(post, serializer: MyApp::PostSerializerWithBaseUrl)
|
1181
|
+
expect(data['data']['links']['self']).to eq('http://example.com/posts/1')
|
1182
|
+
expect(data['data']['relationships']['author']['links']).to eq({
|
1183
|
+
'self' => 'http://example.com/posts/1/relationships/author',
|
1184
|
+
'related' => 'http://example.com/posts/1/author'
|
1185
|
+
})
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
describe 'inheritance through subclassing' do
|
1190
|
+
it 'inherits attributes' do
|
1191
|
+
tagged_post = create(:tagged_post)
|
1192
|
+
options = {serializer: MyApp::PostSerializerWithInheritedProperties}
|
1193
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(tagged_post, options);
|
1194
|
+
expect(data['data']['attributes']['title']).to eq('Title for TaggedPost 1');
|
1195
|
+
expect(data['data']['attributes']['tag']).to eq('Tag for TaggedPost 1');
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
it 'inherits relations' do
|
1199
|
+
long_comments = create_list(:long_comment, 2)
|
1200
|
+
tagged_post = create(:tagged_post, :with_author, long_comments: long_comments)
|
1201
|
+
options = {serializer: MyApp::PostSerializerWithInheritedProperties}
|
1202
|
+
data = ForestAdmin::JSONAPI::Serializer.serialize(tagged_post, options);
|
1203
|
+
|
1204
|
+
expect(data['data']['relationships']).to eq({
|
1205
|
+
'author' => {
|
1206
|
+
'links' => {
|
1207
|
+
'self' => '/tagged-posts/1/relationships/author',
|
1208
|
+
'related' => '/tagged-posts/1/author',
|
1209
|
+
},
|
1210
|
+
},
|
1211
|
+
'long-comments' => {
|
1212
|
+
'links' => {
|
1213
|
+
'self' => '/tagged-posts/1/relationships/long-comments',
|
1214
|
+
'related' => '/tagged-posts/1/long-comments',
|
1215
|
+
}
|
1216
|
+
}
|
1217
|
+
})
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
describe 'include validation' do
|
1222
|
+
it 'raises an exception when join character is invalid' do
|
1223
|
+
expect do
|
1224
|
+
ForestAdmin::JSONAPI::Serializer.serialize(create(:post), include: 'long_comments');
|
1225
|
+
end.to raise_error(ForestAdmin::JSONAPI::Serializer::InvalidIncludeError)
|
1226
|
+
|
1227
|
+
expect do
|
1228
|
+
ForestAdmin::JSONAPI::Serializer.serialize(create(:post), include: 'long-comments');
|
1229
|
+
end.not_to raise_error
|
1230
|
+
|
1231
|
+
expect do
|
1232
|
+
ForestAdmin::JSONAPI::Serializer.serialize(create(:underscore_test), include: 'tagged-posts');
|
1233
|
+
end.to raise_error(ForestAdmin::JSONAPI::Serializer::InvalidIncludeError)
|
1234
|
+
|
1235
|
+
expect do
|
1236
|
+
ForestAdmin::JSONAPI::Serializer.serialize(create(:underscore_test), include: 'tagged_posts');
|
1237
|
+
end.not_to raise_error
|
1238
|
+
end
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
describe 'serializer with namespace option' do
|
1242
|
+
it 'can serialize a simple object with namespace Api::V1' do
|
1243
|
+
user = create(:user)
|
1244
|
+
expect(ForestAdmin::JSONAPI::Serializer.serialize(user, {namespace: Api::V1})).to eq({
|
1245
|
+
'data' => serialize_primary(user, {serializer: Api::V1::MyApp::UserSerializer}),
|
1246
|
+
})
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
it 'handles recursive loading of relationships with namespaces' do
|
1250
|
+
user = create(:user)
|
1251
|
+
long_comments = create_list(:long_comment, 2, user: user)
|
1252
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
1253
|
+
# Make sure each long-comment has a circular reference back to the post.
|
1254
|
+
long_comments.each { |c| c.post = post }
|
1255
|
+
|
1256
|
+
expected_data = {
|
1257
|
+
'data' => serialize_primary(post, {
|
1258
|
+
serializer: Api::V1::MyApp::PostSerializer,
|
1259
|
+
include_linkages: ['long-comments']
|
1260
|
+
}),
|
1261
|
+
'included' => [
|
1262
|
+
# Intermediates are included: long-comments, long-comments.post, and long-comments.post.author
|
1263
|
+
# http://jsonapi.org/format/#document-structure-compound-documents
|
1264
|
+
serialize_primary(post.long_comments.first, {
|
1265
|
+
serializer: Api::V1::MyApp::LongCommentSerializer,
|
1266
|
+
include_linkages: ['post']
|
1267
|
+
}),
|
1268
|
+
serialize_primary(post.long_comments.last, {
|
1269
|
+
serializer: Api::V1::MyApp::LongCommentSerializer,
|
1270
|
+
include_linkages: ['post']
|
1271
|
+
}),
|
1272
|
+
serialize_primary(post, {
|
1273
|
+
serializer: Api::V1::MyApp::PostSerializer,
|
1274
|
+
include_linkages: ['author', 'post.long-comments']
|
1275
|
+
}),
|
1276
|
+
serialize_primary(post.author, {serializer: Api::V1::MyApp::UserSerializer})
|
1277
|
+
],
|
1278
|
+
}
|
1279
|
+
includes = ['long-comments.post.author']
|
1280
|
+
actual_data = ForestAdmin::JSONAPI::Serializer.serialize(post, include: includes, namespace: Api::V1)
|
1281
|
+
# Multiple expectations for better diff output for debugging.
|
1282
|
+
expect(actual_data['data']).to eq(expected_data['data'])
|
1283
|
+
expect(actual_data['included']).to eq(expected_data['included'])
|
1284
|
+
expect(actual_data).to eq(expected_data)
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
describe 'instrumentation' do
|
1289
|
+
let(:post) { create(:post, :with_author) }
|
1290
|
+
let(:events) { [] }
|
1291
|
+
|
1292
|
+
before do
|
1293
|
+
ActiveSupport::Notifications.subscribe(notification_name) do |*args|
|
1294
|
+
events << ActiveSupport::Notifications::Event.new(*args)
|
1295
|
+
end
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
describe 'serialize_primary' do
|
1299
|
+
let(:notification_name) { 'render.jsonapi_serializers.serialize_primary' }
|
1300
|
+
|
1301
|
+
it 'sends an event for a single serialize call' do
|
1302
|
+
ForestAdmin::JSONAPI::Serializer.serialize(post)
|
1303
|
+
|
1304
|
+
expect(events.length).to eq(1)
|
1305
|
+
expect(events[0].name).to eq(notification_name)
|
1306
|
+
expect(events[0].payload).to eq({class_name: "MyApp::Post"})
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
it 'sends events for includes' do
|
1310
|
+
ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['author'])
|
1311
|
+
|
1312
|
+
expect(events.length).to eq(2)
|
1313
|
+
expect(events[0].payload).to eq({class_name: "MyApp::Post"})
|
1314
|
+
expect(events[1].payload).to eq({class_name: "MyApp::User"})
|
1315
|
+
end
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
describe 'find_recursive_relationships' do
|
1319
|
+
let(:notification_name) { 'render.jsonapi_serializers.find_recursive_relationships' }
|
1320
|
+
|
1321
|
+
it 'does not send event when there are no includes' do
|
1322
|
+
ForestAdmin::JSONAPI::Serializer.serialize(post)
|
1323
|
+
expect(events.length).to eq(0)
|
1324
|
+
end
|
1325
|
+
|
1326
|
+
it 'sends events for includes' do
|
1327
|
+
ForestAdmin::JSONAPI::Serializer.serialize(post, include: ['author'])
|
1328
|
+
|
1329
|
+
expect(events.length).to eq(2)
|
1330
|
+
expect(events[0].name).to eq(notification_name)
|
1331
|
+
expect(events[0].payload).to eq({class_name: "MyApp::User"})
|
1332
|
+
expect(events[1].name).to eq(notification_name)
|
1333
|
+
expect(events[1].payload).to eq({class_name: "MyApp::Post"})
|
1334
|
+
end
|
1335
|
+
end
|
1336
|
+
end
|
1337
|
+
end
|