wcc-contentful 1.2.1 → 1.3.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.
- checksums.yaml +4 -4
- data/lib/wcc/contentful/configuration.rb +6 -0
- data/lib/wcc/contentful/services.rb +9 -0
- data/lib/wcc/contentful/simple_client/cdn.rb +126 -0
- data/lib/wcc/contentful/simple_client/preview.rb +17 -0
- data/lib/wcc/contentful/simple_client/response.rb +21 -14
- data/lib/wcc/contentful/simple_client.rb +2 -104
- data/lib/wcc/contentful/store/base.rb +19 -27
- data/lib/wcc/contentful/store/memory_store.rb +8 -5
- data/lib/wcc/contentful/store/postgres_store.rb +42 -25
- data/lib/wcc/contentful/sync_engine.rb +27 -19
- data/lib/wcc/contentful/version.rb +1 -1
- data/lib/wcc/contentful.rb +5 -6
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9026bfcc1e7745f7d8ecf3fb38928672f02d663d382fd1a3ca953ab33a78cb25
|
4
|
+
data.tar.gz: 36ec7c7f47fb6e49d565d7ca82c5021ac8f88b46eabfe41754ac5c123ac0004d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 633116bd83752074876ccf76265eb64bc6a19a28b9a477bf2026925f0a84977cd651a234f5656b7a657a43e65b3320031e2385ca398bb2b846ab730693105799
|
7
|
+
data.tar.gz: 32f1f08bac98d42d89a136c0b49d2bb1ee1a72e057326edd50a17834b53b2da7fecbbe1db1aded355d2849bc1d222d4f16d3e721024a3c105f786349ab6548fc
|
@@ -10,6 +10,7 @@ class WCC::Contentful::Configuration
|
|
10
10
|
default_locale
|
11
11
|
environment
|
12
12
|
instrumentation_adapter
|
13
|
+
logger
|
13
14
|
management_token
|
14
15
|
preview_token
|
15
16
|
schema_file
|
@@ -182,6 +183,11 @@ class WCC::Contentful::Configuration
|
|
182
183
|
# to :instrument like ActiveSupport::Notifications.instrument
|
183
184
|
attr_accessor :instrumentation_adapter
|
184
185
|
|
186
|
+
# Sets the logger to be used by the wcc-contentful gem, including stores.
|
187
|
+
# Defaults to the rails logger if in a rails context, otherwise creates a new
|
188
|
+
# logger that writes to STDERR.
|
189
|
+
attr_accessor :logger
|
190
|
+
|
185
191
|
def initialize
|
186
192
|
@access_token = ENV.fetch('CONTENTFUL_ACCESS_TOKEN', nil)
|
187
193
|
@app_url = ENV.fetch('APP_URL', nil)
|
@@ -132,6 +132,15 @@ module WCC::Contentful
|
|
132
132
|
# Allow it to be injected into a store
|
133
133
|
alias_method :_instrumentation, :instrumentation
|
134
134
|
|
135
|
+
# Gets the configured logger, defaulting to Rails.logger in a rails context,
|
136
|
+
# or logging to STDERR in a non-rails context.
|
137
|
+
def logger
|
138
|
+
@logger ||=
|
139
|
+
configuration.logger ||
|
140
|
+
(Rails.logger if defined?(Rails)) ||
|
141
|
+
Logger.new($stderr)
|
142
|
+
end
|
143
|
+
|
135
144
|
##
|
136
145
|
# This method enables simple dependency injection -
|
137
146
|
# If the target has a setter matching the name of one of the services,
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The CDN SimpleClient accesses 'https://cdn.contentful.com' to get raw
|
4
|
+
# JSON responses. It exposes methods to query entries, assets, and content_types.
|
5
|
+
# The responses are instances of WCC::Contentful::SimpleClient::Response
|
6
|
+
# which handles paging automatically.
|
7
|
+
#
|
8
|
+
# @api Client
|
9
|
+
class WCC::Contentful::SimpleClient::Cdn < WCC::Contentful::SimpleClient
|
10
|
+
def initialize(space:, access_token:, **options)
|
11
|
+
super(
|
12
|
+
api_url: options[:api_url] || 'https://cdn.contentful.com/',
|
13
|
+
space: space,
|
14
|
+
access_token: access_token,
|
15
|
+
**options
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def client_type
|
20
|
+
'cdn'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets an entry by ID
|
24
|
+
def entry(key, query = {})
|
25
|
+
resp =
|
26
|
+
_instrument 'entries', id: key, type: 'Entry', query: query do
|
27
|
+
get("entries/#{key}", query)
|
28
|
+
end
|
29
|
+
resp.assert_ok!
|
30
|
+
end
|
31
|
+
|
32
|
+
# Queries entries with optional query parameters
|
33
|
+
def entries(query = {})
|
34
|
+
resp =
|
35
|
+
_instrument 'entries', type: 'Entry', query: query do
|
36
|
+
get('entries', query)
|
37
|
+
end
|
38
|
+
resp.assert_ok!
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gets an asset by ID
|
42
|
+
def asset(key, query = {})
|
43
|
+
resp =
|
44
|
+
_instrument 'entries', type: 'Asset', id: key, query: query do
|
45
|
+
get("assets/#{key}", query)
|
46
|
+
end
|
47
|
+
resp.assert_ok!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Queries assets with optional query parameters
|
51
|
+
def assets(query = {})
|
52
|
+
resp =
|
53
|
+
_instrument 'entries', type: 'Asset', query: query do
|
54
|
+
get('assets', query)
|
55
|
+
end
|
56
|
+
resp.assert_ok!
|
57
|
+
end
|
58
|
+
|
59
|
+
# Queries content types with optional query parameters
|
60
|
+
def content_types(query = {})
|
61
|
+
resp =
|
62
|
+
_instrument 'content_types', query: query do
|
63
|
+
get('content_types', query)
|
64
|
+
end
|
65
|
+
resp.assert_ok!
|
66
|
+
end
|
67
|
+
|
68
|
+
# Accesses the Sync API to get a list of items that have changed since
|
69
|
+
# the last sync. Accepts a block that receives each changed item, and returns
|
70
|
+
# the next sync token.
|
71
|
+
#
|
72
|
+
# If `sync_token` is nil, an initial sync is performed.
|
73
|
+
#
|
74
|
+
# @return String the next sync token parsed from nextSyncUrl
|
75
|
+
# @example
|
76
|
+
# my_sync_token = storage.get('sync_token')
|
77
|
+
# my_sync_token = client.sync(sync_token: my_sync_token) do |item|
|
78
|
+
# storage.put(item.dig('sys', 'id'), item) }
|
79
|
+
# end
|
80
|
+
# storage.put('sync_token', my_sync_token)
|
81
|
+
def sync(sync_token: nil, **query, &block)
|
82
|
+
return sync_old(sync_token: sync_token, **query) unless block_given?
|
83
|
+
|
84
|
+
sync_token =
|
85
|
+
if sync_token
|
86
|
+
{ sync_token: sync_token }
|
87
|
+
else
|
88
|
+
{ initial: true }
|
89
|
+
end
|
90
|
+
query = query.merge(sync_token)
|
91
|
+
|
92
|
+
_instrument 'sync', sync_token: sync_token, query: query do
|
93
|
+
resp = get('sync', query)
|
94
|
+
resp = SyncResponse.new(resp)
|
95
|
+
resp.assert_ok!
|
96
|
+
|
97
|
+
resp.each_page do |page|
|
98
|
+
page.page_items.each(&block)
|
99
|
+
sync_token = resp.next_sync_token
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
sync_token
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def sync_old(sync_token: nil, **query)
|
109
|
+
ActiveSupport::Deprecation.warn('Sync without a block is deprecated, please use new block syntax instead')
|
110
|
+
|
111
|
+
sync_token =
|
112
|
+
if sync_token
|
113
|
+
{ sync_token: sync_token }
|
114
|
+
else
|
115
|
+
{ initial: true }
|
116
|
+
end
|
117
|
+
query = query.merge(sync_token)
|
118
|
+
|
119
|
+
resp =
|
120
|
+
_instrument 'sync', sync_token: sync_token, query: query do
|
121
|
+
get('sync', query)
|
122
|
+
end
|
123
|
+
resp = SyncResponse.new(resp, memoize: true)
|
124
|
+
resp.assert_ok!
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api Client
|
4
|
+
class WCC::Contentful::SimpleClient::Preview < WCC::Contentful::SimpleClient::Cdn
|
5
|
+
def initialize(space:, preview_token:, **options)
|
6
|
+
super(
|
7
|
+
**options,
|
8
|
+
api_url: options[:preview_api_url] || 'https://preview.contentful.com/',
|
9
|
+
space: space,
|
10
|
+
access_token: preview_token
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def client_type
|
15
|
+
'preview'
|
16
|
+
end
|
17
|
+
end
|
@@ -110,8 +110,9 @@ class WCC::Contentful::SimpleClient
|
|
110
110
|
end
|
111
111
|
|
112
112
|
class SyncResponse < Response
|
113
|
-
def initialize(response)
|
113
|
+
def initialize(response, memoize: false)
|
114
114
|
super(response.client, response.request, response.raw_response)
|
115
|
+
@memoize = memoize
|
115
116
|
end
|
116
117
|
|
117
118
|
def next_page?
|
@@ -120,6 +121,7 @@ class WCC::Contentful::SimpleClient
|
|
120
121
|
|
121
122
|
def next_page
|
122
123
|
return unless next_page?
|
124
|
+
return @next_page if @next_page
|
123
125
|
|
124
126
|
url = raw['nextPageUrl']
|
125
127
|
next_page =
|
@@ -129,26 +131,31 @@ class WCC::Contentful::SimpleClient
|
|
129
131
|
|
130
132
|
next_page = SyncResponse.new(next_page)
|
131
133
|
next_page.assert_ok!
|
134
|
+
@next_page = next_page if @memoize
|
135
|
+
next_page
|
132
136
|
end
|
133
137
|
|
134
138
|
def next_sync_token
|
135
|
-
# If we
|
136
|
-
#
|
137
|
-
|
138
|
-
|
139
|
-
raw['nextPageUrl'] || raw['nextSyncUrl']
|
140
|
-
)
|
141
|
-
end
|
142
|
-
|
143
|
-
def each_page
|
144
|
-
raise ArgumentError, 'Not a collection response' unless page_items
|
139
|
+
# If we have iterated some pages, return the sync token of the final
|
140
|
+
# page that was iterated. Do this without maintaining a reference to
|
141
|
+
# all the pages.
|
142
|
+
return @last_sync_token if @last_sync_token
|
145
143
|
|
146
|
-
|
144
|
+
SyncResponse.parse_sync_token(raw['nextPageUrl'] || raw['nextSyncUrl'])
|
145
|
+
end
|
147
146
|
|
147
|
+
def each_page(&block)
|
148
148
|
if block_given?
|
149
|
-
|
149
|
+
super do |page|
|
150
|
+
@last_sync_token = page.next_sync_token
|
151
|
+
|
152
|
+
yield page
|
153
|
+
end
|
150
154
|
else
|
151
|
-
|
155
|
+
super.map do |page|
|
156
|
+
@last_sync_token = page.next_sync_token
|
157
|
+
page
|
158
|
+
end
|
152
159
|
end
|
153
160
|
end
|
154
161
|
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require_relative 'simple_client/response'
|
4
4
|
require_relative 'simple_client/management'
|
5
|
+
require_relative 'simple_client/cdn'
|
6
|
+
require_relative 'simple_client/preview'
|
5
7
|
require_relative 'instrumentation'
|
6
8
|
|
7
9
|
module WCC::Contentful
|
@@ -146,109 +148,5 @@ module WCC::Contentful
|
|
146
148
|
return resp
|
147
149
|
end
|
148
150
|
end
|
149
|
-
|
150
|
-
# The CDN SimpleClient accesses 'https://cdn.contentful.com' to get raw
|
151
|
-
# JSON responses. It exposes methods to query entries, assets, and content_types.
|
152
|
-
# The responses are instances of WCC::Contentful::SimpleClient::Response
|
153
|
-
# which handles paging automatically.
|
154
|
-
#
|
155
|
-
# @api Client
|
156
|
-
class Cdn < SimpleClient
|
157
|
-
def initialize(space:, access_token:, **options)
|
158
|
-
super(
|
159
|
-
api_url: options[:api_url] || 'https://cdn.contentful.com/',
|
160
|
-
space: space,
|
161
|
-
access_token: access_token,
|
162
|
-
**options
|
163
|
-
)
|
164
|
-
end
|
165
|
-
|
166
|
-
def client_type
|
167
|
-
'cdn'
|
168
|
-
end
|
169
|
-
|
170
|
-
# Gets an entry by ID
|
171
|
-
def entry(key, query = {})
|
172
|
-
resp =
|
173
|
-
_instrument 'entries', id: key, type: 'Entry', query: query do
|
174
|
-
get("entries/#{key}", query)
|
175
|
-
end
|
176
|
-
resp.assert_ok!
|
177
|
-
end
|
178
|
-
|
179
|
-
# Queries entries with optional query parameters
|
180
|
-
def entries(query = {})
|
181
|
-
resp =
|
182
|
-
_instrument 'entries', type: 'Entry', query: query do
|
183
|
-
get('entries', query)
|
184
|
-
end
|
185
|
-
resp.assert_ok!
|
186
|
-
end
|
187
|
-
|
188
|
-
# Gets an asset by ID
|
189
|
-
def asset(key, query = {})
|
190
|
-
resp =
|
191
|
-
_instrument 'entries', type: 'Asset', id: key, query: query do
|
192
|
-
get("assets/#{key}", query)
|
193
|
-
end
|
194
|
-
resp.assert_ok!
|
195
|
-
end
|
196
|
-
|
197
|
-
# Queries assets with optional query parameters
|
198
|
-
def assets(query = {})
|
199
|
-
resp =
|
200
|
-
_instrument 'entries', type: 'Asset', query: query do
|
201
|
-
get('assets', query)
|
202
|
-
end
|
203
|
-
resp.assert_ok!
|
204
|
-
end
|
205
|
-
|
206
|
-
# Queries content types with optional query parameters
|
207
|
-
def content_types(query = {})
|
208
|
-
resp =
|
209
|
-
_instrument 'content_types', query: query do
|
210
|
-
get('content_types', query)
|
211
|
-
end
|
212
|
-
resp.assert_ok!
|
213
|
-
end
|
214
|
-
|
215
|
-
# Accesses the Sync API to get a list of items that have changed since
|
216
|
-
# the last sync.
|
217
|
-
#
|
218
|
-
# If `sync_token` is nil, an initial sync is performed.
|
219
|
-
# Returns a WCC::Contentful::SimpleClient::SyncResponse
|
220
|
-
# which handles paging automatically.
|
221
|
-
def sync(sync_token: nil, **query)
|
222
|
-
sync_token =
|
223
|
-
if sync_token
|
224
|
-
{ sync_token: sync_token }
|
225
|
-
else
|
226
|
-
{ initial: true }
|
227
|
-
end
|
228
|
-
query = query.merge(sync_token)
|
229
|
-
resp =
|
230
|
-
_instrument 'sync', sync_token: sync_token, query: query do
|
231
|
-
get('sync', query)
|
232
|
-
end
|
233
|
-
resp = SyncResponse.new(resp)
|
234
|
-
resp.assert_ok!
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
# @api Client
|
239
|
-
class Preview < Cdn
|
240
|
-
def initialize(space:, preview_token:, **options)
|
241
|
-
super(
|
242
|
-
**options,
|
243
|
-
api_url: options[:preview_api_url] || 'https://preview.contentful.com/',
|
244
|
-
space: space,
|
245
|
-
access_token: preview_token
|
246
|
-
)
|
247
|
-
end
|
248
|
-
|
249
|
-
def client_type
|
250
|
-
'preview'
|
251
|
-
end
|
252
|
-
end
|
253
151
|
end
|
254
152
|
end
|
@@ -50,30 +50,30 @@ module WCC::Contentful::Store
|
|
50
50
|
# implementation calls into #set and #delete to perform the appropriate
|
51
51
|
# operations in the store.
|
52
52
|
def index(json)
|
53
|
+
# This implementation assumes that #delete and #set are individually thread-safe.
|
54
|
+
# No mutex is needed so long as the revisions are accurate.
|
53
55
|
# Subclasses can override to do this in a more performant thread-safe way.
|
54
56
|
# Example: postgres_store could do this in a stored procedure for speed
|
55
|
-
|
56
|
-
|
57
|
-
case type = json.dig('sys', 'type')
|
58
|
-
when 'DeletedEntry', 'DeletedAsset'
|
59
|
-
delete(json.dig('sys', 'id'))
|
60
|
-
else
|
61
|
-
set(json.dig('sys', 'id'), json)
|
62
|
-
end
|
63
|
-
|
64
|
-
if (prev_rev = prev&.dig('sys', 'revision')) &&
|
65
|
-
(next_rev = json.dig('sys', 'revision')) &&
|
66
|
-
(next_rev < prev_rev)
|
67
|
-
# Uh oh! we overwrote an entry with a prior revision. Put the previous back.
|
68
|
-
return index(prev)
|
69
|
-
end
|
70
|
-
|
71
|
-
case type
|
57
|
+
prev =
|
58
|
+
case type = json.dig('sys', 'type')
|
72
59
|
when 'DeletedEntry', 'DeletedAsset'
|
73
|
-
|
60
|
+
delete(json.dig('sys', 'id'))
|
74
61
|
else
|
75
|
-
json
|
62
|
+
set(json.dig('sys', 'id'), json)
|
76
63
|
end
|
64
|
+
|
65
|
+
if (prev_rev = prev&.dig('sys', 'revision')) &&
|
66
|
+
(next_rev = json.dig('sys', 'revision')) &&
|
67
|
+
(next_rev < prev_rev)
|
68
|
+
# Uh oh! we overwrote an entry with a prior revision. Put the previous back.
|
69
|
+
return index(prev)
|
70
|
+
end
|
71
|
+
|
72
|
+
case type
|
73
|
+
when 'DeletedEntry', 'DeletedAsset'
|
74
|
+
nil
|
75
|
+
else
|
76
|
+
json
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -107,17 +107,9 @@ module WCC::Contentful::Store
|
|
107
107
|
)
|
108
108
|
end
|
109
109
|
|
110
|
-
def initialize
|
111
|
-
@mutex = Concurrent::ReentrantReadWriteLock.new
|
112
|
-
end
|
113
|
-
|
114
110
|
def ensure_hash(val)
|
115
111
|
raise ArgumentError, 'Value must be a Hash' unless val.is_a?(Hash)
|
116
112
|
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
attr_reader :mutex
|
121
113
|
end
|
122
114
|
end
|
123
115
|
|
@@ -9,13 +9,15 @@ module WCC::Contentful::Store
|
|
9
9
|
class MemoryStore < Base
|
10
10
|
def initialize
|
11
11
|
super
|
12
|
+
|
13
|
+
@mutex = Concurrent::ReentrantReadWriteLock.new
|
12
14
|
@hash = {}
|
13
15
|
end
|
14
16
|
|
15
17
|
def set(key, value)
|
16
18
|
value = value.deep_dup.freeze
|
17
19
|
ensure_hash value
|
18
|
-
mutex.with_write_lock do
|
20
|
+
@mutex.with_write_lock do
|
19
21
|
old = @hash[key]
|
20
22
|
@hash[key] = value
|
21
23
|
old
|
@@ -23,17 +25,17 @@ module WCC::Contentful::Store
|
|
23
25
|
end
|
24
26
|
|
25
27
|
def delete(key)
|
26
|
-
mutex.with_write_lock do
|
28
|
+
@mutex.with_write_lock do
|
27
29
|
@hash.delete(key)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
33
|
def keys
|
32
|
-
mutex.with_read_lock { @hash.keys }
|
34
|
+
@mutex.with_read_lock { @hash.keys }
|
33
35
|
end
|
34
36
|
|
35
37
|
def find(key, **_options)
|
36
|
-
mutex.with_read_lock do
|
38
|
+
@mutex.with_read_lock do
|
37
39
|
@hash[key]
|
38
40
|
end
|
39
41
|
end
|
@@ -45,7 +47,8 @@ module WCC::Contentful::Store
|
|
45
47
|
raise ArgumentError, "Operator :#{bad_op} not supported"
|
46
48
|
end
|
47
49
|
|
48
|
-
|
50
|
+
# Since @hash.values returns a new array, we only need to lock here
|
51
|
+
relation = @mutex.with_read_lock { @hash.values }
|
49
52
|
|
50
53
|
# relation is an enumerable that we apply conditions to in the form of
|
51
54
|
# Enumerable#select and Enumerable#reject.
|
@@ -23,18 +23,22 @@ module WCC::Contentful::Store
|
|
23
23
|
connection_options ||= { dbname: 'postgres' }
|
24
24
|
pool_options ||= {}
|
25
25
|
@connection_pool = PostgresStore.build_connection_pool(connection_options, pool_options)
|
26
|
-
@dirty =
|
26
|
+
@dirty = Concurrent::AtomicBoolean.new
|
27
|
+
@mutex = Mutex.new
|
27
28
|
end
|
28
29
|
|
29
30
|
def set(key, value)
|
30
31
|
ensure_hash value
|
32
|
+
|
31
33
|
result =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
_instrument 'upsert_entry' do
|
35
|
+
@connection_pool.with do |conn|
|
36
|
+
conn.exec_prepared('upsert_entry', [
|
37
|
+
key,
|
38
|
+
value.to_json,
|
39
|
+
quote_array(extract_links(value))
|
40
|
+
])
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
previous_value =
|
@@ -45,16 +49,23 @@ module WCC::Contentful::Store
|
|
45
49
|
JSON.parse(val) if val
|
46
50
|
end
|
47
51
|
|
48
|
-
if views_need_update?(value, previous_value)
|
49
|
-
|
50
|
-
|
52
|
+
if views_need_update?(value, previous_value)
|
53
|
+
# Mark the views as needing to be refreshed, they will be refreshed on the next query.
|
54
|
+
was_dirty = @dirty.make_true
|
55
|
+
# Send out an instrumentation event if we are the thread that marked it dirty
|
56
|
+
# (make_true returns true if the value changed)
|
57
|
+
_instrument 'mark_dirty' if was_dirty
|
51
58
|
end
|
52
59
|
|
53
60
|
previous_value
|
54
61
|
end
|
55
62
|
|
56
63
|
def keys
|
57
|
-
result =
|
64
|
+
result =
|
65
|
+
_instrument 'select_ids' do
|
66
|
+
@connection_pool.with { |conn| conn.exec_prepared('select_ids') }
|
67
|
+
end
|
68
|
+
|
58
69
|
arr = []
|
59
70
|
result.each { |r| arr << r['id'].strip }
|
60
71
|
arr
|
@@ -63,14 +74,21 @@ module WCC::Contentful::Store
|
|
63
74
|
end
|
64
75
|
|
65
76
|
def delete(key)
|
66
|
-
result =
|
77
|
+
result =
|
78
|
+
_instrument 'delete_by_id', key: key do
|
79
|
+
@connection_pool.with { |conn| conn.exec_prepared('delete_by_id', [key]) }
|
80
|
+
end
|
81
|
+
|
67
82
|
return if result.num_tuples == 0
|
68
83
|
|
69
84
|
JSON.parse(result.getvalue(0, 1))
|
70
85
|
end
|
71
86
|
|
72
87
|
def find(key, **_options)
|
73
|
-
result =
|
88
|
+
result =
|
89
|
+
_instrument 'select_entry', key: key do
|
90
|
+
@connection_pool.with { |conn| conn.exec_prepared('select_entry', [key]) }
|
91
|
+
end
|
74
92
|
return if result.num_tuples == 0
|
75
93
|
|
76
94
|
JSON.parse(result.getvalue(0, 1))
|
@@ -87,22 +105,21 @@ module WCC::Contentful::Store
|
|
87
105
|
end
|
88
106
|
|
89
107
|
def exec_query(statement, params = [])
|
90
|
-
if
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
@connection_pool.with { |conn| conn.exec_prepared('refresh_views_concurrently') }
|
108
|
+
if @dirty.true?
|
109
|
+
# Only one thread should call refresh_views_concurrently but all should wait for it to finish.
|
110
|
+
@mutex.synchronize do
|
111
|
+
# We have to check again b/c another thread may have gotten here first
|
112
|
+
if @dirty.true?
|
113
|
+
_instrument 'refresh_views' do
|
114
|
+
@connection_pool.with { |conn| conn.exec_prepared('refresh_views_concurrently') }
|
115
|
+
end
|
116
|
+
# Mark that the views have been refreshed.
|
117
|
+
@dirty.make_false
|
101
118
|
end
|
102
119
|
end
|
103
120
|
end
|
104
121
|
|
105
|
-
logger&.debug("[PostgresStore] #{statement}
|
122
|
+
logger&.debug("[PostgresStore] #{statement} #{params.inspect}")
|
106
123
|
_instrument 'exec' do
|
107
124
|
@connection_pool.with { |conn| conn.exec(statement, params) }
|
108
125
|
end
|
@@ -76,21 +76,23 @@ module WCC::Contentful
|
|
76
76
|
|
77
77
|
@mutex.synchronize do
|
78
78
|
@state ||= read_state || token_wrapper_factory(nil)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
79
|
+
sync_token = @state['token']
|
80
|
+
|
81
|
+
next_sync_token =
|
82
|
+
client.sync(sync_token: sync_token) do |item|
|
83
|
+
id = item.dig('sys', 'id')
|
84
|
+
id_found ||= id == up_to_id
|
85
|
+
|
86
|
+
store.index(item) if store&.index?
|
87
|
+
event = WCC::Contentful::Event.from_raw(item, source: self)
|
88
|
+
yield(event) if block_given?
|
89
|
+
emit_event(event)
|
90
|
+
|
91
|
+
# Only keep the "sys" not the content in case we have a large space
|
92
|
+
all_events << WCC::Contentful::Event.from_raw(item.slice('sys'), source: self)
|
93
|
+
end
|
92
94
|
|
93
|
-
@state = @state.merge('token' =>
|
95
|
+
@state = @state.merge('token' => next_sync_token)
|
94
96
|
write_state
|
95
97
|
end
|
96
98
|
|
@@ -136,13 +138,19 @@ module WCC::Contentful
|
|
136
138
|
# This job uses the Contentful Sync API to update the configured store with
|
137
139
|
# the latest data from Contentful.
|
138
140
|
class Job < ActiveJob::Base
|
139
|
-
include WCC::Contentful::ServiceAccessors
|
140
|
-
|
141
141
|
self.queue_adapter = :async
|
142
142
|
queue_as :default
|
143
143
|
|
144
|
+
def configuration
|
145
|
+
@configuration ||= WCC::Contentful.configuration
|
146
|
+
end
|
147
|
+
|
148
|
+
def services
|
149
|
+
@services ||= WCC::Contentful::Services.instance
|
150
|
+
end
|
151
|
+
|
144
152
|
def perform(event = nil)
|
145
|
-
return unless sync_engine&.should_sync?
|
153
|
+
return unless services.sync_engine&.should_sync?
|
146
154
|
|
147
155
|
up_to_id = nil
|
148
156
|
retry_count = 0
|
@@ -162,9 +170,9 @@ module WCC::Contentful
|
|
162
170
|
# the sync again after a few minutes.
|
163
171
|
#
|
164
172
|
def sync!(up_to_id: nil, retry_count: 0)
|
165
|
-
id_found, count = sync_engine.next(up_to_id: up_to_id)
|
173
|
+
id_found, count = services.sync_engine.next(up_to_id: up_to_id)
|
166
174
|
|
167
|
-
next_sync_token = sync_engine.state['token']
|
175
|
+
next_sync_token = services.sync_engine.state['token']
|
168
176
|
|
169
177
|
logger.info "Synced #{count} entries. Next sync token:\n #{next_sync_token}"
|
170
178
|
unless id_found
|
data/lib/wcc/contentful.rb
CHANGED
@@ -46,9 +46,8 @@ module WCC::Contentful
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def logger
|
49
|
-
|
50
|
-
|
51
|
-
@logger ||= Logger.new($stderr)
|
49
|
+
ActiveSupport::Deprecation.warn('Use WCC::Contentful::Services.instance.logger instead')
|
50
|
+
WCC::Contentful::Services.instance.logger
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
@@ -90,7 +89,7 @@ module WCC::Contentful
|
|
90
89
|
rescue WCC::Contentful::SimpleClient::ApiError => e
|
91
90
|
raise InitializationError, e if configuration.update_schema_file == :always
|
92
91
|
|
93
|
-
|
92
|
+
Services.instance.logger.warn("Unable to download schema from management API - #{e.message}")
|
94
93
|
end
|
95
94
|
end
|
96
95
|
|
@@ -98,7 +97,7 @@ module WCC::Contentful
|
|
98
97
|
begin
|
99
98
|
JSON.parse(File.read(configuration.schema_file))['contentTypes'] if File.exist?(configuration.schema_file)
|
100
99
|
rescue JSON::ParserError
|
101
|
-
|
100
|
+
Services.instance.warn("Schema file invalid, ignoring it: #{configuration.schema_file}")
|
102
101
|
nil
|
103
102
|
end
|
104
103
|
|
@@ -112,7 +111,7 @@ module WCC::Contentful
|
|
112
111
|
content_types = client.content_types(limit: 1000).items if client
|
113
112
|
rescue WCC::Contentful::SimpleClient::ApiError => e
|
114
113
|
# indicates bad credentials
|
115
|
-
|
114
|
+
Services.instance.logger.warn("Unable to load content types from API - #{e.message}")
|
116
115
|
end
|
117
116
|
end
|
118
117
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wcc-contentful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Watermark Dev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -452,7 +452,9 @@ files:
|
|
452
452
|
- lib/wcc/contentful/rspec.rb
|
453
453
|
- lib/wcc/contentful/services.rb
|
454
454
|
- lib/wcc/contentful/simple_client.rb
|
455
|
+
- lib/wcc/contentful/simple_client/cdn.rb
|
455
456
|
- lib/wcc/contentful/simple_client/management.rb
|
457
|
+
- lib/wcc/contentful/simple_client/preview.rb
|
456
458
|
- lib/wcc/contentful/simple_client/response.rb
|
457
459
|
- lib/wcc/contentful/simple_client/typhoeus_adapter.rb
|
458
460
|
- lib/wcc/contentful/store.rb
|
@@ -489,7 +491,7 @@ homepage: https://github.com/watermarkchurch/wcc-contentful/wcc-contentful
|
|
489
491
|
licenses:
|
490
492
|
- MIT
|
491
493
|
metadata:
|
492
|
-
documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.
|
494
|
+
documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.3/wcc-contentful
|
493
495
|
rubygems_mfa_required: 'true'
|
494
496
|
post_install_message:
|
495
497
|
rdoc_options: []
|