wcc-contentful 1.3.0 → 1.4.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +105 -14
- data/lib/wcc/contentful/configuration.rb +12 -5
- data/lib/wcc/contentful/downloads_schema.rb +14 -2
- data/lib/wcc/contentful/entry_locale_transformer.rb +107 -0
- data/lib/wcc/contentful/exceptions.rb +5 -0
- data/lib/wcc/contentful/link_visitor.rb +12 -1
- data/lib/wcc/contentful/middleware/store/caching_middleware.rb +25 -8
- data/lib/wcc/contentful/middleware/store/locale_middleware.rb +30 -0
- data/lib/wcc/contentful/middleware/store.rb +20 -16
- data/lib/wcc/contentful/model_api.rb +2 -2
- data/lib/wcc/contentful/model_builder.rb +9 -2
- data/lib/wcc/contentful/model_methods.rb +4 -6
- data/lib/wcc/contentful/simple_client/cdn.rb +5 -2
- data/lib/wcc/contentful/simple_client/management.rb +16 -0
- data/lib/wcc/contentful/simple_client.rb +1 -0
- data/lib/wcc/contentful/store/base.rb +6 -1
- data/lib/wcc/contentful/store/cdn_adapter.rb +13 -4
- data/lib/wcc/contentful/store/factory.rb +8 -1
- data/lib/wcc/contentful/store/memory_store.rb +27 -8
- data/lib/wcc/contentful/store/postgres_store.rb +4 -3
- data/lib/wcc/contentful/store/query/condition.rb +89 -0
- data/lib/wcc/contentful/store/query.rb +9 -35
- data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +84 -12
- data/lib/wcc/contentful/store/rspec_examples/locale_queries.rb +220 -0
- data/lib/wcc/contentful/store/rspec_examples/operators/eq.rb +1 -1
- data/lib/wcc/contentful/store/rspec_examples.rb +13 -1
- data/lib/wcc/contentful/sync_engine.rb +1 -1
- data/lib/wcc/contentful/test/double.rb +1 -1
- data/lib/wcc/contentful/test/factory.rb +2 -4
- data/lib/wcc/contentful/version.rb +1 -1
- data/lib/wcc/contentful.rb +17 -6
- metadata +112 -62
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_examples 'supports locales in queries' do |feature_set|
|
4
|
+
describe 'supports query options: { locale: ... }' do
|
5
|
+
before { skip('querying alternate locales not supported') } if feature_set == false
|
6
|
+
|
7
|
+
generator = proc { "test#{rand(1..10_000)}" }
|
8
|
+
|
9
|
+
let(:desired_value) {
|
10
|
+
generator.call
|
11
|
+
}
|
12
|
+
|
13
|
+
let(:data) {
|
14
|
+
1.upto(3).map do |i|
|
15
|
+
{
|
16
|
+
'sys' => { 'id' => "k#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
17
|
+
'fields' => {
|
18
|
+
'slug' => {
|
19
|
+
'en-US' => generator.call,
|
20
|
+
'es-ES' => generator.call
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
let(:desired) {
|
28
|
+
{
|
29
|
+
'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
30
|
+
'fields' => {
|
31
|
+
'slug' => {
|
32
|
+
'en-US' => generator.call,
|
33
|
+
'es-ES' => desired_value
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
context 'when localized value exists' do
|
40
|
+
it 'find_by can apply filter object' do
|
41
|
+
[*data, desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
42
|
+
|
43
|
+
# act
|
44
|
+
found = subject.find_by(content_type: 'test1',
|
45
|
+
filter: { 'slug' => desired_value },
|
46
|
+
options: { locale: 'es-ES' })
|
47
|
+
|
48
|
+
# assert
|
49
|
+
expect(found).to_not be_nil
|
50
|
+
expect(found).to eq(desired)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'find_by can find value in array' do
|
54
|
+
data =
|
55
|
+
1.upto(3).map do |i|
|
56
|
+
{
|
57
|
+
'sys' => {
|
58
|
+
'id' => "k#{i}",
|
59
|
+
'contentType' => { 'sys' => { 'id' => 'test1' } }
|
60
|
+
},
|
61
|
+
'fields' => {
|
62
|
+
'slug' => {
|
63
|
+
'en-US' => [generator.call, generator.call],
|
64
|
+
'es-ES' => [generator.call, generator.call]
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
desired_value = generator.call
|
71
|
+
desired = {
|
72
|
+
'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
73
|
+
'fields' => {
|
74
|
+
'slug' => {
|
75
|
+
'en-US' => [generator.call, generator.call],
|
76
|
+
'es-ES' => [generator.call, desired_value]
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
data << desired
|
82
|
+
data.shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
83
|
+
|
84
|
+
# act
|
85
|
+
found = subject.find_by(content_type: 'test1',
|
86
|
+
filter: { 'slug' => { eq: desired_value } },
|
87
|
+
options: { locale: 'es-ES' })
|
88
|
+
|
89
|
+
# assert
|
90
|
+
expect(found).to_not be_nil
|
91
|
+
expect(found).to eq(desired)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'find_all can apply operator' do
|
95
|
+
desired =
|
96
|
+
4.upto(5).map do |i|
|
97
|
+
{
|
98
|
+
'sys' => { 'id' => "d#{i}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
99
|
+
'fields' => {
|
100
|
+
'slug' => {
|
101
|
+
'en-US' => generator.call,
|
102
|
+
'es-ES' => desired_value
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
[*data, *desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
109
|
+
|
110
|
+
# act
|
111
|
+
found = subject.find_all(content_type: 'test1', options: { locale: 'es-ES' })
|
112
|
+
.eq('slug', desired_value)
|
113
|
+
|
114
|
+
# assert
|
115
|
+
expect(found.count).to eq(2)
|
116
|
+
sorted = found.to_a.sort_by { |item| item.dig('sys', 'id') }
|
117
|
+
expect(sorted).to eq(desired)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'using fallback locales' do
|
122
|
+
before { pending('querying alternate locales not yet implemented') } if feature_set&.to_s == 'pending'
|
123
|
+
|
124
|
+
before do
|
125
|
+
allow(configuration).to receive(:locale_fallbacks)
|
126
|
+
.and_return({
|
127
|
+
'es-MX' => 'es-ES',
|
128
|
+
'es-ES' => 'en-US'
|
129
|
+
})
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'find_by can apply filter object' do
|
133
|
+
[*data, desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
134
|
+
|
135
|
+
# act
|
136
|
+
found = subject.find_by(content_type: 'test1',
|
137
|
+
filter: { 'slug' => desired_value },
|
138
|
+
options: { locale: 'es-MX' })
|
139
|
+
|
140
|
+
# assert
|
141
|
+
expect(found).to_not be_nil
|
142
|
+
expect(found).to eq(desired)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'find_by can find value in array' do
|
146
|
+
data =
|
147
|
+
1.upto(3).map do |i|
|
148
|
+
{
|
149
|
+
'sys' => {
|
150
|
+
'id' => "k#{i}",
|
151
|
+
'contentType' => { 'sys' => { 'id' => 'test1' } }
|
152
|
+
},
|
153
|
+
'fields' => {
|
154
|
+
'slug' => {
|
155
|
+
'en-US' => [generator.call, generator.call],
|
156
|
+
'es-ES' => [generator.call, generator.call]
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
desired_value = generator.call
|
163
|
+
desired = {
|
164
|
+
'sys' => { 'id' => "k#{rand}", 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
165
|
+
'fields' => {
|
166
|
+
'slug' => {
|
167
|
+
'en-US' => [generator.call, generator.call],
|
168
|
+
'es-ES' => [generator.call, desired_value]
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
data << desired
|
174
|
+
data.shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
175
|
+
|
176
|
+
# act
|
177
|
+
found = subject.find_by(content_type: 'test1',
|
178
|
+
filter: { 'slug' => { eq: desired_value } },
|
179
|
+
options: { locale: 'es-MX' })
|
180
|
+
|
181
|
+
# assert
|
182
|
+
expect(found).to_not be_nil
|
183
|
+
expect(found).to eq(desired)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'find_all can apply operator' do
|
187
|
+
desired = [
|
188
|
+
{
|
189
|
+
'sys' => { 'id' => 'd1', 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
190
|
+
'fields' => {
|
191
|
+
'slug' => {
|
192
|
+
'en-US' => generator.call,
|
193
|
+
'es-ES' => desired_value
|
194
|
+
}
|
195
|
+
}
|
196
|
+
},
|
197
|
+
{
|
198
|
+
'sys' => { 'id' => 'd2', 'contentType' => { 'sys' => { 'id' => 'test1' } } },
|
199
|
+
'fields' => {
|
200
|
+
'slug' => {
|
201
|
+
'en-US' => desired_value
|
202
|
+
}
|
203
|
+
}
|
204
|
+
}
|
205
|
+
]
|
206
|
+
|
207
|
+
[*data, *desired].shuffle.each { |d| subject.set(d.dig('sys', 'id'), d) }
|
208
|
+
|
209
|
+
# act
|
210
|
+
found = subject.find_all(content_type: 'test1', options: { locale: 'es-MX' })
|
211
|
+
.eq('slug', desired_value)
|
212
|
+
|
213
|
+
# assert
|
214
|
+
expect(found.count).to eq(2)
|
215
|
+
sorted = found.to_a.sort_by { |item| item.dig('sys', 'id') }
|
216
|
+
expect(sorted).to eq(desired)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -46,7 +46,7 @@ RSpec.shared_examples 'supports :eq operator' do
|
|
46
46
|
'id' => "k#{i}",
|
47
47
|
'contentType' => { 'sys' => { 'id' => 'test1' } }
|
48
48
|
},
|
49
|
-
'fields' => {
|
49
|
+
'fields' => { type.to_s => { 'en-US' => [generator.call, generator.call] } }
|
50
50
|
}
|
51
51
|
end
|
52
52
|
|
@@ -4,6 +4,7 @@ require_relative './rspec_examples/basic_store'
|
|
4
4
|
require_relative './rspec_examples/operators'
|
5
5
|
require_relative './rspec_examples/nested_queries'
|
6
6
|
require_relative './rspec_examples/include_param'
|
7
|
+
require_relative './rspec_examples/locale_queries'
|
7
8
|
|
8
9
|
# rubocop:disable Style/BlockDelimiters
|
9
10
|
|
@@ -24,6 +25,11 @@ require_relative './rspec_examples/include_param'
|
|
24
25
|
# all linked entries of an object in a single query.
|
25
26
|
# If your store does not respect the include parameter, then the Model layer
|
26
27
|
# will be calling #find a lot in order to resolve linked entries.
|
28
|
+
# [:locale_queries] - This feature defines how the store respects the `locale: x`
|
29
|
+
# key in the Options hash. If this option is set, then the store needs to
|
30
|
+
# compare the query to the appropriate localized value.
|
31
|
+
# If the store does not support this, then either the application should not
|
32
|
+
# use multiple locales OR should always query using the default locale.
|
27
33
|
#
|
28
34
|
# @example
|
29
35
|
# require 'wcc/contentful/store/rspec_examples'
|
@@ -38,13 +44,19 @@ require_relative './rspec_examples/include_param'
|
|
38
44
|
RSpec.shared_examples 'contentful store' do |feature_set|
|
39
45
|
feature_set = {
|
40
46
|
nested_queries: 'pending',
|
41
|
-
include_param: 'pending'
|
47
|
+
include_param: 'pending',
|
48
|
+
locale_queries: 'pending'
|
42
49
|
}.merge(feature_set&.symbolize_keys || {})
|
43
50
|
|
51
|
+
let(:configuration) {
|
52
|
+
WCC::Contentful::Configuration.new
|
53
|
+
}
|
54
|
+
|
44
55
|
include_examples 'basic store'
|
45
56
|
include_examples 'operators', feature_set[:operators]
|
46
57
|
include_examples 'supports nested queries', feature_set[:nested_queries]
|
47
58
|
include_examples 'supports include param', feature_set[:include_param]
|
59
|
+
include_examples 'supports locales in queries', feature_set[:locale_queries]
|
48
60
|
end
|
49
61
|
|
50
62
|
# rubocop:enable Style/BlockDelimiters
|
@@ -138,7 +138,7 @@ module WCC::Contentful
|
|
138
138
|
# This job uses the Contentful Sync API to update the configured store with
|
139
139
|
# the latest data from Contentful.
|
140
140
|
class Job < ActiveJob::Base
|
141
|
-
self.queue_adapter
|
141
|
+
self.queue_adapter ||= :async
|
142
142
|
queue_as :default
|
143
143
|
|
144
144
|
def configuration
|
@@ -24,7 +24,7 @@ module WCC::Contentful::Test::Factory
|
|
24
24
|
|
25
25
|
raw_value = v
|
26
26
|
raw_value = to_raw(v, field.type) if %i[Asset Link].include?(field.type)
|
27
|
-
raw['fields'][field.name]
|
27
|
+
raw['fields'][field.name] = raw_value
|
28
28
|
end
|
29
29
|
|
30
30
|
instance = const.new(raw, context)
|
@@ -84,9 +84,7 @@ module WCC::Contentful::Test::Factory
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def contentful_fields(model)
|
87
|
-
WCC::Contentful::Test::Attributes.defaults(model)
|
88
|
-
{ 'en-US' => v }
|
89
|
-
end
|
87
|
+
WCC::Contentful::Test::Attributes.defaults(model)
|
90
88
|
end
|
91
89
|
|
92
90
|
def to_raw(val, field_type)
|
data/lib/wcc/contentful.rb
CHANGED
@@ -10,6 +10,7 @@ require 'wcc/contentful/configuration'
|
|
10
10
|
require 'wcc/contentful/downloads_schema'
|
11
11
|
require 'wcc/contentful/exceptions'
|
12
12
|
require 'wcc/contentful/helpers'
|
13
|
+
require 'wcc/contentful/entry_locale_transformer'
|
13
14
|
require 'wcc/contentful/link_visitor'
|
14
15
|
require 'wcc/contentful/services'
|
15
16
|
require 'wcc/contentful/simple_client'
|
@@ -40,9 +41,8 @@ module WCC::Contentful
|
|
40
41
|
end
|
41
42
|
|
42
43
|
# Gets all queryable locales.
|
43
|
-
# Reserved for future use.
|
44
44
|
def locales
|
45
|
-
|
45
|
+
configuration&.locale_fallbacks
|
46
46
|
end
|
47
47
|
|
48
48
|
def logger
|
@@ -93,15 +93,18 @@ module WCC::Contentful
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
|
96
|
+
schema =
|
97
97
|
begin
|
98
|
-
JSON.parse(File.read(configuration.schema_file))
|
98
|
+
JSON.parse(File.read(configuration.schema_file)) if File.exist?(configuration.schema_file)
|
99
99
|
rescue JSON::ParserError
|
100
100
|
Services.instance.warn("Schema file invalid, ignoring it: #{configuration.schema_file}")
|
101
101
|
nil
|
102
102
|
end
|
103
103
|
|
104
|
-
|
104
|
+
content_types = schema['contentTypes'] if schema
|
105
|
+
locales = schema['locales'] if schema
|
106
|
+
|
107
|
+
if !schema && %i[if_possible never].include?(configuration.update_schema_file)
|
105
108
|
# Final fallback - try to grab content types from CDN. We can't update the file
|
106
109
|
# because the CDN doesn't have all the field validation info, but we can at least
|
107
110
|
# build the WCC::Contentful::Model instances.
|
@@ -127,7 +130,15 @@ module WCC::Contentful
|
|
127
130
|
services: WCC::Contentful::Services.instance
|
128
131
|
)
|
129
132
|
|
130
|
-
#
|
133
|
+
# Update the locale fallbacks from the schema file, unless they have already
|
134
|
+
# been configured.
|
135
|
+
locales&.each do |locale_hash|
|
136
|
+
next if @configuration.locale_fallbacks[locale_hash['code']]
|
137
|
+
|
138
|
+
@configuration.locale_fallbacks[locale_hash['code']] = locale_hash['fallbackCode']
|
139
|
+
end
|
140
|
+
|
141
|
+
# Enqueue an initial sync
|
131
142
|
WCC::Contentful::SyncEngine::Job.perform_later if defined?(WCC::Contentful::SyncEngine::Job)
|
132
143
|
|
133
144
|
@configuration = @configuration.freeze
|