wcc-contentful 0.4.0.pre.beta → 1.0.0

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.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/Guardfile +43 -0
  3. data/README.md +205 -17
  4. data/Rakefile +5 -0
  5. data/app/controllers/wcc/contentful/webhook_controller.rb +25 -24
  6. data/app/jobs/wcc/contentful/webhook_enable_job.rb +36 -2
  7. data/config/routes.rb +1 -1
  8. data/doc-static/wcc-contentful.png +0 -0
  9. data/lib/tasks/download_schema.rake +12 -0
  10. data/lib/wcc/contentful.rb +70 -16
  11. data/lib/wcc/contentful/active_record_shim.rb +72 -0
  12. data/lib/wcc/contentful/configuration.rb +177 -46
  13. data/lib/wcc/contentful/content_type_indexer.rb +14 -0
  14. data/lib/wcc/contentful/downloads_schema.rb +112 -0
  15. data/lib/wcc/contentful/engine.rb +33 -14
  16. data/lib/wcc/contentful/event.rb +171 -0
  17. data/lib/wcc/contentful/events.rb +41 -0
  18. data/lib/wcc/contentful/exceptions.rb +3 -0
  19. data/lib/wcc/contentful/indexed_representation.rb +2 -2
  20. data/lib/wcc/contentful/instrumentation.rb +31 -0
  21. data/lib/wcc/contentful/link.rb +28 -0
  22. data/lib/wcc/contentful/link_visitor.rb +122 -0
  23. data/lib/wcc/contentful/middleware.rb +7 -0
  24. data/lib/wcc/contentful/middleware/store.rb +158 -0
  25. data/lib/wcc/contentful/middleware/store/caching_middleware.rb +114 -0
  26. data/lib/wcc/contentful/model.rb +37 -3
  27. data/lib/wcc/contentful/model_builder.rb +1 -0
  28. data/lib/wcc/contentful/model_methods.rb +40 -15
  29. data/lib/wcc/contentful/model_singleton_methods.rb +47 -30
  30. data/lib/wcc/contentful/rake.rb +4 -0
  31. data/lib/wcc/contentful/rspec.rb +13 -8
  32. data/lib/wcc/contentful/services.rb +61 -27
  33. data/lib/wcc/contentful/simple_client.rb +81 -25
  34. data/lib/wcc/contentful/simple_client/management.rb +43 -10
  35. data/lib/wcc/contentful/simple_client/response.rb +61 -22
  36. data/lib/wcc/contentful/simple_client/typhoeus_adapter.rb +17 -17
  37. data/lib/wcc/contentful/store.rb +7 -66
  38. data/lib/wcc/contentful/store/README.md +85 -0
  39. data/lib/wcc/contentful/store/base.rb +34 -119
  40. data/lib/wcc/contentful/store/cdn_adapter.rb +71 -12
  41. data/lib/wcc/contentful/store/factory.rb +186 -0
  42. data/lib/wcc/contentful/store/instrumentation.rb +55 -0
  43. data/lib/wcc/contentful/store/interface.rb +82 -0
  44. data/lib/wcc/contentful/store/memory_store.rb +27 -24
  45. data/lib/wcc/contentful/store/postgres_store.rb +253 -107
  46. data/lib/wcc/contentful/store/postgres_store/schema_1.sql +73 -0
  47. data/lib/wcc/contentful/store/postgres_store/schema_2.sql +21 -0
  48. data/lib/wcc/contentful/store/query.rb +246 -0
  49. data/lib/wcc/contentful/store/query/interface.rb +63 -0
  50. data/lib/wcc/contentful/store/rspec_examples.rb +48 -0
  51. data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +629 -0
  52. data/lib/wcc/contentful/store/rspec_examples/include_param.rb +283 -0
  53. data/lib/wcc/contentful/store/rspec_examples/nested_queries.rb +342 -0
  54. data/lib/wcc/contentful/sync_engine.rb +181 -0
  55. data/lib/wcc/contentful/test/attributes.rb +17 -5
  56. data/lib/wcc/contentful/test/factory.rb +22 -46
  57. data/lib/wcc/contentful/version.rb +1 -1
  58. data/wcc-contentful.gemspec +22 -11
  59. metadata +295 -144
  60. data/Gemfile +0 -6
  61. data/app/jobs/wcc/contentful/delayed_sync_job.rb +0 -63
  62. data/lib/wcc/contentful/client_ext.rb +0 -28
  63. data/lib/wcc/contentful/graphql.rb +0 -14
  64. data/lib/wcc/contentful/graphql/builder.rb +0 -177
  65. data/lib/wcc/contentful/graphql/types.rb +0 -54
  66. data/lib/wcc/contentful/simple_client/http_adapter.rb +0 -24
  67. data/lib/wcc/contentful/store/lazy_cache_store.rb +0 -161
@@ -0,0 +1,283 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'supports include param' do |feature_set|
4
+ describe 'supports options: { include: >0 }' do
5
+ before { skip('include_param feature not supported') } if feature_set == false
6
+ before { pending('include_param feature not yet implemented') } if feature_set&.to_s == 'pending'
7
+
8
+ let(:root) do
9
+ {
10
+ 'sys' => {
11
+ 'id' => 'root',
12
+ 'type' => 'Entry',
13
+ 'contentType' => { 'sys' => { 'id' => 'root' } }
14
+ },
15
+ 'fields' => {
16
+ 'name' => { 'en-US' => 'root' },
17
+ 'link' => { 'en-US' => make_link_to('deep1') },
18
+ 'links' => { 'en-US' => [
19
+ make_link_to('shallow3'),
20
+ make_link_to('deep2')
21
+ ] }
22
+ }
23
+ }
24
+ end
25
+
26
+ def shallow(id = nil) # rubocop:disable Naming/UncommunicativeMethodParamName
27
+ {
28
+ 'sys' => {
29
+ 'id' => "shallow#{id}",
30
+ 'type' => 'Entry',
31
+ 'contentType' => { 'sys' => { 'id' => 'shallow' } }
32
+ },
33
+ 'fields' => { 'name' => { 'en-US' => "shallow#{id}" } }
34
+ }
35
+ end
36
+
37
+ def deep(id, link = nil) # rubocop:disable Naming/UncommunicativeMethodParamName
38
+ {
39
+ 'sys' => {
40
+ 'id' => "deep#{id}",
41
+ 'type' => 'Entry',
42
+ 'contentType' => { 'sys' => { 'id' => 'deep' } }
43
+ },
44
+ 'fields' => {
45
+ 'name' => { 'en-US' => "deep#{id}" },
46
+ 'subLink' => { 'en-US' => link || make_link_to("shallow#{id}") }
47
+ }
48
+ }
49
+ end
50
+
51
+ describe '#find_by' do
52
+ it 'recursively resolves links if include > 0' do
53
+ [
54
+ root,
55
+ *1.upto(3).map { |i| shallow(i) },
56
+ *1.upto(2).map { |i| deep(i) }
57
+ ].each { |d| subject.set(d.dig('sys', 'id'), d) }
58
+
59
+ # act
60
+ found = subject.find_by(content_type: 'root', filter: { name: 'root' }, options: {
61
+ include: 2
62
+ })
63
+
64
+ # assert
65
+ expect(found.dig('sys', 'id')).to eq('root')
66
+
67
+ # depth 1
68
+ link = found.dig('fields', 'link', 'en-US')
69
+ expect(link.dig('fields', 'name', 'en-US')).to eq('deep1')
70
+ links = found.dig('fields', 'links', 'en-US')
71
+ expect(links[0].dig('fields', 'name', 'en-US')).to eq('shallow3')
72
+
73
+ # depth 2
74
+ expect(link.dig('fields', 'subLink', 'en-US', 'fields', 'name', 'en-US'))
75
+ .to eq('shallow1')
76
+ expect(links[1].dig('fields', 'subLink', 'en-US', 'fields', 'name', 'en-US'))
77
+ .to eq('shallow2')
78
+ end
79
+
80
+ it 'stops resolving links at include depth' do
81
+ [
82
+ root,
83
+ *1.upto(3).map { |i| shallow(i) },
84
+ *1.upto(2).map { |i| deep(i) }
85
+ ].each { |d| subject.set(d.dig('sys', 'id'), d) }
86
+
87
+ # act
88
+ found = subject.find_by(content_type: 'root', filter: { name: 'root' }, options: {
89
+ include: 1
90
+ })
91
+
92
+ # assert
93
+ expect(found.dig('sys', 'id')).to eq('root')
94
+
95
+ # depth 1
96
+ link = found.dig('fields', 'link', 'en-US')
97
+ expect(link.dig('fields', 'name', 'en-US')).to eq('deep1')
98
+ links = found.dig('fields', 'links', 'en-US')
99
+ expect(links[0].dig('fields', 'name', 'en-US')).to eq('shallow3')
100
+
101
+ # depth 2
102
+ expect(link.dig('fields', 'subLink', 'en-US', 'sys', 'type'))
103
+ .to eq('Link')
104
+ expect(links[1].dig('fields', 'subLink', 'en-US', 'sys', 'type'))
105
+ .to eq('Link')
106
+ end
107
+
108
+ 1.upto(5).each do |depth|
109
+ it "does not call into #find in order to resolve include: #{depth}" do
110
+ skip("supported up to #{feature_set}") if feature_set.is_a?(Integer) && feature_set < depth
111
+
112
+ items = [root]
113
+ # 1..N
114
+ 1.upto(depth).map do |n|
115
+ items << deep(n, make_link_to("deep#{n + 1}"))
116
+ end
117
+ items.each { |d| subject.set(d.dig('sys', 'id'), d) }
118
+
119
+ # Expect
120
+ expect(subject).to_not receive(:find)
121
+
122
+ # act
123
+ found = subject.find_by(content_type: 'root', filter: { name: 'root' }, options: {
124
+ include: depth
125
+ })
126
+
127
+ link = found.dig('fields', 'link', 'en-US')
128
+ 1.upto(depth).each do |_n|
129
+ expect(link.dig('sys', 'type')).to eq('Entry')
130
+ link = link.dig('fields', 'subLink', 'en-US')
131
+ end
132
+ expect(link.dig('sys', 'type')).to eq('Link')
133
+ end
134
+ end
135
+
136
+ it 'handles recursion' do
137
+ items = [
138
+ deep(0, make_link_to('deep1')),
139
+ deep(1, make_link_to('deep0'))
140
+ ]
141
+ items.each { |d| subject.set(d.dig('sys', 'id'), d) }
142
+
143
+ # act
144
+ r0 = subject.find_by(content_type: 'deep', filter: { id: 'deep0' }, options: {
145
+ include: 4
146
+ })
147
+
148
+ link = r0.dig('fields', 'subLink', 'en-US')
149
+ expect(link.dig('sys', 'type')).to eq('Entry')
150
+ expect(link.dig('sys', 'id')).to eq('deep1')
151
+ link = link.dig('fields', 'subLink', 'en-US')
152
+ expect(link.dig('sys', 'type')).to eq('Entry')
153
+ expect(link.dig('sys', 'id')).to eq('deep0')
154
+ link = link.dig('fields', 'subLink', 'en-US')
155
+ expect(link.dig('sys', 'type')).to eq('Entry')
156
+ expect(link.dig('sys', 'id')).to eq('deep1')
157
+ link = link.dig('fields', 'subLink', 'en-US')
158
+ expect(link.dig('sys', 'type')).to eq('Entry')
159
+ expect(link.dig('sys', 'id')).to eq('deep0')
160
+ end
161
+ end
162
+
163
+ describe '#find_all' do
164
+ it 'recursively resolves links if include > 0' do
165
+ [
166
+ root,
167
+ *1.upto(3).map { |i| shallow(i) },
168
+ *1.upto(2).map { |i| deep(i) }
169
+ ].each { |d| subject.set(d.dig('sys', 'id'), d) }
170
+
171
+ # act
172
+ result = subject.find_all(content_type: 'root', options: {
173
+ include: 2
174
+ }).to_a
175
+
176
+ # assert
177
+ found = result.first
178
+ expect(found.dig('sys', 'id')).to eq('root')
179
+
180
+ # depth 1
181
+ link = found.dig('fields', 'link', 'en-US')
182
+ expect(link.dig('fields', 'name', 'en-US')).to eq('deep1')
183
+ links = found.dig('fields', 'links', 'en-US')
184
+ expect(links[0].dig('fields', 'name', 'en-US')).to eq('shallow3')
185
+
186
+ # depth 2
187
+ expect(link.dig('fields', 'subLink', 'en-US', 'fields', 'name', 'en-US'))
188
+ .to eq('shallow1')
189
+ expect(links[1].dig('fields', 'subLink', 'en-US', 'fields', 'name', 'en-US'))
190
+ .to eq('shallow2')
191
+ end
192
+
193
+ it 'stops resolving links at include depth' do
194
+ [
195
+ root,
196
+ *1.upto(3).map { |i| shallow(i) },
197
+ *1.upto(2).map { |i| deep(i) }
198
+ ].each { |d| subject.set(d.dig('sys', 'id'), d) }
199
+
200
+ # act
201
+ result = subject.find_all(content_type: 'root', options: {
202
+ include: 1
203
+ }).to_a
204
+
205
+ # assert
206
+ found = result.first
207
+ expect(found.dig('sys', 'id')).to eq('root')
208
+
209
+ # depth 1
210
+ link = found.dig('fields', 'link', 'en-US')
211
+ expect(link.dig('fields', 'name', 'en-US')).to eq('deep1')
212
+ links = found.dig('fields', 'links', 'en-US')
213
+ expect(links[0].dig('fields', 'name', 'en-US')).to eq('shallow3')
214
+
215
+ # depth 2
216
+ expect(link.dig('fields', 'subLink', 'en-US', 'sys', 'type'))
217
+ .to eq('Link')
218
+ expect(links[1].dig('fields', 'subLink', 'en-US', 'sys', 'type'))
219
+ .to eq('Link')
220
+ end
221
+
222
+ 1.upto(5).each do |depth|
223
+ it "does not call into #find in order to resolve include: #{depth}" do
224
+ skip("supported up to #{feature_set}") if feature_set.is_a?(Integer) && feature_set < depth
225
+
226
+ # 1..N
227
+ items =
228
+ 0.upto(depth).map do |n|
229
+ deep(n, make_link_to("deep#{n + 1}"))
230
+ end
231
+ items.each { |d| subject.set(d.dig('sys', 'id'), d) }
232
+
233
+ # Expect
234
+ expect(subject).to_not receive(:find)
235
+
236
+ # act
237
+ results = subject.find_all(content_type: 'deep', options: {
238
+ include: depth
239
+ }).to_a
240
+
241
+ results.sort_by { |entry| entry.dig('sys', 'id') }.each_with_index do |found, n|
242
+ link = found.dig('fields', 'subLink', 'en-US')
243
+ 1.upto(depth - n).each do |_n|
244
+ expect(link.dig('sys', 'type')).to eq('Entry')
245
+ link = link.dig('fields', 'subLink', 'en-US')
246
+ end
247
+ expect(link.dig('sys', 'type')).to eq('Link')
248
+ end
249
+ expect(results.length).to eq(items.length)
250
+ end
251
+ end
252
+
253
+ it 'handles recursion' do
254
+ items = [
255
+ deep(0, make_link_to('deep1')),
256
+ deep(1, make_link_to('deep0'))
257
+ ]
258
+ items.each { |d| subject.set(d.dig('sys', 'id'), d) }
259
+
260
+ # act
261
+ results = subject.find_all(content_type: 'deep', options: {
262
+ include: 4
263
+ }).to_a
264
+
265
+ results = results.sort_by { |entry| entry.dig('sys', 'id') }
266
+
267
+ r0 = results[0]
268
+ link = r0.dig('fields', 'subLink', 'en-US')
269
+ expect(link.dig('sys', 'type')).to eq('Entry')
270
+ expect(link.dig('sys', 'id')).to eq('deep1')
271
+ link = link.dig('fields', 'subLink', 'en-US')
272
+ expect(link.dig('sys', 'type')).to eq('Entry')
273
+ expect(link.dig('sys', 'id')).to eq('deep0')
274
+ link = link.dig('fields', 'subLink', 'en-US')
275
+ expect(link.dig('sys', 'type')).to eq('Entry')
276
+ expect(link.dig('sys', 'id')).to eq('deep1')
277
+ link = link.dig('fields', 'subLink', 'en-US')
278
+ expect(link.dig('sys', 'type')).to eq('Entry')
279
+ expect(link.dig('sys', 'id')).to eq('deep0')
280
+ end
281
+ end
282
+ end
283
+ end
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/BlockDelimiters
4
+
5
+ RSpec.shared_examples 'supports nested queries' do |feature_set|
6
+ describe 'nested (join) queries' do
7
+ before { skip('nested_queries feature not supported') } if feature_set == false
8
+ before { pending('nested_queries feature to be implemented') } if feature_set&.to_s == 'pending'
9
+
10
+ let(:team) {
11
+ JSON.parse(<<~JSON)
12
+ {
13
+ "sys": {
14
+ "space": {
15
+ "sys": {
16
+ "type": "Link",
17
+ "linkType": "Space",
18
+ "id": "hw5pse7y1ojx"
19
+ }
20
+ },
21
+ "id": "Team-1234",
22
+ "type": "Entry",
23
+ "createdAt": "2018-03-09T23:39:27.737Z",
24
+ "updatedAt": "2018-03-09T23:39:27.737Z",
25
+ "revision": 1,
26
+ "contentType": {
27
+ "sys": {
28
+ "type": "Link",
29
+ "linkType": "ContentType",
30
+ "id": "team"
31
+ }
32
+ }
33
+ },
34
+ "fields": {
35
+ "name": {
36
+ "en-US": "Dallas Cowboys"
37
+ },
38
+ "members": {
39
+ "en-US": [
40
+ {
41
+ "sys": {
42
+ "type": "Link",
43
+ "linkType": "Entry",
44
+ "id": "Member-1"
45
+ }
46
+ },
47
+ {
48
+ "sys": {
49
+ "type": "Link",
50
+ "linkType": "Entry",
51
+ "id": "Member-2"
52
+ }
53
+ }
54
+ ]
55
+ },
56
+ "owner": {
57
+ "en-US": {
58
+ "sys": {
59
+ "type": "Link",
60
+ "linkType": "Entry",
61
+ "id": "Owner-1"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ JSON
68
+ }
69
+
70
+ let(:member1) {
71
+ JSON.parse(<<~JSON)
72
+ {
73
+ "sys": {
74
+ "space": {
75
+ "sys": {
76
+ "type": "Link",
77
+ "linkType": "Space",
78
+ "id": "hw5pse7y1ojx"
79
+ }
80
+ },
81
+ "id": "Member-1",
82
+ "type": "Entry",
83
+ "createdAt": "2019-09-16T19:49:57.879Z",
84
+ "updatedAt": "2019-09-16T19:49:57.879Z",
85
+ "revision": 1,
86
+ "contentType": {
87
+ "sys": {
88
+ "type": "Link",
89
+ "linkType": "ContentType",
90
+ "id": "person"
91
+ }
92
+ }
93
+ },
94
+ "fields": {
95
+ "firstName": {
96
+ "en-US": "Ezekiel "
97
+ },
98
+ "lastName": {
99
+ "en-US": "Elliot"
100
+ },
101
+ "position": {
102
+ "en-US": "Running Back"
103
+ },
104
+ "profileImage": {
105
+ "en-US": {
106
+ "sys": {
107
+ "type": "Link",
108
+ "linkType": "Asset",
109
+ "id": "3pWma8spR62aegAWAWacyA"
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ JSON
116
+ }
117
+
118
+ let(:member2) {
119
+ JSON.parse(<<~JSON)
120
+ {
121
+ "sys": {
122
+ "space": {
123
+ "sys": {
124
+ "type": "Link",
125
+ "linkType": "Space",
126
+ "id": "hw5pse7y1ojx"
127
+ }
128
+ },
129
+ "id": "Member-2",
130
+ "type": "Entry",
131
+ "createdAt": "2019-09-16T19:49:57.879Z",
132
+ "updatedAt": "2019-09-16T19:49:57.879Z",
133
+ "revision": 1,
134
+ "contentType": {
135
+ "sys": {
136
+ "type": "Link",
137
+ "linkType": "ContentType",
138
+ "id": "person"
139
+ }
140
+ }
141
+ },
142
+ "fields": {
143
+ "firstName": {
144
+ "en-US": "Dak"
145
+ },
146
+ "lastName": {
147
+ "en-US": "Prescot"
148
+ },
149
+ "position": {
150
+ "en-US": "Quarterback"
151
+ },
152
+ "profileImage": null
153
+ }
154
+ }
155
+ JSON
156
+ }
157
+
158
+ let(:owner) {
159
+ JSON.parse(<<~JSON)
160
+ {
161
+ "sys": {
162
+ "space": {
163
+ "sys": {
164
+ "type": "Link",
165
+ "linkType": "Space",
166
+ "id": "hw5pse7y1ojx"
167
+ }
168
+ },
169
+ "id": "Owner-1",
170
+ "type": "Entry",
171
+ "createdAt": "2019-09-16T19:49:57.879Z",
172
+ "updatedAt": "2019-09-16T19:49:57.879Z",
173
+ "revision": 1,
174
+ "contentType": {
175
+ "sys": {
176
+ "type": "Link",
177
+ "linkType": "ContentType",
178
+ "id": "person"
179
+ }
180
+ }
181
+ },
182
+ "fields": {
183
+ "firstName": {
184
+ "en-US": "Jerry"
185
+ },
186
+ "lastName": {
187
+ "en-US": "Jones"
188
+ },
189
+ "position": {
190
+ "en-US": "owner"
191
+ }
192
+ }
193
+ }
194
+ JSON
195
+ }
196
+
197
+ describe '#find_by' do
198
+ context 'singular reference' do
199
+ before do
200
+ # add a dummy redirect that we ought to pass over
201
+ redirect2 = entry.deep_dup
202
+ redirect2['sys']['id'] = 'wrong_one'
203
+ redirect2['fields'].delete('page')
204
+ subject.set('wrong_one', redirect2)
205
+
206
+ [entry, page, asset].each { |d| subject.set(d.dig('sys', 'id'), d) }
207
+ end
208
+
209
+ it 'allows filtering by a reference field' do
210
+ # act
211
+ found = subject.find_by(
212
+ content_type: 'redirect',
213
+ filter: {
214
+ page: {
215
+ slug: { eq: 'some-page' }
216
+ }
217
+ }
218
+ )
219
+
220
+ # assert
221
+ expect(found).to_not be_nil
222
+ expect(found.dig('sys', 'id')).to eq('1qLdW7i7g4Ycq6i4Cckg44')
223
+ expect(found.dig('sys', 'contentType', 'sys', 'id')).to eq('redirect')
224
+ end
225
+
226
+ it 'allows filtering by reference id' do
227
+ # act
228
+ found = subject.find_by(
229
+ content_type: 'redirect',
230
+ filter: { 'page' => { id: '2zKTmej544IakmIqoEu0y8' } }
231
+ )
232
+
233
+ # assert
234
+ expect(found).to_not be_nil
235
+ expect(found.dig('sys', 'id')).to eq('1qLdW7i7g4Ycq6i4Cckg44')
236
+ end
237
+
238
+ it 'handles explicitly specified sys attr' do
239
+ # act
240
+ found = subject.find_by(
241
+ content_type: 'redirect',
242
+ filter: {
243
+ page: {
244
+ 'sys.contentType.sys.id' => 'page'
245
+ }
246
+ }
247
+ )
248
+
249
+ # assert
250
+ expect(found).to_not be_nil
251
+ expect(found.dig('sys', 'id')).to eq('1qLdW7i7g4Ycq6i4Cckg44')
252
+ end
253
+ end
254
+
255
+ context 'array reference' do
256
+ before do
257
+ # add a second team
258
+ team2 = team.deep_dup
259
+ team2['sys']['id'] = 'wrong_one'
260
+ team2['fields'].delete('members')
261
+ subject.set('wrong_one', team2)
262
+
263
+ [team, member1, member2, owner].each { |d| subject.set(d.dig('sys', 'id'), d) }
264
+ end
265
+
266
+ it 'filters by array reference field' do
267
+ # act
268
+ found = subject.find_by(
269
+ content_type: 'team',
270
+ filter: {
271
+ members: {
272
+ firstName: { eq: 'Dak' }
273
+ }
274
+ }
275
+ )
276
+
277
+ # assert
278
+ expect(found).to_not be_nil
279
+ expect(found.dig('sys', 'id')).to eq('Team-1234')
280
+ end
281
+
282
+ # The PostgresStore uses a 'links' column to do a join expression,
283
+ # this checks errors related to that case.
284
+ it 'does not include a link in another field' do
285
+ # act
286
+ found = subject.find_by(
287
+ content_type: 'team',
288
+ filter: {
289
+ members: {
290
+ lastName: { eq: 'Jones' }
291
+ }
292
+ }
293
+ )
294
+
295
+ expect(found).to be_nil
296
+
297
+ found2 = subject.find_by(
298
+ content_type: 'team',
299
+ filter: {
300
+ owner: {
301
+ lastName: { eq: 'Jones' }
302
+ }
303
+ }
304
+ )
305
+ expect(found2).to_not be_nil
306
+ end
307
+
308
+ it 'filters by array reference ID' do
309
+ # act
310
+ found = subject.find_by(
311
+ content_type: 'team',
312
+ filter: {
313
+ members: { id: 'Member-1' }
314
+ }
315
+ )
316
+
317
+ # assert
318
+ expect(found).to_not be_nil
319
+ expect(found.dig('sys', 'id')).to eq('Team-1234')
320
+ end
321
+
322
+ it 'handles explicitly specified sys attr' do
323
+ # act
324
+ found = subject.find_by(
325
+ content_type: 'team',
326
+ filter: {
327
+ members: {
328
+ 'sys.contentType.sys.id' => 'person'
329
+ }
330
+ }
331
+ )
332
+
333
+ # assert
334
+ expect(found).to_not be_nil
335
+ expect(found.dig('sys', 'id')).to eq('Team-1234')
336
+ end
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ # rubocop:enable Style/BlockDelimiters