wcc-contentful 1.3.1 → 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 +80 -13
- 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_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 +78 -46
@@ -34,6 +34,22 @@ class WCC::Contentful::SimpleClient::Management < WCC::Contentful::SimpleClient
|
|
34
34
|
resp.assert_ok!
|
35
35
|
end
|
36
36
|
|
37
|
+
def locales(**query)
|
38
|
+
resp =
|
39
|
+
_instrument 'locales', query: query do
|
40
|
+
get('locales', query)
|
41
|
+
end
|
42
|
+
resp.assert_ok!
|
43
|
+
end
|
44
|
+
|
45
|
+
def locale(key, query = {})
|
46
|
+
resp =
|
47
|
+
_instrument 'locales', content_type: key, query: query do
|
48
|
+
get("locales/#{key}", query)
|
49
|
+
end
|
50
|
+
resp.assert_ok!
|
51
|
+
end
|
52
|
+
|
37
53
|
def editor_interface(content_type_id, query = {})
|
38
54
|
resp =
|
39
55
|
_instrument 'editor_interfaces', content_type: content_type_id, query: query do
|
@@ -17,6 +17,10 @@ module WCC::Contentful::Store
|
|
17
17
|
class Base
|
18
18
|
include WCC::Contentful::Store::Interface
|
19
19
|
|
20
|
+
def initialize(configuration = nil)
|
21
|
+
@configuration = configuration || WCC::Contentful.configuration
|
22
|
+
end
|
23
|
+
|
20
24
|
# Sets the value of the entry with the given ID in the store.
|
21
25
|
# @abstract
|
22
26
|
def set(_id, _value)
|
@@ -103,7 +107,8 @@ module WCC::Contentful::Store
|
|
103
107
|
Query.new(
|
104
108
|
self,
|
105
109
|
content_type: content_type,
|
106
|
-
options: options
|
110
|
+
options: options,
|
111
|
+
configuration: @configuration
|
107
112
|
)
|
108
113
|
end
|
109
114
|
|
@@ -29,7 +29,7 @@ module WCC::Contentful::Store
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def find(key, hint: nil, **options)
|
32
|
-
options =
|
32
|
+
options = options&.dup || {}
|
33
33
|
entry =
|
34
34
|
if hint
|
35
35
|
client.public_send(hint.underscore, key, options)
|
@@ -114,7 +114,7 @@ module WCC::Contentful::Store
|
|
114
114
|
op = :in if op.nil?
|
115
115
|
end
|
116
116
|
|
117
|
-
param = parameter(field, operator: op, context: context, locale:
|
117
|
+
param = parameter(field, operator: op, context: context, locale: false)
|
118
118
|
|
119
119
|
self.class.new(
|
120
120
|
@store,
|
@@ -157,10 +157,10 @@ module WCC::Contentful::Store
|
|
157
157
|
@response ||=
|
158
158
|
if @relation[:content_type] == 'Asset'
|
159
159
|
@client.assets(
|
160
|
-
|
160
|
+
@relation.reject { |k| k == :content_type }.merge(@options)
|
161
161
|
)
|
162
162
|
else
|
163
|
-
@client.entries(
|
163
|
+
@client.entries(@relation.merge(@options))
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
@@ -180,6 +180,15 @@ module WCC::Contentful::Store
|
|
180
180
|
included
|
181
181
|
end
|
182
182
|
|
183
|
+
# Constructs the CDN query parameter from a structured field definition and
|
184
|
+
# operator.
|
185
|
+
# Notes:
|
186
|
+
# * "eq" can be omitted, e.g. 'fields.slug=/' is equivalent to 'fields.slug[eq]=/'
|
187
|
+
# * If "locale" is specified in the query, matching is done against that locale,
|
188
|
+
# unless the query explicitly specifies the locale. Examples:
|
189
|
+
# 'locale=es-US&fields.title=página principal' matches on the es locale
|
190
|
+
# 'locale=en-US&fields.title=página principal' returns nothing
|
191
|
+
# 'locale=en-US&fields.title.es-US=página principal' returns the page, but in the english locale.
|
183
192
|
def parameter(field, operator: nil, context: nil, locale: false)
|
184
193
|
if sys?(field)
|
185
194
|
"#{field}#{op_param(operator)}"
|
@@ -5,6 +5,7 @@ require_relative 'memory_store'
|
|
5
5
|
require_relative 'cdn_adapter'
|
6
6
|
require_relative '../middleware/store'
|
7
7
|
require_relative '../middleware/store/caching_middleware'
|
8
|
+
require_relative '../middleware/store/locale_middleware'
|
8
9
|
|
9
10
|
module WCC::Contentful::Store
|
10
11
|
# This factory presents a DSL for configuring the store stack. The store stack
|
@@ -46,6 +47,8 @@ module WCC::Contentful::Store
|
|
46
47
|
self.middleware << [middleware, middleware_params, configure_proc]
|
47
48
|
end
|
48
49
|
|
50
|
+
# Replaces a middleware in the chain. The middleware to replace is selected
|
51
|
+
# by matching the class.
|
49
52
|
def replace(middleware, *middleware_params, &block)
|
50
53
|
idx = self.middleware.find_index { |m| m[0] == middleware }
|
51
54
|
raise ArgumentError, "Middleware #{middleware} not present" if idx.nil?
|
@@ -54,6 +57,8 @@ module WCC::Contentful::Store
|
|
54
57
|
self.middleware[idx] = [middleware, middleware_params, configure_proc]
|
55
58
|
end
|
56
59
|
|
60
|
+
# Removes a middleware from the chain, finding it by matching the class
|
61
|
+
# constant.
|
57
62
|
def unuse(middleware)
|
58
63
|
idx = self.middleware.find_index { |m| m[0] == middleware }
|
59
64
|
return if idx.nil?
|
@@ -174,7 +179,9 @@ module WCC::Contentful::Store
|
|
174
179
|
# The middleware that by default lives at the top of the middleware stack.
|
175
180
|
def default_middleware
|
176
181
|
[
|
177
|
-
[WCC::Contentful::Store::InstrumentationMiddleware]
|
182
|
+
[WCC::Contentful::Store::InstrumentationMiddleware],
|
183
|
+
# Stores do not guarantee that the entry is resolved to the locale
|
184
|
+
[WCC::Contentful::Middleware::Store::LocaleMiddleware]
|
178
185
|
].freeze
|
179
186
|
end
|
180
187
|
end
|
@@ -7,9 +7,12 @@ module WCC::Contentful::Store
|
|
7
7
|
# point for more useful implementations. It only implements equality queries
|
8
8
|
# and does not support querying through an association.
|
9
9
|
class MemoryStore < Base
|
10
|
-
|
10
|
+
delegate :locale_fallbacks, to: :@configuration
|
11
|
+
|
12
|
+
def initialize(configuration = nil)
|
11
13
|
super
|
12
14
|
|
15
|
+
@configuration = configuration
|
13
16
|
@mutex = Concurrent::ReentrantReadWriteLock.new
|
14
17
|
@hash = {}
|
15
18
|
end
|
@@ -36,7 +39,7 @@ module WCC::Contentful::Store
|
|
36
39
|
|
37
40
|
def find(key, **_options)
|
38
41
|
@mutex.with_read_lock do
|
39
|
-
@hash[key]
|
42
|
+
@hash[key].deep_dup
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -64,9 +67,12 @@ module WCC::Contentful::Store
|
|
64
67
|
|
65
68
|
# For each condition, we apply a new Enumerable#select with a block that
|
66
69
|
# enforces the condition.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
+
relation =
|
71
|
+
query.conditions.reduce(relation) do |memo, condition|
|
72
|
+
__send__("apply_#{condition.op}", memo, condition)
|
73
|
+
end
|
74
|
+
|
75
|
+
relation.map(&:deep_dup)
|
70
76
|
end
|
71
77
|
|
72
78
|
private
|
@@ -80,8 +86,7 @@ module WCC::Contentful::Store
|
|
80
86
|
end
|
81
87
|
|
82
88
|
def eq?(entry, condition)
|
83
|
-
|
84
|
-
val = entry.dig(*condition.path)
|
89
|
+
val = select_value_for_compare(entry, condition)
|
85
90
|
|
86
91
|
# For arrays, equality is defined as does the array include the expected value.
|
87
92
|
# See https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/array-equality-inequality
|
@@ -101,7 +106,7 @@ module WCC::Contentful::Store
|
|
101
106
|
end
|
102
107
|
|
103
108
|
def in?(entry, condition)
|
104
|
-
val = entry
|
109
|
+
val = select_value_for_compare(entry, condition)
|
105
110
|
|
106
111
|
if val.is_a? Array
|
107
112
|
# TODO: detect if in ruby 3.1 and use val.intersect?(condition.expected)
|
@@ -110,5 +115,19 @@ module WCC::Contentful::Store
|
|
110
115
|
condition.expected.include?(val)
|
111
116
|
end
|
112
117
|
end
|
118
|
+
|
119
|
+
# Selects the value for the condition from the entry, taking into account locale fallbacks
|
120
|
+
def select_value_for_compare(entry, condition)
|
121
|
+
condition.each_locale_fallback do |cond|
|
122
|
+
# The condition's path tells us where to find the value in the JSON object
|
123
|
+
val = entry.dig(*cond.path)
|
124
|
+
|
125
|
+
# If the object has no value for this locale, try the fallbacks
|
126
|
+
next if val.nil?
|
127
|
+
|
128
|
+
# The object has a value for this locale, so we must compare against it and not the fallbacks
|
129
|
+
return val
|
130
|
+
end
|
131
|
+
end
|
113
132
|
end
|
114
133
|
end
|
@@ -17,8 +17,8 @@ module WCC::Contentful::Store
|
|
17
17
|
attr_reader :connection_pool
|
18
18
|
attr_accessor :logger
|
19
19
|
|
20
|
-
def initialize(
|
21
|
-
super()
|
20
|
+
def initialize(configuration = nil, connection_options = nil, pool_options = nil)
|
21
|
+
super(configuration)
|
22
22
|
@schema_ensured = false
|
23
23
|
connection_options ||= { dbname: 'postgres' }
|
24
24
|
pool_options ||= {}
|
@@ -100,7 +100,8 @@ module WCC::Contentful::Store
|
|
100
100
|
Query.new(
|
101
101
|
self,
|
102
102
|
content_type: content_type,
|
103
|
-
options: options
|
103
|
+
options: options,
|
104
|
+
configuration: @configuration
|
104
105
|
)
|
105
106
|
end
|
106
107
|
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class WCC::Contentful::Store::Query
|
4
|
+
Condition =
|
5
|
+
Struct.new(:path, :op, :expected, :locale_fallbacks) do
|
6
|
+
LINK_KEYS = %w[id type linkType].freeze # rubocop:disable Lint/ConstantDefinitionInBlock
|
7
|
+
|
8
|
+
##
|
9
|
+
# Breaks the path into an array of tuples, where each tuple represents an
|
10
|
+
# entry subquery.
|
11
|
+
# If the query is a simple query on a field in an entry, there will be one
|
12
|
+
# tuple in the array:
|
13
|
+
# { 'title' => 'foo' } becomes
|
14
|
+
# [['fields', 'title', 'en-US']]
|
15
|
+
#
|
16
|
+
# If the query is a query through a link, there will be multiple tuples:
|
17
|
+
# { 'page' => { 'title' => 'foo' } } becomes
|
18
|
+
# [['fields', 'page', 'en-US'], ['fields', 'title', 'en-US']]
|
19
|
+
def path_tuples
|
20
|
+
return @path_tuples if @path_tuples
|
21
|
+
|
22
|
+
arr = []
|
23
|
+
remaining = path.dup
|
24
|
+
until remaining.empty?
|
25
|
+
locale = nil
|
26
|
+
link_sys = nil
|
27
|
+
link_field = nil
|
28
|
+
|
29
|
+
sys_or_fields = remaining.shift
|
30
|
+
field = remaining.shift
|
31
|
+
locale = remaining.shift if sys_or_fields == 'fields'
|
32
|
+
|
33
|
+
if remaining[0] == 'sys' && LINK_KEYS.include?(remaining[1])
|
34
|
+
link_sys = remaining.shift
|
35
|
+
link_field = remaining.shift
|
36
|
+
end
|
37
|
+
|
38
|
+
arr << [sys_or_fields, field, locale, link_sys, link_field].compact
|
39
|
+
end
|
40
|
+
@path_tuples = arr.freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Starting with the last part of the path that is a locale, iterates all the
|
45
|
+
# combinations of potential locale fallbacks.
|
46
|
+
# e.g. if the path is ['fields', 'page', 'es-MX', 'fields', 'title', 'es-MX']
|
47
|
+
# then we get:
|
48
|
+
# ['fields', 'page', 'es-MX', 'fields', 'title', 'es-MX'] (self)
|
49
|
+
# ['fields', 'page', 'es-MX', 'fields', 'title', 'es-US']
|
50
|
+
# ['fields', 'page', 'es-MX', 'fields', 'title', 'en-US']
|
51
|
+
# ['fields', 'page', 'es-US', 'fields', 'title', 'es-MX']
|
52
|
+
# ['fields', 'page', 'es-US', 'fields', 'title', 'es-US']
|
53
|
+
# ['fields', 'page', 'es-US', 'fields', 'title', 'en-US']
|
54
|
+
# ['fields', 'page', 'en-US', 'fields', 'title', 'es-MX']
|
55
|
+
# ['fields', 'page', 'en-US', 'fields', 'title', 'es-US']
|
56
|
+
# ['fields', 'page', 'en-US', 'fields', 'title', 'en-US']
|
57
|
+
def each_locale_fallback(&block)
|
58
|
+
return to_enum(:each_locale_fallback) unless block_given?
|
59
|
+
|
60
|
+
_each_locale_fallback(path_tuples, 0, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Find the next fallback tuples from this set of tuples
|
66
|
+
def _each_locale_fallback(original_tuples, start_at, &block)
|
67
|
+
tuples = original_tuples.deep_dup
|
68
|
+
varying = tuples[start_at]
|
69
|
+
|
70
|
+
if varying[2].nil?
|
71
|
+
# This is a non-localizable query, so just yield it
|
72
|
+
yield Condition.new(tuples.flatten, op, expected, locale_fallbacks)
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
while varying[2]
|
77
|
+
if tuples.length > start_at + 1
|
78
|
+
# There's more locales that we need to vary to the right, so recurse into those
|
79
|
+
_each_locale_fallback(tuples, start_at + 1, &block)
|
80
|
+
else
|
81
|
+
# We're the tail of the condition, so yield it.
|
82
|
+
yield Condition.new(tuples.flatten, op, expected, locale_fallbacks)
|
83
|
+
end
|
84
|
+
|
85
|
+
varying[2] = locale_fallbacks[varying[2]]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative '../../contentful'
|
4
4
|
require_relative './query/interface'
|
5
|
+
require_relative './query/condition'
|
5
6
|
|
6
7
|
module WCC::Contentful::Store
|
7
8
|
# The default query object returned by Stores that extend WCC::Contentful::Store::Base.
|
@@ -26,11 +27,12 @@ module WCC::Contentful::Store
|
|
26
27
|
|
27
28
|
attr_reader :store, :content_type, :conditions
|
28
29
|
|
29
|
-
def initialize(store, content_type:, conditions: nil, options: nil, **extra)
|
30
|
+
def initialize(store, content_type:, conditions: nil, options: nil, configuration: nil, **extra) # rubocop:disable Metrics/ParameterLists
|
30
31
|
@store = store
|
31
32
|
@content_type = content_type
|
32
33
|
@conditions = conditions || []
|
33
34
|
@options = options || {}
|
35
|
+
@configuration = configuration || WCC::Contentful.configuration
|
34
36
|
@extra = extra
|
35
37
|
end
|
36
38
|
|
@@ -60,7 +62,7 @@ module WCC::Contentful::Store
|
|
60
62
|
# Can be an array, symbol, or dotted-notation path specification.
|
61
63
|
# @expected The expected value to compare the field's value against.
|
62
64
|
# @context A context object optionally containing `context[:locale]`
|
63
|
-
def apply_operator(operator, field, expected,
|
65
|
+
def apply_operator(operator, field, expected, _context = nil)
|
64
66
|
operator ||= expected.is_a?(Array) ? :in : :eq
|
65
67
|
raise ArgumentError, "Operator #{operator} not supported" unless respond_to?(operator)
|
66
68
|
raise ArgumentError, 'value cannot be nil (try using exists: false)' if expected.nil?
|
@@ -75,10 +77,10 @@ module WCC::Contentful::Store
|
|
75
77
|
field = field.to_s if field.is_a? Symbol
|
76
78
|
path = field.is_a?(Array) ? field : field.split('.')
|
77
79
|
|
78
|
-
path = self.class.normalize_condition_path(path,
|
80
|
+
path = self.class.normalize_condition_path(path, @options)
|
79
81
|
|
80
82
|
_append_condition(
|
81
|
-
Condition.new(path, operator, expected)
|
83
|
+
Condition.new(path, operator, expected, @configuration&.locale_fallbacks || {})
|
82
84
|
)
|
83
85
|
end
|
84
86
|
|
@@ -177,15 +179,15 @@ module WCC::Contentful::Store
|
|
177
179
|
end
|
178
180
|
|
179
181
|
def known_locales
|
180
|
-
@known_locales
|
182
|
+
@known_locales ||= WCC::Contentful.locales&.keys || ['en-US']
|
181
183
|
end
|
182
184
|
RESERVED_NAMES = %w[fields sys].freeze
|
183
185
|
|
184
186
|
# Takes a path array in non-normal form and inserts 'sys', 'fields',
|
185
187
|
# and the current locale as appropriate to normalize it.
|
186
188
|
# rubocop:disable Metrics/BlockNesting
|
187
|
-
def normalize_condition_path(path,
|
188
|
-
context_locale =
|
189
|
+
def normalize_condition_path(path, options = nil)
|
190
|
+
context_locale = options[:locale]&.to_s if options.present?
|
189
191
|
context_locale ||= 'en-US'
|
190
192
|
|
191
193
|
rev_path = path.reverse
|
@@ -234,33 +236,5 @@ module WCC::Contentful::Store
|
|
234
236
|
end
|
235
237
|
# rubocop:enable Metrics/BlockNesting
|
236
238
|
end
|
237
|
-
|
238
|
-
Condition =
|
239
|
-
Struct.new(:path, :op, :expected) do
|
240
|
-
LINK_KEYS = %w[id type linkType].freeze # rubocop:disable Lint/ConstantDefinitionInBlock
|
241
|
-
|
242
|
-
def path_tuples
|
243
|
-
@path_tuples ||=
|
244
|
-
[].tap do |arr|
|
245
|
-
remaining = path.dup
|
246
|
-
until remaining.empty?
|
247
|
-
locale = nil
|
248
|
-
link_sys = nil
|
249
|
-
link_field = nil
|
250
|
-
|
251
|
-
sys_or_fields = remaining.shift
|
252
|
-
field = remaining.shift
|
253
|
-
locale = remaining.shift if sys_or_fields == 'fields'
|
254
|
-
|
255
|
-
if remaining[0] == 'sys' && LINK_KEYS.include?(remaining[1])
|
256
|
-
link_sys = remaining.shift
|
257
|
-
link_field = remaining.shift
|
258
|
-
end
|
259
|
-
|
260
|
-
arr << [sys_or_fields, field, locale, link_sys, link_field].compact
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|
265
239
|
end
|
266
240
|
end
|
@@ -105,7 +105,7 @@ RSpec.shared_examples 'basic store' do
|
|
105
105
|
"type": "Asset",
|
106
106
|
"createdAt": "2018-02-12T19:53:39.309Z",
|
107
107
|
"updatedAt": "2018-02-12T19:53:39.309Z",
|
108
|
-
"revision":
|
108
|
+
"revision": 2
|
109
109
|
},
|
110
110
|
"fields": {
|
111
111
|
"title": {
|
@@ -133,7 +133,10 @@ RSpec.shared_examples 'basic store' do
|
|
133
133
|
describe '#set/#find' do
|
134
134
|
describe 'ensures that the stored value is of type Hash' do
|
135
135
|
it 'should not raise an error if value is a Hash' do
|
136
|
-
data = {
|
136
|
+
data = {
|
137
|
+
'sys' => { 'id' => 'sync:token', 'type' => 'token' },
|
138
|
+
'token' => 'state'
|
139
|
+
}
|
137
140
|
|
138
141
|
# assert
|
139
142
|
expect { subject.set('sync:token', data) }.to_not raise_error
|
@@ -146,7 +149,11 @@ RSpec.shared_examples 'basic store' do
|
|
146
149
|
end
|
147
150
|
|
148
151
|
it 'stores and finds data by ID' do
|
149
|
-
data = {
|
152
|
+
data = {
|
153
|
+
'sys' => { 'id' => '1234' },
|
154
|
+
'key' => 'val',
|
155
|
+
'1' => { 'deep' => 9 }
|
156
|
+
}
|
150
157
|
|
151
158
|
# act
|
152
159
|
subject.set('1234', data)
|
@@ -157,7 +164,11 @@ RSpec.shared_examples 'basic store' do
|
|
157
164
|
end
|
158
165
|
|
159
166
|
it 'find returns nil if key doesnt exist' do
|
160
|
-
data = {
|
167
|
+
data = {
|
168
|
+
'sys' => { 'id' => '1234' },
|
169
|
+
'key' => 'val',
|
170
|
+
'1' => { 'deep' => 9 }
|
171
|
+
}
|
161
172
|
subject.set('1234', data)
|
162
173
|
|
163
174
|
# act
|
@@ -181,8 +192,16 @@ RSpec.shared_examples 'basic store' do
|
|
181
192
|
end
|
182
193
|
|
183
194
|
it 'set returns prior value if exists' do
|
184
|
-
data = {
|
185
|
-
|
195
|
+
data = {
|
196
|
+
'sys' => { 'id' => '1234', 'revision' => 1 },
|
197
|
+
'key' => 'val',
|
198
|
+
'1' => { 'deep' => 9 }
|
199
|
+
}
|
200
|
+
data2 = {
|
201
|
+
'sys' => { 'id' => '1234', 'revision' => 2 },
|
202
|
+
'key' => 'val',
|
203
|
+
'1' => { 'deep' => 11 }
|
204
|
+
}
|
186
205
|
|
187
206
|
# act
|
188
207
|
prior1 = subject.set('1234', data)
|
@@ -193,11 +212,27 @@ RSpec.shared_examples 'basic store' do
|
|
193
212
|
expect(prior2).to eq(data)
|
194
213
|
expect(subject.find('1234')).to eq(data2)
|
195
214
|
end
|
215
|
+
|
216
|
+
it 'modifying found entry does not modify underlying data' do
|
217
|
+
subject.index(entry)
|
218
|
+
|
219
|
+
# act
|
220
|
+
found = subject.find('1qLdW7i7g4Ycq6i4Cckg44')
|
221
|
+
found['fields']['slug']['en-US'] = 'new slug'
|
222
|
+
|
223
|
+
# assert
|
224
|
+
found2 = subject.find('1qLdW7i7g4Ycq6i4Cckg44')
|
225
|
+
expect(found2.dig('fields', 'slug', 'en-US')).to eq('redirect-with-slug-and-url')
|
226
|
+
end
|
196
227
|
end
|
197
228
|
|
198
229
|
describe '#delete' do
|
199
230
|
it 'deletes an item out of the store' do
|
200
|
-
data = {
|
231
|
+
data = {
|
232
|
+
'sys' => { 'id' => '1234' },
|
233
|
+
'key' => 'val',
|
234
|
+
'1' => { 'deep' => 9 }
|
235
|
+
}
|
201
236
|
subject.set('9999', data)
|
202
237
|
|
203
238
|
# act
|
@@ -209,7 +244,11 @@ RSpec.shared_examples 'basic store' do
|
|
209
244
|
end
|
210
245
|
|
211
246
|
it "returns nil if item doesn't exist" do
|
212
|
-
data = {
|
247
|
+
data = {
|
248
|
+
'sys' => { 'id' => '9999' },
|
249
|
+
'key' => 'val',
|
250
|
+
'1' => { 'deep' => 9 }
|
251
|
+
}
|
213
252
|
subject.set('9999', data)
|
214
253
|
|
215
254
|
# act
|
@@ -327,7 +366,10 @@ RSpec.shared_examples 'basic store' do
|
|
327
366
|
end
|
328
367
|
|
329
368
|
it 'updates an "Asset" when exists' do
|
330
|
-
existing = {
|
369
|
+
existing = {
|
370
|
+
'sys' => { 'id' => '3pWma8spR62aegAWAWacyA', 'revision' => 1 },
|
371
|
+
'test' => { 'data' => 'asdf' }
|
372
|
+
}
|
331
373
|
subject.set('3pWma8spR62aegAWAWacyA', existing)
|
332
374
|
|
333
375
|
# act
|
@@ -341,7 +383,7 @@ RSpec.shared_examples 'basic store' do
|
|
341
383
|
it 'does not overwrite an asset if revision is lower' do
|
342
384
|
initial = asset
|
343
385
|
updated = asset.deep_dup
|
344
|
-
updated['sys']['revision'] =
|
386
|
+
updated['sys']['revision'] = 3
|
345
387
|
updated['fields']['title']['en-US'] = 'test title'
|
346
388
|
|
347
389
|
subject.index(updated)
|
@@ -355,7 +397,10 @@ RSpec.shared_examples 'basic store' do
|
|
355
397
|
end
|
356
398
|
|
357
399
|
it 'removes a "DeletedEntry"' do
|
358
|
-
existing = {
|
400
|
+
existing = {
|
401
|
+
'sys' => { 'id' => '6HQsABhZDiWmi0ekCouUuy' },
|
402
|
+
'test' => { 'data' => 'asdf' }
|
403
|
+
}
|
359
404
|
subject.set('6HQsABhZDiWmi0ekCouUuy', existing)
|
360
405
|
|
361
406
|
# act
|
@@ -381,7 +426,10 @@ RSpec.shared_examples 'basic store' do
|
|
381
426
|
end
|
382
427
|
|
383
428
|
it 'removes a "DeletedAsset"' do
|
384
|
-
existing = {
|
429
|
+
existing = {
|
430
|
+
'sys' => { 'id' => '3pWma8spR62aegAWAWacyA' },
|
431
|
+
'test' => { 'data' => 'asdf' }
|
432
|
+
}
|
385
433
|
subject.set('3pWma8spR62aegAWAWacyA', existing)
|
386
434
|
|
387
435
|
# act
|
@@ -528,6 +576,18 @@ RSpec.shared_examples 'basic store' do
|
|
528
576
|
expect(found.dig('sys', 'id')).to eq('idTwo')
|
529
577
|
expect(found.dig('fields', 'system', 'en-US')).to eq('Two')
|
530
578
|
end
|
579
|
+
|
580
|
+
it 'modifying found entry does not modify underlying data' do
|
581
|
+
subject.index(entry)
|
582
|
+
|
583
|
+
# act
|
584
|
+
found = subject.find_by(filter: { 'sys.id' => '1qLdW7i7g4Ycq6i4Cckg44' }, content_type: 'redirect')
|
585
|
+
found['fields']['slug']['en-US'] = 'new slug'
|
586
|
+
|
587
|
+
# assert
|
588
|
+
found2 = subject.find_by(filter: { 'sys.id' => '1qLdW7i7g4Ycq6i4Cckg44' }, content_type: 'redirect')
|
589
|
+
expect(found2.dig('fields', 'slug', 'en-US')).to eq('redirect-with-slug-and-url')
|
590
|
+
end
|
531
591
|
end
|
532
592
|
|
533
593
|
describe '#find_all' do
|
@@ -572,6 +632,18 @@ RSpec.shared_examples 'basic store' do
|
|
572
632
|
%w[k1 k5 k9]
|
573
633
|
)
|
574
634
|
end
|
635
|
+
|
636
|
+
it 'modifying found entry does not modify underlying data' do
|
637
|
+
subject.index(entry)
|
638
|
+
|
639
|
+
# act
|
640
|
+
found = subject.find_all(content_type: 'redirect').eq('sys.id', '1qLdW7i7g4Ycq6i4Cckg44').first
|
641
|
+
found['fields']['slug']['en-US'] = 'new slug'
|
642
|
+
|
643
|
+
# assert
|
644
|
+
found2 = subject.find_all(content_type: 'redirect').eq('sys.id', '1qLdW7i7g4Ycq6i4Cckg44').first
|
645
|
+
expect(found2.dig('fields', 'slug', 'en-US')).to eq('redirect-with-slug-and-url')
|
646
|
+
end
|
575
647
|
end
|
576
648
|
|
577
649
|
def make_link_to(id, link_type = 'Entry')
|