wcc-contentful 1.2.0 → 1.3.1

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -1
  3. data/app/controllers/wcc/contentful/webhook_controller.rb +2 -0
  4. data/lib/tasks/download_schema.rake +1 -1
  5. data/lib/wcc/contentful/configuration.rb +37 -18
  6. data/lib/wcc/contentful/downloads_schema.rb +5 -4
  7. data/lib/wcc/contentful/engine.rb +2 -4
  8. data/lib/wcc/contentful/event.rb +2 -3
  9. data/lib/wcc/contentful/exceptions.rb +2 -3
  10. data/lib/wcc/contentful/indexed_representation.rb +2 -6
  11. data/lib/wcc/contentful/instrumentation.rb +2 -1
  12. data/lib/wcc/contentful/link.rb +1 -3
  13. data/lib/wcc/contentful/link_visitor.rb +2 -4
  14. data/lib/wcc/contentful/middleware/store/caching_middleware.rb +4 -8
  15. data/lib/wcc/contentful/middleware/store.rb +4 -6
  16. data/lib/wcc/contentful/model_api.rb +3 -5
  17. data/lib/wcc/contentful/model_builder.rb +5 -4
  18. data/lib/wcc/contentful/model_methods.rb +10 -12
  19. data/lib/wcc/contentful/model_singleton_methods.rb +1 -1
  20. data/lib/wcc/contentful/rich_text/node.rb +1 -3
  21. data/lib/wcc/contentful/rich_text.rb +1 -1
  22. data/lib/wcc/contentful/rspec.rb +1 -3
  23. data/lib/wcc/contentful/services.rb +9 -0
  24. data/lib/wcc/contentful/simple_client/cdn.rb +126 -0
  25. data/lib/wcc/contentful/simple_client/preview.rb +17 -0
  26. data/lib/wcc/contentful/simple_client/response.rb +24 -19
  27. data/lib/wcc/contentful/simple_client.rb +13 -118
  28. data/lib/wcc/contentful/store/base.rb +19 -27
  29. data/lib/wcc/contentful/store/cdn_adapter.rb +1 -1
  30. data/lib/wcc/contentful/store/factory.rb +1 -1
  31. data/lib/wcc/contentful/store/memory_store.rb +10 -7
  32. data/lib/wcc/contentful/store/postgres_store.rb +52 -42
  33. data/lib/wcc/contentful/store/query.rb +2 -2
  34. data/lib/wcc/contentful/store/rspec_examples/include_param.rb +6 -4
  35. data/lib/wcc/contentful/store/rspec_examples/operators.rb +1 -1
  36. data/lib/wcc/contentful/sync_engine.rb +58 -34
  37. data/lib/wcc/contentful/sys.rb +2 -1
  38. data/lib/wcc/contentful/test/double.rb +3 -5
  39. data/lib/wcc/contentful/test/factory.rb +4 -6
  40. data/lib/wcc/contentful/version.rb +1 -1
  41. data/lib/wcc/contentful.rb +9 -12
  42. data/wcc-contentful.gemspec +5 -5
  43. metadata +37 -30
@@ -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 = false
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
- @connection_pool.with do |conn|
33
- conn.exec_prepared('upsert_entry', [
34
- key,
35
- value.to_json,
36
- quote_array(extract_links(value))
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 =
@@ -46,18 +50,22 @@ module WCC::Contentful::Store
46
50
  end
47
51
 
48
52
  if views_need_update?(value, previous_value)
49
- # mark dirty - we need to refresh the materialized view
50
- unless mutex.with_read_lock { @dirty }
51
- _instrument 'mark_dirty'
52
- mutex.with_write_lock { @dirty = true }
53
- end
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
54
58
  end
55
59
 
56
60
  previous_value
57
61
  end
58
62
 
59
63
  def keys
60
- result = @connection_pool.with { |conn| conn.exec_prepared('select_ids') }
64
+ result =
65
+ _instrument 'select_ids' do
66
+ @connection_pool.with { |conn| conn.exec_prepared('select_ids') }
67
+ end
68
+
61
69
  arr = []
62
70
  result.each { |r| arr << r['id'].strip }
63
71
  arr
@@ -66,14 +74,21 @@ module WCC::Contentful::Store
66
74
  end
67
75
 
68
76
  def delete(key)
69
- result = @connection_pool.with { |conn| conn.exec_prepared('delete_by_id', [key]) }
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
+
70
82
  return if result.num_tuples == 0
71
83
 
72
84
  JSON.parse(result.getvalue(0, 1))
73
85
  end
74
86
 
75
87
  def find(key, **_options)
76
- result = @connection_pool.with { |conn| conn.exec_prepared('select_entry', [key]) }
88
+ result =
89
+ _instrument 'select_entry', key: key do
90
+ @connection_pool.with { |conn| conn.exec_prepared('select_entry', [key]) }
91
+ end
77
92
  return if result.num_tuples == 0
78
93
 
79
94
  JSON.parse(result.getvalue(0, 1))
@@ -90,22 +105,21 @@ module WCC::Contentful::Store
90
105
  end
91
106
 
92
107
  def exec_query(statement, params = [])
93
- if mutex.with_read_lock { @dirty }
94
- was_dirty =
95
- mutex.with_write_lock do
96
- was_dirty = @dirty
97
- @dirty = false
98
- was_dirty
99
- end
100
-
101
- if was_dirty
102
- _instrument 'refresh_views' do
103
- @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
104
118
  end
105
119
  end
106
120
  end
107
121
 
108
- logger&.debug('[PostgresStore] ' + statement + "\n" + params.inspect)
122
+ logger&.debug("[PostgresStore] #{statement} #{params.inspect}")
109
123
  _instrument 'exec' do
110
124
  @connection_pool.with { |conn| conn.exec(statement, params) }
111
125
  end
@@ -231,16 +245,12 @@ module WCC::Contentful::Store
231
245
  end
232
246
 
233
247
  statement =
234
- select_statement +
235
- " FROM #{table} AS t \n" +
236
- joins.join("\n") + "\n" +
237
- statement +
238
- (limit_statement || '')
248
+ "#{select_statement} FROM #{table} AS t \n#{joins.join("\n")}\n#{statement}#{limit_statement || ''}"
239
249
 
240
250
  [statement, params]
241
251
  end
242
252
 
243
- def _eq(path, expected, params)
253
+ def _eq(path, expected, params) # rubocop:disable Layout/LineContinuationLeadingSpace
244
254
  return " AND t.id = $#{push_param(expected, params)}" if path == %w[sys id]
245
255
 
246
256
  if path[3] == 'sys'
@@ -248,12 +258,12 @@ module WCC::Contentful::Store
248
258
  # into it to detect whether it contains `{ "sys": { "id" => expected } }`
249
259
  expected = { 'sys' => { path[4] => expected } }.to_json
250
260
  return ' AND fn_contentful_jsonb_any_to_jsonb_array(t.data->' \
251
- "#{quote_parameter_path(path.take(3))}) @> " \
252
- "jsonb_build_array($#{push_param(expected, params)}::jsonb)"
261
+ "#{quote_parameter_path(path.take(3))}) @> " \
262
+ "jsonb_build_array($#{push_param(expected, params)}::jsonb)"
253
263
  end
254
264
 
255
- " AND t.data->#{quote_parameter_path(path)}" \
256
- " @> to_jsonb($#{push_param(expected, params)})"
265
+ " AND t.data->#{quote_parameter_path(path)} " \
266
+ "@> to_jsonb($#{push_param(expected, params)})"
257
267
  end
258
268
 
259
269
  PARAM_TYPES = {
@@ -292,7 +302,7 @@ module WCC::Contentful::Store
292
302
  def push_join(_path, joins)
293
303
  table_alias = "s#{joins.length}"
294
304
  joins << "JOIN contentful_raw AS #{table_alias} ON " \
295
- "#{table_alias}.id=ANY(t.links)"
305
+ "#{table_alias}.id=ANY(t.links)"
296
306
  table_alias
297
307
  end
298
308
  end
@@ -326,8 +336,8 @@ module WCC::Contentful::Store
326
336
  end
327
337
 
328
338
  def schema_ensured?(conn)
329
- result = conn.exec('SELECT version FROM wcc_contentful_schema_version' \
330
- ' ORDER BY version DESC LIMIT 1')
339
+ result = conn.exec('SELECT version FROM wcc_contentful_schema_version ' \
340
+ 'ORDER BY version DESC LIMIT 1')
331
341
  return false if result.num_tuples == 0
332
342
 
333
343
  result[0]['version'].to_i >= EXPECTED_VERSION
@@ -339,8 +349,8 @@ module WCC::Contentful::Store
339
349
  def ensure_schema(conn)
340
350
  result =
341
351
  begin
342
- conn.exec('SELECT version FROM wcc_contentful_schema_version' \
343
- ' ORDER BY version DESC')
352
+ conn.exec('SELECT version FROM wcc_contentful_schema_version ' \
353
+ 'ORDER BY version DESC')
344
354
  rescue PG::UndefinedTable
345
355
  []
346
356
  end
@@ -36,7 +36,7 @@ module WCC::Contentful::Store
36
36
 
37
37
  FALSE_VALUES = [
38
38
  false, 0,
39
- '0', :"0",
39
+ '0', :'0',
40
40
  'f', :f,
41
41
  'F', :F,
42
42
  'false', :false, # rubocop:disable Lint/BooleanSymbol
@@ -237,7 +237,7 @@ module WCC::Contentful::Store
237
237
 
238
238
  Condition =
239
239
  Struct.new(:path, :op, :expected) do
240
- LINK_KEYS = %w[id type linkType].freeze
240
+ LINK_KEYS = %w[id type linkType].freeze # rubocop:disable Lint/ConstantDefinitionInBlock
241
241
 
242
242
  def path_tuples
243
243
  @path_tuples ||=
@@ -15,10 +15,12 @@ RSpec.shared_examples 'supports include param' do |feature_set|
15
15
  'fields' => {
16
16
  'name' => { 'en-US' => 'root' },
17
17
  'link' => { 'en-US' => make_link_to('deep1') },
18
- 'links' => { 'en-US' => [
19
- make_link_to('shallow3'),
20
- make_link_to('deep2')
21
- ] }
18
+ 'links' => {
19
+ 'en-US' => [
20
+ make_link_to('shallow3'),
21
+ make_link_to('deep2')
22
+ ]
23
+ }
22
24
  }
23
25
  }
24
26
  end
@@ -40,7 +40,7 @@ RSpec.shared_examples 'operators' do |feature_set|
40
40
  end
41
41
  end
42
42
 
43
- supported_operators.each do |op, value|
43
+ supported_operators.each do |op, value| # rubocop:disable Style/CombinableLoops
44
44
  next unless value
45
45
 
46
46
  it_behaves_like "supports :#{op} operator" do
@@ -31,15 +31,18 @@ module WCC::Contentful
31
31
  (@state&.dup || token_wrapper_factory(nil)).freeze
32
32
  end
33
33
 
34
- attr_reader :store
35
- attr_reader :client
34
+ attr_reader :store, :client, :options
36
35
 
37
36
  def should_sync?
38
37
  store&.index?
39
38
  end
40
39
 
41
- def initialize(state: nil, store: nil, client: nil, key: nil)
42
- @state_key = key || "sync:#{object_id}"
40
+ def initialize(client: nil, store: nil, state: nil, **options)
41
+ @options = {
42
+ key: "sync:#{object_id}"
43
+ }.merge!(options).freeze
44
+
45
+ @state_key = @options[:key] || "sync:#{object_id}"
43
46
  @client = client || WCC::Contentful::Services.instance.client
44
47
  @mutex = Mutex.new
45
48
 
@@ -53,9 +56,7 @@ module WCC::Contentful
53
56
  if state
54
57
  @state = token_wrapper_factory(state)
55
58
  raise ArgumentError, ':state param must be a String or Hash' unless @state.is_a? Hash
56
- unless @state.dig('sys', 'type') == 'token'
57
- raise ArgumentError, ':state param must be of sys.type = "token"'
58
- end
59
+ raise ArgumentError, ':state param must be of sys.type = "token"' unless @state.dig('sys', 'type') == 'token'
59
60
  end
60
61
  raise ArgumentError, 'either :state or :store must be provided' unless @state || @store
61
62
  end
@@ -75,21 +76,23 @@ module WCC::Contentful
75
76
 
76
77
  @mutex.synchronize do
77
78
  @state ||= read_state || token_wrapper_factory(nil)
78
- next_sync_token = @state['token']
79
-
80
- sync_resp = client.sync(sync_token: next_sync_token)
81
- sync_resp.items.each do |item|
82
- id = item.dig('sys', 'id')
83
- id_found ||= id == up_to_id
84
-
85
- store.index(item) if store&.index?
86
- event = WCC::Contentful::Event.from_raw(item, source: self)
87
- yield(event) if block_given?
88
- emit_event(event)
89
- all_events << event
90
- end
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
91
94
 
92
- @state = @state.merge('token' => sync_resp.next_sync_token)
95
+ @state = @state.merge('token' => next_sync_token)
93
96
  write_state
94
97
  end
95
98
 
@@ -135,17 +138,27 @@ module WCC::Contentful
135
138
  # This job uses the Contentful Sync API to update the configured store with
136
139
  # the latest data from Contentful.
137
140
  class Job < ActiveJob::Base
138
- include WCC::Contentful::ServiceAccessors
139
-
140
141
  self.queue_adapter = :async
141
142
  queue_as :default
142
143
 
144
+ def configuration
145
+ @configuration ||= WCC::Contentful.configuration
146
+ end
147
+
148
+ def services
149
+ @services ||= WCC::Contentful::Services.instance
150
+ end
151
+
143
152
  def perform(event = nil)
144
- return unless sync_engine&.should_sync?
153
+ return unless services.sync_engine&.should_sync?
145
154
 
146
155
  up_to_id = nil
147
- up_to_id = event[:up_to_id] || event.dig('sys', 'id') if event
148
- sync!(up_to_id: up_to_id)
156
+ retry_count = 0
157
+ if event
158
+ up_to_id = event[:up_to_id] || event.dig('sys', 'id')
159
+ retry_count = event[:retry_count] if event[:retry_count]
160
+ end
161
+ sync!(up_to_id: up_to_id, retry_count: retry_count)
149
162
  end
150
163
 
151
164
  # Calls the Contentful Sync API and updates the configured store with the returned
@@ -156,21 +169,32 @@ module WCC::Contentful
156
169
  # If we don't find this ID in the sync data, then drop a job to try
157
170
  # the sync again after a few minutes.
158
171
  #
159
- def sync!(up_to_id: nil)
160
- id_found, count = sync_engine.next(up_to_id: up_to_id)
172
+ def sync!(up_to_id: nil, retry_count: 0)
173
+ id_found, count = services.sync_engine.next(up_to_id: up_to_id)
161
174
 
162
- next_sync_token = sync_engine.state['token']
175
+ next_sync_token = services.sync_engine.state['token']
163
176
 
164
177
  logger.info "Synced #{count} entries. Next sync token:\n #{next_sync_token}"
165
- logger.info "Should enqueue again? [#{!id_found}]"
166
- # Passing nil to only enqueue the job 1 more time
167
- sync_later!(up_to_id: nil) unless id_found
178
+ unless id_found
179
+ if retry_count >= configuration.sync_retry_limit
180
+ logger.error "Unable to find item with id '#{up_to_id}' on the Sync API. " \
181
+ "Abandoning after #{retry_count} retries."
182
+ else
183
+ wait = (2**retry_count) * configuration.sync_retry_wait.seconds
184
+ logger.info "Unable to find item with id '#{up_to_id}' on the Sync API. " \
185
+ "Retrying after #{wait.inspect} " \
186
+ "(#{configuration.sync_retry_limit - retry_count} retries remaining)"
187
+
188
+ self.class.set(wait: wait)
189
+ .perform_later(up_to_id: up_to_id, retry_count: retry_count + 1)
190
+ end
191
+ end
168
192
  next_sync_token
169
193
  end
170
194
 
171
- # Drops an ActiveJob job to invoke WCC::Contentful.sync! after a given amount
195
+ # Enqueues an ActiveJob job to invoke WCC::Contentful.sync! after a given amount
172
196
  # of time.
173
- def sync_later!(up_to_id: nil, wait: 10.minutes)
197
+ def sync_later!(up_to_id: nil, wait: 10.seconds)
174
198
  self.class.set(wait: wait)
175
199
  .perform_later(up_to_id: up_to_id)
176
200
  end
@@ -11,7 +11,7 @@ WCC::Contentful::Sys =
11
11
  :revision,
12
12
  :context
13
13
  ) do
14
-
14
+ # rubocop:disable Lint/ConstantDefinitionInBlock
15
15
  ATTRIBUTES = %i[
16
16
  id
17
17
  type
@@ -22,6 +22,7 @@ WCC::Contentful::Sys =
22
22
  revision
23
23
  context
24
24
  ].freeze
25
+ # rubocop:enable Lint/ConstantDefinitionInBlock
25
26
 
26
27
  undef []=
27
28
  ATTRIBUTES.each { |a| __send__(:undef_method, "#{a}=") }
@@ -8,9 +8,7 @@ module WCC::Contentful::Test::Double
8
8
  # All attributes that are known to be required fields on the content type
9
9
  # will return a default value based on the field type.
10
10
  def contentful_double(const, **attrs)
11
- unless const.respond_to?(:content_type_definition)
12
- const = WCC::Contentful::Model.resolve_constant(const.to_s)
13
- end
11
+ const = WCC::Contentful::Model.resolve_constant(const.to_s) unless const.respond_to?(:content_type_definition)
14
12
  attrs.symbolize_keys!
15
13
 
16
14
  bad_attrs = attrs.reject { |a| const.instance_methods.include?(a) }
@@ -45,7 +43,7 @@ module WCC::Contentful::Test::Double
45
43
  sys: {
46
44
  type: 'Link',
47
45
  linkType: 'Space',
48
- id: ENV['CONTENTFUL_SPACE_ID']
46
+ id: ENV.fetch('CONTENTFUL_SPACE_ID', nil)
49
47
  }
50
48
  },
51
49
  id: SecureRandom.urlsafe_base64,
@@ -62,7 +60,7 @@ module WCC::Contentful::Test::Double
62
60
  revision: rand(100),
63
61
  locale: 'en-US'
64
62
  },
65
- fields: attrs.each_with_object({}) { |(k, v), h| h[k] = { 'en-US' => v } }
63
+ fields: attrs.transform_values { |v| { 'en-US' => v } }
66
64
  }
67
65
 
68
66
  double(attrs)
@@ -8,9 +8,7 @@ module WCC::Contentful::Test::Factory
8
8
  # All attributes that are known to be required fields on the content type
9
9
  # will return a default value based on the field type.
10
10
  def contentful_create(const, context = nil, **attrs)
11
- unless const.respond_to?(:content_type_definition)
12
- const = WCC::Contentful::Model.resolve_constant(const.to_s)
13
- end
11
+ const = WCC::Contentful::Model.resolve_constant(const.to_s) unless const.respond_to?(:content_type_definition)
14
12
  attrs = attrs.transform_keys { |a| a.to_s.camelize(:lower) }
15
13
 
16
14
  id = attrs.delete('id')
@@ -59,7 +57,7 @@ module WCC::Contentful::Test::Factory
59
57
  sys: {
60
58
  type: 'Link',
61
59
  linkType: 'Space',
62
- id: ENV['CONTENTFUL_SPACE_ID']
60
+ id: ENV.fetch('CONTENTFUL_SPACE_ID', nil)
63
61
  }
64
62
  },
65
63
  id: id || SecureRandom.urlsafe_base64,
@@ -86,8 +84,8 @@ module WCC::Contentful::Test::Factory
86
84
  end
87
85
 
88
86
  def contentful_fields(model)
89
- WCC::Contentful::Test::Attributes.defaults(model).each_with_object({}) do |(k, v), h|
90
- h[k] = { 'en-US' => v }
87
+ WCC::Contentful::Test::Attributes.defaults(model).transform_values do |v|
88
+ { 'en-US' => v }
91
89
  end
92
90
  end
93
91
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Contentful
5
- VERSION = '1.2.0'
5
+ VERSION = '1.3.1'
6
6
  end
7
7
  end
@@ -46,9 +46,8 @@ module WCC::Contentful
46
46
  end
47
47
 
48
48
  def logger
49
- return Rails.logger if defined?(Rails)
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
 
@@ -82,7 +81,7 @@ module WCC::Contentful
82
81
 
83
82
  if configuration.update_schema_file == :always ||
84
83
  (configuration.update_schema_file == :if_possible && Services.instance.management_client) ||
85
- configuration.update_schema_file == :if_missing && !File.exist?(configuration.schema_file)
84
+ (configuration.update_schema_file == :if_missing && !File.exist?(configuration.schema_file))
86
85
 
87
86
  begin
88
87
  downloader = WCC::Contentful::DownloadsSchema.new
@@ -90,17 +89,15 @@ 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
- WCC::Contentful.logger.warn("Unable to download schema from management API - #{e.message}")
92
+ Services.instance.logger.warn("Unable to download schema from management API - #{e.message}")
94
93
  end
95
94
  end
96
95
 
97
96
  content_types =
98
97
  begin
99
- if File.exist?(configuration.schema_file)
100
- JSON.parse(File.read(configuration.schema_file))['contentTypes']
101
- end
98
+ JSON.parse(File.read(configuration.schema_file))['contentTypes'] if File.exist?(configuration.schema_file)
102
99
  rescue JSON::ParserError
103
- WCC::Contentful.logger.warn("Schema file invalid, ignoring it: #{configuration.schema_file}")
100
+ Services.instance.warn("Schema file invalid, ignoring it: #{configuration.schema_file}")
104
101
  nil
105
102
  end
106
103
 
@@ -114,13 +111,13 @@ module WCC::Contentful
114
111
  content_types = client.content_types(limit: 1000).items if client
115
112
  rescue WCC::Contentful::SimpleClient::ApiError => e
116
113
  # indicates bad credentials
117
- WCC::Contentful.logger.warn("Unable to load content types from API - #{e.message}")
114
+ Services.instance.logger.warn("Unable to load content types from API - #{e.message}")
118
115
  end
119
116
  end
120
117
 
121
118
  unless content_types
122
- raise InitializationError, 'Unable to load content types from schema file or API!' \
123
- ' Check your access token and space ID.'
119
+ raise InitializationError, 'Unable to load content types from schema file or API! ' \
120
+ 'Check your access token and space ID.'
124
121
  end
125
122
 
126
123
  # Set the schema on the default WCC::Contentful::Model
@@ -6,7 +6,7 @@ require 'wcc/contentful/version'
6
6
 
7
7
  doc_version = Gem::Version.new(WCC::Contentful::VERSION).release.to_s.sub(/\.\d+$/, '')
8
8
 
9
- # rubocop:disable Metrics/LineLength
9
+ # rubocop:disable Layout/LineLength
10
10
  Gem::Specification.new do |spec|
11
11
  spec.name = 'wcc-contentful'
12
12
  spec.version = WCC::Contentful::VERSION
@@ -19,10 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.license = 'MIT'
20
20
 
21
21
  spec.metadata = {
22
- 'documentation_uri' => "https://watermarkchurch.github.io/wcc-contentful/#{doc_version}/wcc-contentful"
22
+ 'documentation_uri' => "https://watermarkchurch.github.io/wcc-contentful/#{doc_version}/wcc-contentful",
23
+ 'rubygems_mfa_required' => 'true'
23
24
  }
24
25
 
25
- spec.required_ruby_version = '>= 2.3'
26
+ spec.required_ruby_version = '>= 2.7'
26
27
 
27
28
  spec.files = Dir['app/**/*', 'config/**/*', 'lib/**/*'] +
28
29
  %w[Rakefile README.md wcc-contentful.gemspec]
@@ -38,7 +39,6 @@ Gem::Specification.new do |spec|
38
39
  spec.add_development_dependency 'rspec', '~> 3.0'
39
40
  spec.add_development_dependency 'rspec-instrumentation-matcher'
40
41
  spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
41
- spec.add_development_dependency 'rubocop', '0.68'
42
42
  spec.add_development_dependency 'simplecov', '~> 0.16.1'
43
43
  spec.add_development_dependency 'vcr', '~> 5.0'
44
44
  spec.add_development_dependency 'webmock', '~> 3.0'
@@ -68,4 +68,4 @@ Gem::Specification.new do |spec|
68
68
  spec.add_dependency 'wcc-base', '~> 0.3.1'
69
69
  spec.add_dependency 'wisper', '~> 2.0.0'
70
70
  end
71
- # rubocop:enable Metrics/LineLength
71
+ # rubocop:enable Layout/LineLength