wcc-contentful 0.4.0.pre.alpha → 1.0.0.pre.rc3
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 +5 -5
- data/Guardfile +43 -0
- data/README.md +246 -11
- data/Rakefile +5 -0
- data/app/controllers/wcc/contentful/webhook_controller.rb +25 -24
- data/app/jobs/wcc/contentful/webhook_enable_job.rb +36 -2
- data/config/routes.rb +1 -1
- data/doc +1 -0
- data/lib/tasks/download_schema.rake +12 -0
- data/lib/wcc/contentful.rb +70 -16
- data/lib/wcc/contentful/active_record_shim.rb +72 -0
- data/lib/wcc/contentful/configuration.rb +177 -46
- data/lib/wcc/contentful/content_type_indexer.rb +14 -0
- data/lib/wcc/contentful/downloads_schema.rb +112 -0
- data/lib/wcc/contentful/engine.rb +33 -14
- data/lib/wcc/contentful/event.rb +171 -0
- data/lib/wcc/contentful/events.rb +41 -0
- data/lib/wcc/contentful/exceptions.rb +3 -0
- data/lib/wcc/contentful/indexed_representation.rb +2 -2
- data/lib/wcc/contentful/instrumentation.rb +31 -0
- data/lib/wcc/contentful/link.rb +28 -0
- data/lib/wcc/contentful/link_visitor.rb +122 -0
- data/lib/wcc/contentful/middleware.rb +7 -0
- data/lib/wcc/contentful/middleware/store.rb +158 -0
- data/lib/wcc/contentful/middleware/store/caching_middleware.rb +114 -0
- data/lib/wcc/contentful/model.rb +37 -3
- data/lib/wcc/contentful/model_builder.rb +1 -0
- data/lib/wcc/contentful/model_methods.rb +40 -15
- data/lib/wcc/contentful/model_singleton_methods.rb +47 -30
- data/lib/wcc/contentful/rake.rb +4 -0
- data/lib/wcc/contentful/rspec.rb +46 -0
- data/lib/wcc/contentful/services.rb +61 -27
- data/lib/wcc/contentful/simple_client.rb +81 -25
- data/lib/wcc/contentful/simple_client/management.rb +43 -10
- data/lib/wcc/contentful/simple_client/response.rb +61 -22
- data/lib/wcc/contentful/simple_client/typhoeus_adapter.rb +17 -17
- data/lib/wcc/contentful/store.rb +7 -66
- data/lib/wcc/contentful/store/README.md +85 -0
- data/lib/wcc/contentful/store/base.rb +34 -119
- data/lib/wcc/contentful/store/cdn_adapter.rb +71 -12
- data/lib/wcc/contentful/store/factory.rb +186 -0
- data/lib/wcc/contentful/store/instrumentation.rb +55 -0
- data/lib/wcc/contentful/store/interface.rb +82 -0
- data/lib/wcc/contentful/store/memory_store.rb +27 -24
- data/lib/wcc/contentful/store/postgres_store.rb +268 -101
- data/lib/wcc/contentful/store/postgres_store/schema_1.sql +73 -0
- data/lib/wcc/contentful/store/postgres_store/schema_2.sql +21 -0
- data/lib/wcc/contentful/store/query.rb +246 -0
- data/lib/wcc/contentful/store/query/interface.rb +63 -0
- data/lib/wcc/contentful/store/rspec_examples.rb +48 -0
- data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +629 -0
- data/lib/wcc/contentful/store/rspec_examples/include_param.rb +283 -0
- data/lib/wcc/contentful/store/rspec_examples/nested_queries.rb +342 -0
- data/lib/wcc/contentful/sync_engine.rb +181 -0
- data/lib/wcc/contentful/test.rb +7 -0
- data/lib/wcc/contentful/test/attributes.rb +56 -0
- data/lib/wcc/contentful/test/double.rb +76 -0
- data/lib/wcc/contentful/test/factory.rb +101 -0
- data/lib/wcc/contentful/version.rb +1 -1
- data/wcc-contentful.gemspec +23 -11
- metadata +299 -116
- data/Gemfile +0 -6
- data/app/jobs/wcc/contentful/delayed_sync_job.rb +0 -63
- data/lib/wcc/contentful/client_ext.rb +0 -28
- data/lib/wcc/contentful/graphql.rb +0 -14
- data/lib/wcc/contentful/graphql/builder.rb +0 -177
- data/lib/wcc/contentful/graphql/types.rb +0 -54
- data/lib/wcc/contentful/simple_client/http_adapter.rb +0 -24
- data/lib/wcc/contentful/store/lazy_cache_store.rb +0 -161
@@ -4,10 +4,10 @@
|
|
4
4
|
class WCC::Contentful::SimpleClient::Management < WCC::Contentful::SimpleClient
|
5
5
|
def initialize(space:, management_token:, **options)
|
6
6
|
super(
|
7
|
-
|
7
|
+
**options,
|
8
|
+
api_url: options[:management_api_url] || 'https://api.contentful.com',
|
8
9
|
space: space,
|
9
10
|
access_token: management_token,
|
10
|
-
**options
|
11
11
|
)
|
12
12
|
|
13
13
|
@post_adapter = @adapter if @adapter.respond_to?(:post)
|
@@ -19,12 +19,34 @@ class WCC::Contentful::SimpleClient::Management < WCC::Contentful::SimpleClient
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def content_types(**query)
|
22
|
-
resp =
|
22
|
+
resp =
|
23
|
+
_instrument 'content_types', query: query do
|
24
|
+
get('content_types', query)
|
25
|
+
end
|
26
|
+
resp.assert_ok!
|
27
|
+
end
|
28
|
+
|
29
|
+
def content_type(key, query = {})
|
30
|
+
resp =
|
31
|
+
_instrument 'content_types', content_type: key, query: query do
|
32
|
+
get("content_types/#{key}", query)
|
33
|
+
end
|
34
|
+
resp.assert_ok!
|
35
|
+
end
|
36
|
+
|
37
|
+
def editor_interface(content_type_id, query = {})
|
38
|
+
resp =
|
39
|
+
_instrument 'editor_interfaces', content_type: content_type_id, query: query do
|
40
|
+
get("content_types/#{content_type_id}/editor_interface", query)
|
41
|
+
end
|
23
42
|
resp.assert_ok!
|
24
43
|
end
|
25
44
|
|
26
45
|
def webhook_definitions(**query)
|
27
|
-
resp =
|
46
|
+
resp =
|
47
|
+
_instrument 'webhook_definitions', query: query do
|
48
|
+
get("/spaces/#{space}/webhook_definitions", query)
|
49
|
+
end
|
28
50
|
resp.assert_ok!
|
29
51
|
end
|
30
52
|
|
@@ -51,30 +73,41 @@ class WCC::Contentful::SimpleClient::Management < WCC::Contentful::SimpleClient
|
|
51
73
|
# ]
|
52
74
|
# }
|
53
75
|
def post_webhook_definition(webhook)
|
54
|
-
resp =
|
76
|
+
resp =
|
77
|
+
_instrument 'post.webhook_definitions' do
|
78
|
+
post("/spaces/#{space}/webhook_definitions", webhook)
|
79
|
+
end
|
55
80
|
resp.assert_ok!
|
56
81
|
end
|
57
82
|
|
58
83
|
def post(path, body)
|
59
84
|
url = URI.join(@api_url, path)
|
60
85
|
|
86
|
+
resp =
|
87
|
+
_instrument 'post_http', url: url do
|
88
|
+
post_http(url, body)
|
89
|
+
end
|
90
|
+
|
61
91
|
Response.new(self,
|
62
92
|
{ url: url, body: body },
|
63
|
-
|
93
|
+
resp)
|
64
94
|
end
|
65
95
|
|
66
96
|
private
|
67
97
|
|
68
|
-
def post_http(url, body, headers = {}
|
98
|
+
def post_http(url, body, headers = {})
|
69
99
|
headers = {
|
70
100
|
Authorization: "Bearer #{@access_token}",
|
71
101
|
'Content-Type' => 'application/vnd.contentful.management.v1+json'
|
72
102
|
}.merge(headers || {})
|
73
103
|
|
74
|
-
|
104
|
+
body = body.to_json unless body.is_a? String
|
105
|
+
resp = @post_adapter.post(url, body, headers)
|
75
106
|
|
76
|
-
if [301, 302, 307].include?(resp.
|
77
|
-
resp = get_http(resp.headers['location'], nil, headers
|
107
|
+
if [301, 302, 307].include?(resp.status) && !@options[:no_follow_redirects]
|
108
|
+
resp = get_http(resp.headers['location'], nil, headers)
|
109
|
+
elsif resp.status == 308 && !@options[:no_follow_redirects]
|
110
|
+
resp = post_http(resp.headers['location'], body, headers)
|
78
111
|
end
|
79
112
|
resp
|
80
113
|
end
|
@@ -1,12 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../instrumentation'
|
4
|
+
|
3
5
|
class WCC::Contentful::SimpleClient
|
4
6
|
class Response
|
7
|
+
include ::WCC::Contentful::Instrumentation
|
8
|
+
|
5
9
|
attr_reader :raw_response
|
6
10
|
attr_reader :client
|
7
11
|
attr_reader :request
|
8
12
|
|
9
|
-
delegate :
|
13
|
+
delegate :status, to: :raw_response
|
14
|
+
alias_method :code, :status
|
10
15
|
delegate :headers, to: :raw_response
|
11
16
|
|
12
17
|
def body
|
@@ -19,25 +24,41 @@ class WCC::Contentful::SimpleClient
|
|
19
24
|
alias_method :to_json, :raw
|
20
25
|
|
21
26
|
def error_message
|
22
|
-
|
27
|
+
parsed_message =
|
28
|
+
begin
|
29
|
+
raw.dig('message')
|
30
|
+
rescue JSON::ParserError
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
parsed_message || "#{code}: #{raw_response.body}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def skip
|
37
|
+
raw['skip']
|
38
|
+
end
|
39
|
+
|
40
|
+
def total
|
41
|
+
raw['total']
|
23
42
|
end
|
24
43
|
|
25
44
|
def next_page?
|
26
45
|
return unless raw.key? 'items'
|
27
46
|
|
28
|
-
|
47
|
+
page_items.length + skip < total
|
29
48
|
end
|
30
49
|
|
31
50
|
def next_page
|
32
51
|
return unless next_page?
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
52
|
+
return @next_page if @next_page
|
53
|
+
|
54
|
+
query = (@request[:query] || {}).merge({
|
55
|
+
skip: page_items.length + skip
|
56
|
+
})
|
57
|
+
np =
|
58
|
+
_instrument 'page', url: @request[:url], query: query do
|
59
|
+
@client.get(@request[:url], query)
|
60
|
+
end
|
61
|
+
@next_page = np.assert_ok!
|
41
62
|
end
|
42
63
|
|
43
64
|
def initialize(client, request, raw_response)
|
@@ -48,13 +69,13 @@ class WCC::Contentful::SimpleClient
|
|
48
69
|
end
|
49
70
|
|
50
71
|
def assert_ok!
|
51
|
-
return self if
|
72
|
+
return self if status >= 200 && status < 300
|
52
73
|
|
53
|
-
raise ApiError[
|
74
|
+
raise ApiError[status], self
|
54
75
|
end
|
55
76
|
|
56
77
|
def each_page(&block)
|
57
|
-
raise ArgumentError, 'Not a collection response' unless
|
78
|
+
raise ArgumentError, 'Not a collection response' unless page_items
|
58
79
|
|
59
80
|
ret =
|
60
81
|
Enumerator.new do |y|
|
@@ -75,19 +96,21 @@ class WCC::Contentful::SimpleClient
|
|
75
96
|
end
|
76
97
|
|
77
98
|
def items
|
78
|
-
each_page.flat_map
|
79
|
-
|
80
|
-
|
99
|
+
each_page.flat_map(&:page_items)
|
100
|
+
end
|
101
|
+
|
102
|
+
def page_items
|
103
|
+
raw['items']
|
81
104
|
end
|
82
105
|
|
83
106
|
def count
|
84
|
-
|
107
|
+
total
|
85
108
|
end
|
86
109
|
|
87
110
|
def first
|
88
|
-
raise ArgumentError, 'Not a collection response' unless
|
111
|
+
raise ArgumentError, 'Not a collection response' unless page_items
|
89
112
|
|
90
|
-
|
113
|
+
page_items.first
|
91
114
|
end
|
92
115
|
|
93
116
|
def includes
|
@@ -115,7 +138,13 @@ class WCC::Contentful::SimpleClient
|
|
115
138
|
def next_page
|
116
139
|
return unless next_page?
|
117
140
|
|
118
|
-
|
141
|
+
url = raw['nextPageUrl']
|
142
|
+
next_page =
|
143
|
+
_instrument 'page', url: url do
|
144
|
+
@client.get(url)
|
145
|
+
end
|
146
|
+
|
147
|
+
@next_page ||= SyncResponse.new(next_page)
|
119
148
|
@next_page.assert_ok!
|
120
149
|
end
|
121
150
|
|
@@ -129,7 +158,7 @@ class WCC::Contentful::SimpleClient
|
|
129
158
|
end
|
130
159
|
|
131
160
|
def each_page
|
132
|
-
raise ArgumentError, 'Not a collection response' unless
|
161
|
+
raise ArgumentError, 'Not a collection response' unless page_items
|
133
162
|
|
134
163
|
ret =
|
135
164
|
Enumerator.new do |y|
|
@@ -168,6 +197,10 @@ class WCC::Contentful::SimpleClient
|
|
168
197
|
case code
|
169
198
|
when 404
|
170
199
|
NotFoundError
|
200
|
+
when 401
|
201
|
+
UnauthorizedError
|
202
|
+
when 429
|
203
|
+
RateLimitError
|
171
204
|
else
|
172
205
|
ApiError
|
173
206
|
end
|
@@ -181,4 +214,10 @@ class WCC::Contentful::SimpleClient
|
|
181
214
|
|
182
215
|
class NotFoundError < ApiError
|
183
216
|
end
|
217
|
+
|
218
|
+
class UnauthorizedError < ApiError
|
219
|
+
end
|
220
|
+
|
221
|
+
class RateLimitError < ApiError
|
222
|
+
end
|
184
223
|
end
|
@@ -3,15 +3,15 @@
|
|
3
3
|
gem 'typhoeus'
|
4
4
|
require 'typhoeus'
|
5
5
|
|
6
|
-
class TyphoeusAdapter
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
class WCC::Contentful::SimpleClient::TyphoeusAdapter
|
7
|
+
def get(url, params = {}, headers = {})
|
8
|
+
req = OpenStruct.new(params: params, headers: headers)
|
9
|
+
yield req if block_given?
|
10
|
+
Response.new(
|
11
11
|
Typhoeus.get(
|
12
12
|
url,
|
13
|
-
params:
|
14
|
-
headers: headers
|
13
|
+
params: req.params,
|
14
|
+
headers: req.headers
|
15
15
|
)
|
16
16
|
)
|
17
17
|
end
|
@@ -19,7 +19,7 @@ class TyphoeusAdapter
|
|
19
19
|
def post(url, body, headers = {}, proxy = {})
|
20
20
|
raise NotImplementedError, 'Proxying Not Yet Implemented' if proxy[:host]
|
21
21
|
|
22
|
-
|
22
|
+
Response.new(
|
23
23
|
Typhoeus.post(
|
24
24
|
url,
|
25
25
|
body: body.to_json,
|
@@ -28,15 +28,15 @@ class TyphoeusAdapter
|
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
31
|
-
Response
|
32
|
-
|
33
|
-
delegate :body, to: :raw
|
34
|
-
delegate :to_s, to: :body
|
35
|
-
delegate :code, to: :raw
|
36
|
-
delegate :headers, to: :raw
|
31
|
+
class Response < SimpleDelegator
|
32
|
+
delegate :to_s, to: :body
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
end
|
34
|
+
def raw
|
35
|
+
__getobj__
|
41
36
|
end
|
37
|
+
|
38
|
+
def status
|
39
|
+
code
|
40
|
+
end
|
41
|
+
end
|
42
42
|
end
|
data/lib/wcc/contentful/store.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'store/
|
4
|
-
require_relative 'store/memory_store'
|
5
|
-
require_relative 'store/cdn_adapter'
|
6
|
-
require_relative 'store/lazy_cache_store'
|
3
|
+
require_relative 'store/factory'
|
7
4
|
|
8
5
|
# The "Store" is the middle layer in the WCC::Contentful gem. It exposes an API
|
9
6
|
# that implements the configured content delivery strategy.
|
@@ -18,84 +15,28 @@ require_relative 'store/lazy_cache_store'
|
|
18
15
|
#
|
19
16
|
# lazy_sync:: Uses the Contentful CDN in combination with an ActiveSupport::Cache
|
20
17
|
# implementation in order to respond with the cached data where possible,
|
21
|
-
# saving your CDN quota. The cache is kept up-to-date via the Sync
|
22
|
-
# and the WCC::Contentful::
|
18
|
+
# saving your CDN quota. The cache is kept up-to-date via the Sync Engine
|
19
|
+
# and the WCC::Contentful::SyncEngine::Job. It is correct, but not complete.
|
23
20
|
#
|
24
21
|
# eager_sync:: Uses one of the full store implementations to store the entirety
|
25
22
|
# of the Contentful space locally. All queries are run against this
|
26
|
-
# local copy, which is kept up to date via the Sync
|
27
|
-
# WCC::Contentful::
|
23
|
+
# local copy, which is kept up to date via the Sync Engine and the
|
24
|
+
# WCC::Contentful::SyncEngine::Job. The local store is correct and complete.
|
28
25
|
#
|
29
26
|
# The currently configured store is available on WCC::Contentful::Services.instance.store
|
30
27
|
module WCC::Contentful::Store
|
31
28
|
SYNC_STORES = {
|
32
|
-
memory: ->(_config) { WCC::Contentful::Store::MemoryStore.new },
|
29
|
+
memory: ->(_config, *_options) { WCC::Contentful::Store::MemoryStore.new },
|
33
30
|
postgres: ->(config, *options) {
|
34
31
|
require_relative 'store/postgres_store'
|
35
32
|
WCC::Contentful::Store::PostgresStore.new(config, *options)
|
36
33
|
}
|
37
34
|
}.freeze
|
38
35
|
|
39
|
-
|
36
|
+
PRESETS = %i[
|
40
37
|
eager_sync
|
41
38
|
lazy_sync
|
42
39
|
direct
|
43
40
|
custom
|
44
41
|
].freeze
|
45
|
-
|
46
|
-
Factory =
|
47
|
-
Struct.new(:config, :services, :cdn_method, :content_delivery_params) do
|
48
|
-
def build_sync_store
|
49
|
-
unless respond_to?("build_#{cdn_method}")
|
50
|
-
raise ArgumentError, "Don't know how to build content delivery method #{cdn_method}"
|
51
|
-
end
|
52
|
-
|
53
|
-
public_send("build_#{cdn_method}", config, *content_delivery_params)
|
54
|
-
end
|
55
|
-
|
56
|
-
def validate!
|
57
|
-
unless CDN_METHODS.include?(cdn_method)
|
58
|
-
raise ArgumentError, "Please use one of #{CDN_METHODS} instead of #{cdn_method}"
|
59
|
-
end
|
60
|
-
|
61
|
-
return unless respond_to?("validate_#{cdn_method}")
|
62
|
-
|
63
|
-
public_send("validate_#{cdn_method}", config, *content_delivery_params)
|
64
|
-
end
|
65
|
-
|
66
|
-
def build_eager_sync(config, store = nil, *options)
|
67
|
-
store = SYNC_STORES[store].call(config, *options) if store.is_a?(Symbol)
|
68
|
-
store || MemoryStore.new
|
69
|
-
end
|
70
|
-
|
71
|
-
def build_lazy_sync(_config, *options)
|
72
|
-
WCC::Contentful::Store::LazyCacheStore.new(
|
73
|
-
services.client,
|
74
|
-
cache: ActiveSupport::Cache.lookup_store(*options)
|
75
|
-
)
|
76
|
-
end
|
77
|
-
|
78
|
-
def build_direct(_config, *options)
|
79
|
-
if options.find { |array| array[:preview] == true }
|
80
|
-
CDNAdapter.new(services.preview_client)
|
81
|
-
else
|
82
|
-
CDNAdapter.new(services.client)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def build_custom(config, *options)
|
87
|
-
store = config.store
|
88
|
-
return store unless store&.respond_to?(:new)
|
89
|
-
|
90
|
-
store.new(config, options)
|
91
|
-
end
|
92
|
-
|
93
|
-
def validate_eager_sync(_config, store = nil, *_options)
|
94
|
-
return unless store.is_a?(Symbol)
|
95
|
-
|
96
|
-
return if SYNC_STORES.key?(store)
|
97
|
-
|
98
|
-
raise ArgumentError, "Please use one of #{SYNC_STORES.keys}"
|
99
|
-
end
|
100
|
-
end
|
101
42
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Store API
|
2
|
+
|
3
|
+
The Store layer is used by the Model API to access Contentful data in a raw form.
|
4
|
+
The Store layer returns entries as hashes parsed from JSON, conforming to the
|
5
|
+
object structure returned from the Contentful CDN.
|
6
|
+
|
7
|
+
## Public Interface
|
8
|
+
|
9
|
+
See WCC::Contentful::Store::Interface in wcc/contentful/store/interface.rb
|
10
|
+
|
11
|
+
This is the interface consumed by higher layers, such as the Model and GraphQL
|
12
|
+
APIs. It implements the following methods:
|
13
|
+
|
14
|
+
* `index?` - Returns boolean true if the SyncEngine should call the store's `index(json)`
|
15
|
+
method with the results of a Sync.
|
16
|
+
* `index(json)` - Updates the store with the latest data. The JSON can be an Entry,
|
17
|
+
Asset, DeletedEntry, or DeletedAsset.
|
18
|
+
* `find(id)` - Finds an entry or asset by it's ID.
|
19
|
+
* `find_all(content_type:, options: nil)` - Returns a query object that can be
|
20
|
+
enumerated to lazily iterate over all values of a content type. Query conditions
|
21
|
+
can be applied on the query object before it is enumerated to restrict the result
|
22
|
+
set.
|
23
|
+
* `find_by(content_type:, filter: nil, options: nil)` - Returns the first entry
|
24
|
+
of the given content type which matches the filter.
|
25
|
+
Note: assets have the special content
|
26
|
+
type of `Asset` (capital A)
|
27
|
+
|
28
|
+
## Implementing your own store
|
29
|
+
|
30
|
+
The most straightforward way to implement a store is to include the
|
31
|
+
WCC::Contentful::Store::Interface module and then override all the defined
|
32
|
+
methods. The easiest way however, is to inherit from WCC::Contentful::Store::Base
|
33
|
+
and override the `set`, `delete`, `find`, and `execute` methods.
|
34
|
+
|
35
|
+
Let's take a look at the MemoryStore for a simplistic example. The MemoryStore
|
36
|
+
stores entries in a simple Ruby hash keyed by entry ID. `set` is simply
|
37
|
+
assigning to the key in the hash and returning the old value. `delete` and `find`
|
38
|
+
are likewise simple. The only complex method is `execute`, because it powers the
|
39
|
+
`find_by` and `find_all` query methods.
|
40
|
+
|
41
|
+
The query passed in to `execute` is an instance of WCC::Contentful::Store::Query.
|
42
|
+
This object contains a set of WCC::Contentful::Store::Query::Condition structs.
|
43
|
+
Each struct is a tuple of `path`, `op`, and `expected`. `op` is one of
|
44
|
+
WCC::Contentful::Store::Query::Interface::OPERATORS, `path` is an array of fields
|
45
|
+
pointing to a value in the JSON representation of an entry, and `expected` is the
|
46
|
+
expected value that should be compared to the value selected by `path`.
|
47
|
+
|
48
|
+
Since the MemoryStore only implements the equality operator, it simply digs
|
49
|
+
out the value at the given path using `val = entry.dig(*condition.path)`
|
50
|
+
and compares it using Contentful's definition of equality:
|
51
|
+
```rb
|
52
|
+
# For arrays, equality is defined as does the array include the expected value.
|
53
|
+
# See https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/array-equality-inequality
|
54
|
+
if val.is_a? Array
|
55
|
+
val.include?(condition.expected)
|
56
|
+
else
|
57
|
+
val == condition.expected
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
### RSpec shared examples
|
62
|
+
|
63
|
+
To ensure you have implemented all the appropriate behavior, there
|
64
|
+
are a set of rspec shared examples in wcc/contentful/store/rspec_examples.rb which
|
65
|
+
you can include in your specs for your store implementation. Let's look at
|
66
|
+
spec/wcc/contentful/store/memory_store_spec.rb to see how it's used:
|
67
|
+
|
68
|
+
```rb
|
69
|
+
require 'wcc/contentful/store/rspec_examples'
|
70
|
+
|
71
|
+
RSpec.describe WCC::Contentful::Store::MemoryStore do
|
72
|
+
subject { WCC::Contentful::Store::MemoryStore.new }
|
73
|
+
|
74
|
+
it_behaves_like 'contentful store', {
|
75
|
+
# memory store does not support JOINs like `Player.find_by(team: { slug: 'dallas-cowboys' })
|
76
|
+
nested_queries: false,
|
77
|
+
# Memory store supports resolving includes, but it does so in the most naiive
|
78
|
+
# way possible (by recursing down the entry's links and calling #find on every one)
|
79
|
+
include_param: 0
|
80
|
+
}
|
81
|
+
```
|
82
|
+
|
83
|
+
The hash passed to the shared examples describes the features that the store
|
84
|
+
supports. Any key not provided causes the specs to be given the 'pending'
|
85
|
+
attribute. You can disable a set of specs by providing `false` for that key.
|