wcc-contentful 0.4.0.pre.rc → 1.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/Guardfile +43 -0
  3. data/README.md +101 -12
  4. data/app/controllers/wcc/contentful/webhook_controller.rb +25 -24
  5. data/app/jobs/wcc/contentful/webhook_enable_job.rb +36 -2
  6. data/config/routes.rb +1 -1
  7. data/doc/wcc-contentful.png +0 -0
  8. data/lib/tasks/download_schema.rake +12 -0
  9. data/lib/wcc/contentful.rb +70 -16
  10. data/lib/wcc/contentful/active_record_shim.rb +72 -0
  11. data/lib/wcc/contentful/configuration.rb +177 -46
  12. data/lib/wcc/contentful/content_type_indexer.rb +14 -0
  13. data/lib/wcc/contentful/downloads_schema.rb +112 -0
  14. data/lib/wcc/contentful/engine.rb +33 -14
  15. data/lib/wcc/contentful/event.rb +171 -0
  16. data/lib/wcc/contentful/events.rb +41 -0
  17. data/lib/wcc/contentful/exceptions.rb +3 -0
  18. data/lib/wcc/contentful/indexed_representation.rb +2 -2
  19. data/lib/wcc/contentful/instrumentation.rb +31 -0
  20. data/lib/wcc/contentful/link.rb +28 -0
  21. data/lib/wcc/contentful/link_visitor.rb +122 -0
  22. data/lib/wcc/contentful/middleware.rb +7 -0
  23. data/lib/wcc/contentful/middleware/store.rb +158 -0
  24. data/lib/wcc/contentful/middleware/store/caching_middleware.rb +114 -0
  25. data/lib/wcc/contentful/model.rb +37 -3
  26. data/lib/wcc/contentful/model_builder.rb +1 -0
  27. data/lib/wcc/contentful/model_methods.rb +40 -15
  28. data/lib/wcc/contentful/model_singleton_methods.rb +47 -30
  29. data/lib/wcc/contentful/rake.rb +3 -0
  30. data/lib/wcc/contentful/rspec.rb +13 -8
  31. data/lib/wcc/contentful/services.rb +61 -27
  32. data/lib/wcc/contentful/simple_client.rb +81 -25
  33. data/lib/wcc/contentful/simple_client/management.rb +43 -10
  34. data/lib/wcc/contentful/simple_client/response.rb +61 -22
  35. data/lib/wcc/contentful/simple_client/typhoeus_adapter.rb +17 -17
  36. data/lib/wcc/contentful/store.rb +7 -66
  37. data/lib/wcc/contentful/store/README.md +85 -0
  38. data/lib/wcc/contentful/store/base.rb +34 -119
  39. data/lib/wcc/contentful/store/cdn_adapter.rb +71 -12
  40. data/lib/wcc/contentful/store/factory.rb +186 -0
  41. data/lib/wcc/contentful/store/instrumentation.rb +55 -0
  42. data/lib/wcc/contentful/store/interface.rb +82 -0
  43. data/lib/wcc/contentful/store/memory_store.rb +27 -24
  44. data/lib/wcc/contentful/store/postgres_store.rb +253 -107
  45. data/lib/wcc/contentful/store/postgres_store/schema_1.sql +73 -0
  46. data/lib/wcc/contentful/store/postgres_store/schema_2.sql +21 -0
  47. data/lib/wcc/contentful/store/query.rb +246 -0
  48. data/lib/wcc/contentful/store/query/interface.rb +63 -0
  49. data/lib/wcc/contentful/store/rspec_examples.rb +48 -0
  50. data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +629 -0
  51. data/lib/wcc/contentful/store/rspec_examples/include_param.rb +283 -0
  52. data/lib/wcc/contentful/store/rspec_examples/nested_queries.rb +342 -0
  53. data/lib/wcc/contentful/sync_engine.rb +181 -0
  54. data/lib/wcc/contentful/test/attributes.rb +17 -5
  55. data/lib/wcc/contentful/test/factory.rb +22 -46
  56. data/lib/wcc/contentful/version.rb +1 -1
  57. data/wcc-contentful.gemspec +14 -11
  58. metadata +201 -146
  59. data/Gemfile +0 -6
  60. data/app/jobs/wcc/contentful/delayed_sync_job.rb +0 -63
  61. data/lib/wcc/contentful/client_ext.rb +0 -28
  62. data/lib/wcc/contentful/graphql.rb +0 -14
  63. data/lib/wcc/contentful/graphql/builder.rb +0 -177
  64. data/lib/wcc/contentful/graphql/types.rb +0 -54
  65. data/lib/wcc/contentful/simple_client/http_adapter.rb +0 -24
  66. data/lib/wcc/contentful/store/lazy_cache_store.rb +0 -161
@@ -0,0 +1,629 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/BlockDelimiters
4
+
5
+ RSpec.shared_examples 'basic store' do
6
+ let(:entry) do
7
+ JSON.parse(<<~JSON)
8
+ {
9
+ "sys": {
10
+ "space": {
11
+ "sys": {
12
+ "type": "Link",
13
+ "linkType": "Space",
14
+ "id": "343qxys30lid"
15
+ }
16
+ },
17
+ "id": "1qLdW7i7g4Ycq6i4Cckg44",
18
+ "type": "Entry",
19
+ "createdAt": "2018-03-09T23:39:27.737Z",
20
+ "updatedAt": "2018-03-09T23:39:27.737Z",
21
+ "revision": 1,
22
+ "contentType": {
23
+ "sys": {
24
+ "type": "Link",
25
+ "linkType": "ContentType",
26
+ "id": "redirect"
27
+ }
28
+ }
29
+ },
30
+ "fields": {
31
+ "slug": {
32
+ "en-US": "redirect-with-slug-and-url"
33
+ },
34
+ "url": {
35
+ "en-US": "http://www.google.com"
36
+ },
37
+ "page": {
38
+ "en-US": {
39
+ "sys": {
40
+ "type": "Link",
41
+ "linkType": "Entry",
42
+ "id": "2zKTmej544IakmIqoEu0y8"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ JSON
49
+ end
50
+
51
+ let(:page) do
52
+ JSON.parse(<<~JSON)
53
+ {
54
+ "sys": {
55
+ "space": {
56
+ "sys": {
57
+ "type": "Link",
58
+ "linkType": "Space",
59
+ "id": "343qxys30lid"
60
+ }
61
+ },
62
+ "id": "2zKTmej544IakmIqoEu0y8",
63
+ "type": "Entry",
64
+ "createdAt": "2018-03-09T23:39:27.737Z",
65
+ "updatedAt": "2018-03-09T23:39:27.737Z",
66
+ "revision": 1,
67
+ "contentType": {
68
+ "sys": {
69
+ "type": "Link",
70
+ "linkType": "ContentType",
71
+ "id": "page"
72
+ }
73
+ }
74
+ },
75
+ "fields": {
76
+ "slug": {
77
+ "en-US": "some-page"
78
+ },
79
+ "hero": {
80
+ "en-US": {
81
+ "sys": {
82
+ "type": "Link",
83
+ "linkType": "Asset",
84
+ "id": "3pWma8spR62aegAWAWacyA"
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ JSON
91
+ end
92
+
93
+ let(:asset) do
94
+ JSON.parse(<<~JSON)
95
+ {
96
+ "sys": {
97
+ "space": {
98
+ "sys": {
99
+ "type": "Link",
100
+ "linkType": "Space",
101
+ "id": "343qxys30lid"
102
+ }
103
+ },
104
+ "id": "3pWma8spR62aegAWAWacyA",
105
+ "type": "Asset",
106
+ "createdAt": "2018-02-12T19:53:39.309Z",
107
+ "updatedAt": "2018-02-12T19:53:39.309Z",
108
+ "revision": 1
109
+ },
110
+ "fields": {
111
+ "title": {
112
+ "en-US": "apple-touch-icon"
113
+ },
114
+ "file": {
115
+ "en-US": {
116
+ "url": "//images.contentful.com/343qxys30lid/3pWma8spR62aegAWAWacyA/1beaebf5b66d2405ff9c9769a74db709/apple-touch-icon.png",
117
+ "details": {
118
+ "size": 40832,
119
+ "image": {
120
+ "width": 180,
121
+ "height": 180
122
+ }
123
+ },
124
+ "fileName": "apple-touch-icon.png",
125
+ "contentType": "image/png"
126
+ }
127
+ }
128
+ }
129
+ }
130
+ JSON
131
+ end
132
+
133
+ before do
134
+ allow(WCC::Contentful).to receive(:types)
135
+ .and_return({
136
+ 'root' => double(fields: {
137
+ 'name' => double(name: 'name', type: :String, array: false),
138
+ 'link' => double(name: 'link', type: :Link, array: false),
139
+ 'links' => double(name: 'links', type: :Link, array: true)
140
+ }),
141
+ 'shallow' => double(fields: {
142
+ 'name' => double(name: 'name', type: :String, array: false)
143
+ }),
144
+ 'deep' => double(fields: {
145
+ 'name' => double(name: 'name', type: :String, array: false),
146
+ 'subLink' => double(name: 'subLink', type: :Link, array: false)
147
+ })
148
+ })
149
+ end
150
+
151
+ describe '#set/#find' do
152
+ describe 'ensures that the stored value is of type Hash' do
153
+ it 'should not raise an error if value is a Hash' do
154
+ data = { token: 'jenny_8675309' }
155
+
156
+ # assert
157
+ expect { subject.set('sync:token', data) }.to_not raise_error
158
+ end
159
+
160
+ it 'should raise an error if the value is not a Hash' do
161
+ data = 'jenny_8675309'
162
+ expect { subject.set('sync:token', data) }.to raise_error(ArgumentError)
163
+ end
164
+ end
165
+
166
+ it 'stores and finds data by ID' do
167
+ data = { 'key' => 'val', '1' => { 'deep' => 9 } }
168
+
169
+ # act
170
+ subject.set('1234', data)
171
+ found = subject.find('1234')
172
+
173
+ # assert
174
+ expect(found).to eq(data)
175
+ end
176
+
177
+ it 'find returns nil if key doesnt exist' do
178
+ data = { 'key' => 'val', '1' => { 'deep' => 9 } }
179
+ subject.set('1234', data)
180
+
181
+ # act
182
+ found = subject.find('asdf')
183
+
184
+ # assert
185
+ expect(found).to be_nil
186
+ end
187
+
188
+ it 'find accepts hint param' do
189
+ subject.set('1qLdW7i7g4Ycq6i4Cckg44', entry)
190
+ subject.set('3pWma8spR62aegAWAWacyA', asset)
191
+
192
+ # act
193
+ found_entry = subject.find('1qLdW7i7g4Ycq6i4Cckg44', hint: 'Entry')
194
+ found_asset = subject.find('3pWma8spR62aegAWAWacyA', hint: 'Asset')
195
+
196
+ # assert
197
+ expect(found_entry.dig('sys', 'id')).to eq(entry.dig('sys', 'id'))
198
+ expect(found_asset.dig('sys', 'id')).to eq(asset.dig('sys', 'id'))
199
+ end
200
+
201
+ it 'set returns prior value if exists' do
202
+ data = { 'key' => 'val', '1' => { 'deep' => 9 } }
203
+ data2 = { 'key' => 'val', '2' => { 'deep' => 11 } }
204
+
205
+ # act
206
+ prior1 = subject.set('1234', data)
207
+ prior2 = subject.set('1234', data2)
208
+
209
+ # assert
210
+ expect(prior1).to be_nil
211
+ expect(prior2).to eq(data)
212
+ expect(subject.find('1234')).to eq(data2)
213
+ end
214
+ end
215
+
216
+ describe '#delete' do
217
+ it 'deletes an item out of the store' do
218
+ data = { 'key' => 'val', '1' => { 'deep' => 9 } }
219
+ subject.set('9999', data)
220
+
221
+ # act
222
+ deleted = subject.delete('9999')
223
+
224
+ # assert
225
+ expect(deleted).to eq(data)
226
+ expect(subject.find('9999')).to be_nil
227
+ end
228
+
229
+ it "returns nil if item doesn't exist" do
230
+ data = { 'key' => 'val', '1' => { 'deep' => 9 } }
231
+ subject.set('9999', data)
232
+
233
+ # act
234
+ deleted = subject.delete('asdf')
235
+
236
+ # assert
237
+ expect(deleted).to be_nil
238
+ expect(subject.find('9999')).to eq(data)
239
+ end
240
+ end
241
+
242
+ describe '#index' do
243
+ let(:deleted_entry) do
244
+ JSON.parse(<<~JSON)
245
+ {
246
+ "sys": {
247
+ "space": {
248
+ "sys": {
249
+ "type": "Link",
250
+ "linkType": "Space",
251
+ "id": "343qxys30lid"
252
+ }
253
+ },
254
+ "id": "6HQsABhZDiWmi0ekCouUuy",
255
+ "type": "DeletedEntry",
256
+ "createdAt": "2018-03-13T19:45:44.454Z",
257
+ "updatedAt": "2018-03-13T19:45:44.454Z",
258
+ "deletedAt": "2018-03-13T19:45:44.454Z",
259
+ "environment": {
260
+ "sys": {
261
+ "type": "Link",
262
+ "linkType": "Environment",
263
+ "id": "98322ee2-6dee-4651-b3a5-743be50fb107"
264
+ }
265
+ },
266
+ "revision": 1
267
+ }
268
+ }
269
+ JSON
270
+ end
271
+
272
+ let(:deleted_asset) do
273
+ JSON.parse(<<~JSON)
274
+ {
275
+ "sys": {
276
+ "space": {
277
+ "sys": {
278
+ "type": "Link",
279
+ "linkType": "Space",
280
+ "id": "343qxys30lid"
281
+ }
282
+ },
283
+ "id": "3pWma8spR62aegAWAWacyA",
284
+ "type": "DeletedAsset",
285
+ "createdAt": "2018-03-20T18:44:58.270Z",
286
+ "updatedAt": "2018-03-20T18:44:58.270Z",
287
+ "deletedAt": "2018-03-20T18:44:58.270Z",
288
+ "environment": {
289
+ "sys": {
290
+ "type": "Link",
291
+ "linkType": "Environment",
292
+ "id": "98322ee2-6dee-4651-b3a5-743be50fb107"
293
+ }
294
+ },
295
+ "revision": 1
296
+ }
297
+ }
298
+ JSON
299
+ end
300
+
301
+ it 'stores an "Entry"' do
302
+ # act
303
+ prev = subject.index(entry)
304
+
305
+ # assert
306
+ expect(prev).to eq(entry)
307
+ expect(subject.find('1qLdW7i7g4Ycq6i4Cckg44', hint: 'Entry')).to eq(entry)
308
+ end
309
+
310
+ it 'updates an "Entry" when exists' do
311
+ existing = { 'test' => { 'data' => 'asdf' } }
312
+ subject.set('1qLdW7i7g4Ycq6i4Cckg44', existing)
313
+
314
+ # act
315
+ latest = subject.index(entry)
316
+
317
+ # assert
318
+ expect(latest).to eq(entry)
319
+ expect(subject.find('1qLdW7i7g4Ycq6i4Cckg44')).to eq(entry)
320
+ end
321
+
322
+ it 'does not overwrite an entry if revision is lower' do
323
+ initial = entry
324
+ updated = entry.deep_dup
325
+ updated['sys']['revision'] = 2
326
+ updated['fields']['slug']['en-US'] = 'test slug'
327
+
328
+ subject.index(updated)
329
+
330
+ # act
331
+ latest = subject.index(initial)
332
+
333
+ # assert
334
+ expect(latest).to eq(updated)
335
+ expect(subject.find('1qLdW7i7g4Ycq6i4Cckg44')).to eq(updated)
336
+ end
337
+
338
+ it 'stores an "Asset"' do
339
+ # act
340
+ latest = subject.index(asset)
341
+
342
+ # assert
343
+ expect(latest).to eq(asset)
344
+ expect(subject.find('3pWma8spR62aegAWAWacyA', hint: 'Asset')).to eq(asset)
345
+ end
346
+
347
+ it 'updates an "Asset" when exists' do
348
+ existing = { 'test' => { 'data' => 'asdf' } }
349
+ subject.set('3pWma8spR62aegAWAWacyA', existing)
350
+
351
+ # act
352
+ latest = subject.index(asset)
353
+
354
+ # assert
355
+ expect(latest).to eq(asset)
356
+ expect(subject.find('3pWma8spR62aegAWAWacyA')).to eq(asset)
357
+ end
358
+
359
+ it 'does not overwrite an asset if revision is lower' do
360
+ initial = asset
361
+ updated = asset.deep_dup
362
+ updated['sys']['revision'] = 2
363
+ updated['fields']['title']['en-US'] = 'test title'
364
+
365
+ subject.index(updated)
366
+
367
+ # act
368
+ latest = subject.index(initial)
369
+
370
+ # assert
371
+ expect(latest).to eq(updated)
372
+ expect(subject.find('3pWma8spR62aegAWAWacyA')).to eq(updated)
373
+ end
374
+
375
+ it 'removes a "DeletedEntry"' do
376
+ existing = { 'test' => { 'data' => 'asdf' } }
377
+ subject.set('6HQsABhZDiWmi0ekCouUuy', existing)
378
+
379
+ # act
380
+ latest = subject.index(deleted_entry)
381
+
382
+ # assert
383
+ expect(latest).to be_nil
384
+ expect(subject.find('6HQsABhZDiWmi0ekCouUuy')).to be_nil
385
+ end
386
+
387
+ it 'does not remove if "DeletedEntry" revision is lower' do
388
+ existing = entry
389
+ existing['sys']['id'] = deleted_entry.dig('sys', 'id')
390
+ existing['sys']['revision'] = deleted_entry.dig('sys', 'revision') + 1
391
+ subject.index(existing)
392
+
393
+ # act
394
+ latest = subject.index(deleted_entry)
395
+
396
+ # assert
397
+ expect(latest).to eq(existing)
398
+ expect(subject.find(deleted_entry.dig('sys', 'id'))).to eq(existing)
399
+ end
400
+
401
+ it 'removes a "DeletedAsset"' do
402
+ existing = { 'test' => { 'data' => 'asdf' } }
403
+ subject.set('3pWma8spR62aegAWAWacyA', existing)
404
+
405
+ # act
406
+ latest = subject.index(deleted_asset)
407
+
408
+ # assert
409
+ expect(latest).to be_nil
410
+ expect(subject.find('3pWma8spR62aegAWAWacyA')).to be_nil
411
+ end
412
+
413
+ it 'does not remove if "DeletedAsset" revision is lower' do
414
+ existing = asset
415
+ existing['sys']['id'] = deleted_asset.dig('sys', 'id')
416
+ existing['sys']['revision'] = deleted_asset.dig('sys', 'revision') + 1
417
+ subject.index(existing)
418
+
419
+ # act
420
+ latest = subject.index(deleted_asset)
421
+
422
+ # assert
423
+ expect(latest).to eq(existing)
424
+ expect(subject.find(deleted_asset.dig('sys', 'id'))).to eq(existing)
425
+ end
426
+ end
427
+
428
+ describe '#find_by' do
429
+ it 'finds first of content type' do
430
+ content_types = %w[test1 test2 test3 test4]
431
+ data =
432
+ 1.upto(10).map do |i|
433
+ {
434
+ 'sys' => {
435
+ 'id' => "k#{i}",
436
+ 'contentType' => { 'sys' => { 'id' => content_types[i % content_types.length] } }
437
+ },
438
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
439
+ }
440
+ end
441
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
442
+
443
+ # act
444
+ found = subject.find_by(content_type: 'test3')
445
+
446
+ # assert
447
+ # expect one of these, but order is not defined
448
+ expect(%w[k2 k6 k10]).to include(found.dig('sys', 'id'))
449
+ end
450
+
451
+ it 'returns nil when cant find content type' do
452
+ content_types = %w[test1]
453
+ data =
454
+ 1.upto(4).map do |i|
455
+ {
456
+ 'sys' => {
457
+ 'id' => "k#{i}",
458
+ 'contentType' => { 'sys' => { 'id' => content_types[i % content_types.length] } }
459
+ },
460
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
461
+ }
462
+ end
463
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
464
+
465
+ # act
466
+ nonexistent_content_type = subject.find_by(content_type: 'test2')
467
+
468
+ # assert
469
+ expect(nonexistent_content_type).to be nil
470
+ end
471
+
472
+ it 'can apply filter object' do
473
+ data =
474
+ 1.upto(10).map do |i|
475
+ {
476
+ 'sys' => { 'id' => "k#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
477
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
478
+ }
479
+ end
480
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
481
+
482
+ # act
483
+ found = subject.find_by(content_type: 'test1', filter: { 'name' => 'test4' })
484
+
485
+ # assert
486
+ expect(found).to_not be_nil
487
+ expect(found['sys']['id']).to eq('k4')
488
+ end
489
+
490
+ it 'can find by ID directly' do
491
+ [entry, page, asset].each { |d| subject.set(d.dig('sys', 'id'), d) }
492
+
493
+ # act
494
+ found = subject.find_by(content_type: 'Asset', filter: { id: '3pWma8spR62aegAWAWacyA' })
495
+
496
+ # assert
497
+ expect(found).to eq(asset)
498
+ end
499
+
500
+ it 'filter object can find value in array' do
501
+ content_types = %w[test1 test2 test3 test4]
502
+ data =
503
+ 1.upto(10).map do |i|
504
+ {
505
+ 'sys' => {
506
+ 'id' => "k#{i}",
507
+ 'contentType' => { 'sys' => { 'id' => content_types[i % content_types.length] } }
508
+ },
509
+ 'fields' => { 'name' => { 'en-US' => ["test#{i}", "test_2_#{i}"] } }
510
+ }
511
+ end
512
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
513
+
514
+ # act
515
+ found = subject.find_by(content_type: 'test2', filter: { 'name' => { eq: 'test_2_5' } })
516
+
517
+ # assert
518
+ expect(found).to_not be_nil
519
+ expect(found.dig('sys', 'id')).to eq('k5')
520
+ end
521
+
522
+ it 'allows properties named `*sys*`' do
523
+ %w[One Two].each do |field|
524
+ subject.set("id#{field}", {
525
+ 'sys' => {
526
+ 'id' => "id#{field}",
527
+ 'contentType' => {
528
+ 'sys' => {
529
+ 'id' => 'system'
530
+ }
531
+ }
532
+ },
533
+ 'fields' => {
534
+ 'system' => {
535
+ 'en-US' => field
536
+ }
537
+ }
538
+ })
539
+ end
540
+
541
+ # act
542
+ found = subject.find_by(content_type: 'system', filter: { system: 'Two' })
543
+
544
+ # assert
545
+ expect(found).to_not be_nil
546
+ expect(found.dig('sys', 'id')).to eq('idTwo')
547
+ expect(found.dig('fields', 'system', 'en-US')).to eq('Two')
548
+ end
549
+ end
550
+
551
+ describe '#find_all' do
552
+ it 'find_all can apply filter query' do
553
+ data =
554
+ 1.upto(10).map do |i|
555
+ {
556
+ 'sys' => { 'id' => "k#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
557
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
558
+ }
559
+ end
560
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
561
+
562
+ # act
563
+ found = subject.find_all(content_type: 'test1').eq('name', 'test4')
564
+
565
+ # assert
566
+ expect(found.count).to eq(1)
567
+ expect(found.first['sys']['id']).to eq('k4')
568
+ end
569
+
570
+ it 'find_all filters on content type' do
571
+ content_types = %w[test1 test2 test3 test4]
572
+ data =
573
+ 1.upto(10).map do |i|
574
+ {
575
+ 'sys' => {
576
+ 'id' => "k#{i}",
577
+ 'contentType' => { 'sys' => { 'id' => content_types[i % content_types.length] } }
578
+ },
579
+ 'fields' => { 'name' => { 'en-US' => "test#{i}" } }
580
+ }
581
+ end
582
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
583
+
584
+ # act
585
+ found = subject.find_all(content_type: 'test2').to_a
586
+
587
+ # assert
588
+ expect(found.count).to eq(3)
589
+ expect(found.map { |d| d.dig('sys', 'id') }).to eq(
590
+ %w[k1 k5 k9]
591
+ )
592
+ end
593
+
594
+ it 'filter query eq can find value in array' do
595
+ content_types = %w[test1 test2 test3 test4]
596
+ data =
597
+ 1.upto(10).map do |i|
598
+ {
599
+ 'sys' => {
600
+ 'id' => "k#{i}",
601
+ 'contentType' => { 'sys' => { 'id' => content_types[i % content_types.length] } }
602
+ },
603
+ 'fields' => { 'name' => { 'en-US' => ["test#{i}", "test_2_#{i}"] } }
604
+ }
605
+ end
606
+ data.each { |d| subject.set(d.dig('sys', 'id'), d) }
607
+
608
+ # act
609
+ found = subject.find_all(content_type: 'test2')
610
+ .apply('name' => { eq: 'test_2_5' })
611
+
612
+ # assert
613
+ expect(found.count).to eq(1)
614
+ expect(found.first.dig('sys', 'id')).to eq('k5')
615
+ end
616
+ end
617
+
618
+ def make_link_to(id, link_type = 'Entry')
619
+ {
620
+ 'sys' => {
621
+ 'type' => 'Link',
622
+ 'linkType' => link_type,
623
+ 'id' => id
624
+ }
625
+ }
626
+ end
627
+ end
628
+
629
+ # rubocop:enable Style/BlockDelimiters