wcc-contentful 1.3.2 → 1.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +80 -13
- data/lib/wcc/contentful/active_record_shim.rb +2 -2
- 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')
|