wcc-contentful 1.1.1 → 1.2.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.
- checksums.yaml +4 -4
- data/app/controllers/wcc/contentful/webhook_controller.rb +2 -0
- data/app/jobs/wcc/contentful/webhook_enable_job.rb +1 -1
- data/lib/tasks/download_schema.rake +1 -1
- data/lib/wcc/contentful/active_record_shim.rb +2 -2
- data/lib/wcc/contentful/configuration.rb +31 -18
- data/lib/wcc/contentful/content_type_indexer.rb +2 -0
- data/lib/wcc/contentful/downloads_schema.rb +5 -4
- data/lib/wcc/contentful/engine.rb +2 -4
- data/lib/wcc/contentful/event.rb +4 -11
- data/lib/wcc/contentful/exceptions.rb +2 -3
- data/lib/wcc/contentful/indexed_representation.rb +3 -6
- data/lib/wcc/contentful/instrumentation.rb +2 -1
- data/lib/wcc/contentful/link.rb +1 -3
- data/lib/wcc/contentful/link_visitor.rb +2 -4
- data/lib/wcc/contentful/middleware/store/caching_middleware.rb +5 -9
- data/lib/wcc/contentful/middleware/store.rb +4 -6
- data/lib/wcc/contentful/model.rb +0 -6
- data/lib/wcc/contentful/model_api.rb +17 -12
- data/lib/wcc/contentful/model_builder.rb +8 -4
- data/lib/wcc/contentful/model_methods.rb +10 -12
- data/lib/wcc/contentful/model_singleton_methods.rb +4 -4
- data/lib/wcc/contentful/rich_text/node.rb +60 -0
- data/lib/wcc/contentful/rich_text.rb +105 -0
- data/lib/wcc/contentful/rspec.rb +1 -3
- data/lib/wcc/contentful/simple_client/response.rb +28 -34
- data/lib/wcc/contentful/simple_client.rb +11 -14
- data/lib/wcc/contentful/store/base.rb +5 -5
- data/lib/wcc/contentful/store/cdn_adapter.rb +11 -7
- data/lib/wcc/contentful/store/factory.rb +1 -1
- data/lib/wcc/contentful/store/memory_store.rb +2 -2
- data/lib/wcc/contentful/store/postgres_store.rb +15 -22
- data/lib/wcc/contentful/store/query.rb +2 -2
- data/lib/wcc/contentful/store/rspec_examples/include_param.rb +6 -4
- data/lib/wcc/contentful/store/rspec_examples/operators.rb +1 -1
- data/lib/wcc/contentful/sync_engine.rb +31 -15
- data/lib/wcc/contentful/sys.rb +2 -1
- data/lib/wcc/contentful/test/double.rb +3 -5
- data/lib/wcc/contentful/test/factory.rb +4 -6
- data/lib/wcc/contentful/version.rb +1 -1
- data/lib/wcc/contentful.rb +6 -8
- data/wcc-contentful.gemspec +6 -6
- metadata +13 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 476f7c65f705c14e012e8c2cbb36f68e29782607377b0a4aa9d0c10fb93458da
|
4
|
+
data.tar.gz: 01aeff71919acef51c8d498c63bc42e822ed3c598e7c636f5faf74baeb74c552
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0383a5fe8ef8b042d0a9aca04c964051fc5201b89e702a80b616e5c3b36fd6fa7590d768d178e96f3353c998923fc87458697f5b1112bdd000e6e273fdb07f56'
|
7
|
+
data.tar.gz: 776a6ec463edbcd209d28dd2033d6378ac45e44dd9bca790aefe81ea529c4fa4d0cf6725bd3f67e5c4054f41bd930e9905165f8ccd42f2a9f01b3bc0ac1deaf6
|
@@ -36,6 +36,7 @@ module WCC::Contentful
|
|
36
36
|
def authorize_contentful
|
37
37
|
config = WCC::Contentful.configuration
|
38
38
|
|
39
|
+
# rubocop:disable Style/SoleNestedConditional
|
39
40
|
if config.webhook_username.present? && config.webhook_password.present?
|
40
41
|
unless authenticate_with_http_basic do |u, p|
|
41
42
|
u == config.webhook_username &&
|
@@ -45,6 +46,7 @@ module WCC::Contentful
|
|
45
46
|
return
|
46
47
|
end
|
47
48
|
end
|
49
|
+
# rubocop:enable Style/SoleNestedConditional
|
48
50
|
|
49
51
|
# 'application/vnd.contentful.management.v1+json' is an alias for the 'application/json'
|
50
52
|
# content-type, so 'request.content_type' will give 'application/json'
|
@@ -5,7 +5,7 @@ require 'wcc/contentful/downloads_schema'
|
|
5
5
|
|
6
6
|
namespace :wcc_contentful do
|
7
7
|
desc 'Downloads the schema from the currently configured space and stores it in ' \
|
8
|
-
|
8
|
+
'db/contentful-schema.json'
|
9
9
|
task download_schema: :environment do
|
10
10
|
WCC::Contentful::DownloadsSchema.call
|
11
11
|
end
|
@@ -64,13 +64,13 @@ module WCC::Contentful::ActiveRecordShim
|
|
64
64
|
}
|
65
65
|
}
|
66
66
|
|
67
|
-
find_all(filter).each_slice(batch_size, &block)
|
67
|
+
find_all(**filter).each_slice(batch_size, &block)
|
68
68
|
end
|
69
69
|
|
70
70
|
def where(**conditions)
|
71
71
|
# TODO: return a Query object that implements more of the ActiveRecord query interface
|
72
72
|
# https://guides.rubyonrails.org/active_record_querying.html#conditions
|
73
|
-
find_all(conditions)
|
73
|
+
find_all(**conditions)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -3,22 +3,24 @@
|
|
3
3
|
# This object contains all the configuration options for the `wcc-contentful` gem.
|
4
4
|
class WCC::Contentful::Configuration
|
5
5
|
ATTRIBUTES = %i[
|
6
|
-
space
|
7
6
|
access_token
|
8
7
|
app_url
|
9
|
-
management_token
|
10
|
-
environment
|
11
|
-
default_locale
|
12
|
-
preview_token
|
13
|
-
webhook_username
|
14
|
-
webhook_password
|
15
|
-
webhook_jobs
|
16
8
|
connection
|
17
9
|
connection_options
|
18
|
-
|
10
|
+
default_locale
|
11
|
+
environment
|
12
|
+
instrumentation_adapter
|
13
|
+
management_token
|
14
|
+
preview_token
|
19
15
|
schema_file
|
16
|
+
space
|
20
17
|
store
|
21
|
-
|
18
|
+
sync_retry_limit
|
19
|
+
sync_retry_wait
|
20
|
+
update_schema_file
|
21
|
+
webhook_jobs
|
22
|
+
webhook_password
|
23
|
+
webhook_username
|
22
24
|
].freeze
|
23
25
|
|
24
26
|
# (required) Sets the Contentful Space ID.
|
@@ -57,6 +59,17 @@ class WCC::Contentful::Configuration
|
|
57
59
|
# to implement a webhook job.
|
58
60
|
attr_accessor :webhook_jobs
|
59
61
|
|
62
|
+
# Sets the maximum number of times that the SyncEngine will retry synchronization
|
63
|
+
# when it detects that the Contentful CDN's cache has not been updated after a webhook.
|
64
|
+
# Default: 2
|
65
|
+
attr_accessor :sync_retry_limit
|
66
|
+
|
67
|
+
# Sets the base ActiveSupport::Duration that the SyncEngine will wait before retrying.
|
68
|
+
# Each subsequent retry uses an exponential backoff, so the second retry will be
|
69
|
+
# after (2 * sync_retry_wait), the third after (4 * sync_retry_wait), etc.
|
70
|
+
# Default: 2.seconds
|
71
|
+
attr_accessor :sync_retry_wait
|
72
|
+
|
60
73
|
# Returns true if the currently configured environment is pointing at `master`.
|
61
74
|
def master?
|
62
75
|
!environment.present?
|
@@ -145,9 +158,7 @@ class WCC::Contentful::Configuration
|
|
145
158
|
# WCC::Contentful::InitializationError if the API cannot be reached.
|
146
159
|
def update_schema_file=(sym)
|
147
160
|
valid_syms = %i[never if_possible if_missing always]
|
148
|
-
unless valid_syms.include?(sym)
|
149
|
-
raise ArgumentError, "update_schema_file must be one of #{valid_syms}"
|
150
|
-
end
|
161
|
+
raise ArgumentError, "update_schema_file must be one of #{valid_syms}" unless valid_syms.include?(sym)
|
151
162
|
|
152
163
|
@update_schema_file = sym
|
153
164
|
end
|
@@ -172,22 +183,24 @@ class WCC::Contentful::Configuration
|
|
172
183
|
attr_accessor :instrumentation_adapter
|
173
184
|
|
174
185
|
def initialize
|
175
|
-
@access_token = ENV
|
176
|
-
@app_url = ENV
|
186
|
+
@access_token = ENV.fetch('CONTENTFUL_ACCESS_TOKEN', nil)
|
187
|
+
@app_url = ENV.fetch('APP_URL', nil)
|
177
188
|
@connection_options = {
|
178
189
|
api_url: 'https://cdn.contentful.com/',
|
179
190
|
preview_api_url: 'https://preview.contentful.com/',
|
180
191
|
management_api_url: 'https://api.contentful.com'
|
181
192
|
}
|
182
|
-
@management_token = ENV
|
183
|
-
@preview_token = ENV
|
184
|
-
@space = ENV
|
193
|
+
@management_token = ENV.fetch('CONTENTFUL_MANAGEMENT_TOKEN', nil)
|
194
|
+
@preview_token = ENV.fetch('CONTENTFUL_PREVIEW_TOKEN', nil)
|
195
|
+
@space = ENV.fetch('CONTENTFUL_SPACE_ID', nil)
|
185
196
|
@default_locale = nil
|
186
197
|
@middleware = []
|
187
198
|
@update_schema_file = :if_possible
|
188
199
|
@schema_file = 'db/contentful-schema.json'
|
189
200
|
@webhook_jobs = []
|
190
201
|
@store_factory = WCC::Contentful::Store::Factory.new(self, :direct)
|
202
|
+
@sync_retry_limit = 3
|
203
|
+
@sync_retry_wait = 1.second
|
191
204
|
end
|
192
205
|
|
193
206
|
# Validates the configuration, raising ArgumentError if anything is wrong. This
|
@@ -36,7 +36,7 @@ class WCC::Contentful::DownloadsSchema
|
|
36
36
|
begin
|
37
37
|
JSON.parse(File.read(@file))
|
38
38
|
rescue JSON::ParserError
|
39
|
-
return true
|
39
|
+
return true # rubocop:disable Lint/NoReturnInBeginEndBlocks
|
40
40
|
end
|
41
41
|
|
42
42
|
existing_cts = contents['contentTypes'].sort_by { |ct| ct.dig('sys', 'id') }
|
@@ -83,13 +83,14 @@ class WCC::Contentful::DownloadsSchema
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def deep_contains_all(expected, actual)
|
86
|
-
|
86
|
+
case expected
|
87
|
+
when Array
|
87
88
|
expected.each_with_index do |val, i|
|
88
89
|
return false unless actual[i]
|
89
90
|
return false unless deep_contains_all(val, actual[i])
|
90
91
|
end
|
91
92
|
true
|
92
|
-
|
93
|
+
when Hash
|
93
94
|
expected.each do |(key, val)|
|
94
95
|
return false unless actual.key?(key)
|
95
96
|
return false unless deep_contains_all(val, actual[key])
|
@@ -107,6 +108,6 @@ class WCC::Contentful::DownloadsSchema
|
|
107
108
|
# only in its treatment of empty arrays in the "validations" field.
|
108
109
|
json_string = json_string.gsub(/\[\n\n\s+\]/, '[]')
|
109
110
|
# contentful-shell also adds a newline at the end.
|
110
|
-
json_string
|
111
|
+
"#{json_string}\n"
|
111
112
|
end
|
112
113
|
end
|
@@ -6,9 +6,7 @@ module WCC::Contentful
|
|
6
6
|
config = WCC::Contentful.configuration
|
7
7
|
|
8
8
|
jobs = []
|
9
|
-
if WCC::Contentful::Services.instance.sync_engine&.should_sync?
|
10
|
-
jobs << WCC::Contentful::SyncEngine::Job
|
11
|
-
end
|
9
|
+
jobs << WCC::Contentful::SyncEngine::Job if WCC::Contentful::Services.instance.sync_engine&.should_sync?
|
12
10
|
jobs.push(*WCC::Contentful.configuration.webhook_jobs)
|
13
11
|
|
14
12
|
jobs.each do |job|
|
@@ -19,7 +17,7 @@ module WCC::Contentful
|
|
19
17
|
job.perform_later(event.to_h)
|
20
18
|
else
|
21
19
|
Rails.logger.error "Misconfigured webhook job: #{job} does not respond to " \
|
22
|
-
|
20
|
+
':perform_later'
|
23
21
|
end
|
24
22
|
rescue StandardError => e
|
25
23
|
warn "Error in job #{job}: #{e}"
|
data/lib/wcc/contentful/event.rb
CHANGED
@@ -7,10 +7,10 @@ module WCC::Contentful::Event
|
|
7
7
|
|
8
8
|
# Creates an Event out of a raw value received by a webhook or given from
|
9
9
|
# the Contentful Sync API.
|
10
|
-
def self.from_raw(raw, context = nil)
|
10
|
+
def self.from_raw(raw, context = nil, source: nil)
|
11
11
|
const = Registry.instance.get(raw.dig('sys', 'type'))
|
12
12
|
|
13
|
-
const.new(raw, context)
|
13
|
+
const.new(raw, context, source: source)
|
14
14
|
end
|
15
15
|
|
16
16
|
class Registry
|
@@ -23,9 +23,7 @@ module WCC::Contentful::Event
|
|
23
23
|
|
24
24
|
def register(constant)
|
25
25
|
name = constant.try(:type) || constant.name.demodulize
|
26
|
-
unless constant.respond_to?(:new)
|
27
|
-
raise ArgumentError, "Constant #{constant} does not define 'new'"
|
28
|
-
end
|
26
|
+
raise ArgumentError, "Constant #{constant} does not define 'new'" unless constant.respond_to?(:new)
|
29
27
|
|
30
28
|
@event_types ||= {}
|
31
29
|
@event_types[name] = constant
|
@@ -58,6 +56,7 @@ module WCC::Contentful::Event
|
|
58
56
|
attr_reader :sys
|
59
57
|
attr_reader :raw
|
60
58
|
attr_reader :source
|
59
|
+
|
61
60
|
delegate :id, to: :sys
|
62
61
|
delegate :type, to: :sys
|
63
62
|
delegate :created_at, to: :sys
|
@@ -134,12 +133,6 @@ class WCC::Contentful::Event::SyncComplete
|
|
134
133
|
include WCC::Contentful::Event
|
135
134
|
|
136
135
|
def initialize(items, context = nil, source: nil)
|
137
|
-
items =
|
138
|
-
items.map do |item|
|
139
|
-
next item if item.is_a? WCC::Contentful::Event
|
140
|
-
|
141
|
-
WCC::Contentful::Event.from_raw(item, context, source: source)
|
142
|
-
end
|
143
136
|
@items = items.freeze
|
144
137
|
@source = source
|
145
138
|
@sys = WCC::Contentful::Sys.new(
|
@@ -12,8 +12,7 @@ module WCC::Contentful
|
|
12
12
|
# Raised when an entry contains a circular reference and cannot be represented
|
13
13
|
# as a flat tree.
|
14
14
|
class CircularReferenceError < StandardError
|
15
|
-
attr_reader :stack
|
16
|
-
attr_reader :id
|
15
|
+
attr_reader :stack, :id
|
17
16
|
|
18
17
|
def initialize(stack, id)
|
19
18
|
@id = id
|
@@ -25,7 +24,7 @@ module WCC::Contentful
|
|
25
24
|
return super unless stack
|
26
25
|
|
27
26
|
super + "\n " \
|
28
|
-
|
27
|
+
"#{stack.last} points to #{id} which is also it's ancestor\n " +
|
29
28
|
stack.join('->')
|
30
29
|
end
|
31
30
|
end
|
@@ -103,14 +103,13 @@ module WCC::Contentful
|
|
103
103
|
Boolean
|
104
104
|
Json
|
105
105
|
Coordinates
|
106
|
+
RichText
|
106
107
|
Link
|
107
108
|
Asset
|
108
109
|
].freeze
|
109
110
|
|
110
111
|
def type=(raw_type)
|
111
|
-
unless TYPES.include?(raw_type)
|
112
|
-
raise ArgumentError, "Unknown type #{raw_type}, expected one of: #{TYPES}"
|
113
|
-
end
|
112
|
+
raise ArgumentError, "Unknown type #{raw_type}, expected one of: #{TYPES}" unless TYPES.include?(raw_type)
|
114
113
|
|
115
114
|
@type = raw_type
|
116
115
|
end
|
@@ -130,9 +129,7 @@ module WCC::Contentful
|
|
130
129
|
|
131
130
|
if raw_type = hash_or_id.delete('type')
|
132
131
|
raw_type = raw_type.to_sym
|
133
|
-
unless TYPES.include?(raw_type)
|
134
|
-
raise ArgumentError, "Unknown type #{raw_type}, expected one of: #{TYPES}"
|
135
|
-
end
|
132
|
+
raise ArgumentError, "Unknown type #{raw_type}, expected one of: #{TYPES}" unless TYPES.include?(raw_type)
|
136
133
|
|
137
134
|
@type = raw_type
|
138
135
|
end
|
@@ -7,11 +7,12 @@ module WCC::Contentful
|
|
7
7
|
def _instrumentation_event_prefix
|
8
8
|
@_instrumentation_event_prefix ||=
|
9
9
|
# WCC::Contentful => contentful.wcc
|
10
|
-
'.' + (is_a?(Class) || is_a?(Module) ? self : self.class)
|
10
|
+
'.' + (is_a?(Class) || is_a?(Module) ? self : self.class) # rubocop:disable Style/StringConcatenation
|
11
11
|
.name.parameterize.split('-').reverse.join('.')
|
12
12
|
end
|
13
13
|
|
14
14
|
attr_writer :_instrumentation
|
15
|
+
|
15
16
|
def _instrumentation
|
16
17
|
# look for per-instance instrumentation then try class level
|
17
18
|
@_instrumentation || self.class._instrumentation
|
data/lib/wcc/contentful/link.rb
CHANGED
@@ -4,9 +4,7 @@
|
|
4
4
|
# It is used internally by the Store layer to compose the resulting resolved hashes.
|
5
5
|
# But you can use it too!
|
6
6
|
class WCC::Contentful::LinkVisitor
|
7
|
-
attr_reader :entry
|
8
|
-
attr_reader :fields
|
9
|
-
attr_reader :depth
|
7
|
+
attr_reader :entry, :fields, :depth
|
10
8
|
|
11
9
|
# @param [Hash] entry The entry hash (resolved or unresolved) to walk
|
12
10
|
# @param [Array<Symbol>] The type of fields to select from the entry tree.
|
@@ -66,7 +64,7 @@ class WCC::Contentful::LinkVisitor
|
|
66
64
|
|
67
65
|
def each_field(field)
|
68
66
|
each_locale(field) do |val, locale|
|
69
|
-
if val
|
67
|
+
if val.is_a?(Array)
|
70
68
|
val.each_with_index do |v, index|
|
71
69
|
yield(v, field, locale, index) unless v.nil?
|
72
70
|
end
|
@@ -20,7 +20,7 @@ module WCC::Contentful::Middleware::Store
|
|
20
20
|
event = 'miss'
|
21
21
|
# if it's not a contentful ID don't hit the API.
|
22
22
|
# Store a nil object if we can't find the object on the CDN.
|
23
|
-
(store.find(key, options) || nil_obj(key)) if key =~ /^\w+$/
|
23
|
+
(store.find(key, **options) || nil_obj(key)) if key =~ /^\w+$/
|
24
24
|
end
|
25
25
|
_instrument(event, key: key, options: options)
|
26
26
|
|
@@ -36,12 +36,8 @@ module WCC::Contentful::Middleware::Store
|
|
36
36
|
# figure out how to cache the results of a find_by query, ex:
|
37
37
|
# `find_by('slug' => '/about')`
|
38
38
|
def find_by(content_type:, filter: nil, options: nil)
|
39
|
-
if filter&.keys == ['sys.id']
|
40
|
-
|
41
|
-
# We can return just this item. Stores are not required to implement :include option.
|
42
|
-
if found = @cache.read(filter['sys.id'])
|
43
|
-
return found
|
44
|
-
end
|
39
|
+
if filter&.keys == ['sys.id'] && found = @cache.read(filter['sys.id'])
|
40
|
+
return found
|
45
41
|
end
|
46
42
|
|
47
43
|
store.find_by(content_type: content_type, filter: filter, options: options)
|
@@ -79,8 +75,8 @@ module WCC::Contentful::Middleware::Store
|
|
79
75
|
prev = @cache.read(id)
|
80
76
|
return if prev.nil? && LAZILY_CACHEABLE_TYPES.include?(type)
|
81
77
|
|
82
|
-
if (prev_rev = prev&.dig('sys', 'revision')) && (next_rev = json.dig('sys', 'revision'))
|
83
|
-
return prev
|
78
|
+
if (prev_rev = prev&.dig('sys', 'revision')) && (next_rev = json.dig('sys', 'revision')) && (next_rev < prev_rev)
|
79
|
+
return prev
|
84
80
|
end
|
85
81
|
|
86
82
|
# we also set DeletedEntry objects in the cache - no need to go hit the API when we know
|
@@ -96,9 +96,9 @@ module WCC::Contentful::Middleware::Store
|
|
96
96
|
|
97
97
|
def count
|
98
98
|
if middleware.has_select?
|
99
|
-
raise NameError, "Count cannot be determined because the middleware '#{middleware}'" \
|
100
|
-
|
101
|
-
|
99
|
+
raise NameError, "Count cannot be determined because the middleware '#{middleware}' " \
|
100
|
+
"implements the #select? method. Please use '.to_a.count' to count entries that " \
|
101
|
+
'pass the #select? method.'
|
102
102
|
end
|
103
103
|
|
104
104
|
# The wrapped query may get count from the "Total" field in the response,
|
@@ -112,9 +112,7 @@ module WCC::Contentful::Middleware::Store
|
|
112
112
|
result = wrapped_query.to_enum
|
113
113
|
result = result.select { |x| middleware.select?(x) } if middleware.has_select?
|
114
114
|
|
115
|
-
if options && options[:include]
|
116
|
-
result = result.map { |x| middleware.resolve_includes(x, options[:include]) }
|
117
|
-
end
|
115
|
+
result = result.map { |x| middleware.resolve_includes(x, options[:include]) } if options && options[:include]
|
118
116
|
|
119
117
|
result.map { |x| middleware.transform(x) }
|
120
118
|
end
|
data/lib/wcc/contentful/model.rb
CHANGED
@@ -41,10 +41,6 @@ require_relative './model_api'
|
|
41
41
|
class WCC::Contentful::Model
|
42
42
|
include WCC::Contentful::ModelAPI
|
43
43
|
|
44
|
-
# The Model base class maintains a registry which is best expressed as a
|
45
|
-
# class var.
|
46
|
-
# rubocop:disable Style/ClassVars
|
47
|
-
|
48
44
|
class << self
|
49
45
|
def const_missing(name)
|
50
46
|
type = WCC::Contentful::Helpers.content_type_from_constant(name)
|
@@ -53,5 +49,3 @@ class WCC::Contentful::Model
|
|
53
49
|
end
|
54
50
|
end
|
55
51
|
end
|
56
|
-
|
57
|
-
# rubocop:enable Style/ClassVars
|
@@ -13,9 +13,8 @@ module WCC::Contentful::ModelAPI
|
|
13
13
|
services.instrumentation
|
14
14
|
end
|
15
15
|
|
16
|
-
#
|
17
|
-
|
18
|
-
@@registry = {} # rubocop:disable Style/ClassVars
|
16
|
+
# Set the registry at the top of the namespace
|
17
|
+
@registry = {}
|
19
18
|
end
|
20
19
|
|
21
20
|
class_methods do
|
@@ -52,9 +51,7 @@ module WCC::Contentful::ModelAPI
|
|
52
51
|
|
53
52
|
file = configuration.schema_file
|
54
53
|
schema_json = JSON.parse(File.read(file))['contentTypes']
|
55
|
-
unless schema_json
|
56
|
-
raise ArgumentError, 'Please give either a JSON array of content types or file to load from'
|
57
|
-
end
|
54
|
+
raise ArgumentError, 'Please give either a JSON array of content types or file to load from' unless schema_json
|
58
55
|
|
59
56
|
@schema = WCC::Contentful::ContentTypeIndexer.from_json_schema(schema_json).types
|
60
57
|
end
|
@@ -69,7 +66,7 @@ module WCC::Contentful::ModelAPI
|
|
69
66
|
store = options[:preview] ? services.preview_store : services.store
|
70
67
|
raw =
|
71
68
|
_instrumentation.instrument 'find.model.contentful.wcc', id: id, options: options do
|
72
|
-
store.find(id, options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
|
69
|
+
store.find(id, **options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
|
73
70
|
end
|
74
71
|
|
75
72
|
new_from_raw(raw, options) if raw.present?
|
@@ -89,7 +86,7 @@ module WCC::Contentful::ModelAPI
|
|
89
86
|
def resolve_constant(content_type)
|
90
87
|
raise ArgumentError, 'content_type cannot be nil' unless content_type
|
91
88
|
|
92
|
-
const =
|
89
|
+
const = _registry[content_type]
|
93
90
|
return const if const
|
94
91
|
|
95
92
|
const_name = WCC::Contentful::Helpers.constant_from_content_type(content_type).to_s
|
@@ -143,14 +140,14 @@ module WCC::Contentful::ModelAPI
|
|
143
140
|
|
144
141
|
content_type ||= WCC::Contentful::Helpers.content_type_from_constant(klass)
|
145
142
|
|
146
|
-
|
143
|
+
_registry[content_type] = klass
|
147
144
|
end
|
148
145
|
|
149
146
|
# Returns the current registry of content type names to constants.
|
150
147
|
def registry
|
151
|
-
return {} unless
|
148
|
+
return {} unless _registry
|
152
149
|
|
153
|
-
|
150
|
+
_registry.dup.freeze
|
154
151
|
end
|
155
152
|
|
156
153
|
def reload!
|
@@ -176,7 +173,15 @@ module WCC::Contentful::ModelAPI
|
|
176
173
|
# that class. If nil, the generated WCC::Contentful::Model::{content_type} class
|
177
174
|
# will be resolved for this content type.
|
178
175
|
def registered?(content_type)
|
179
|
-
|
176
|
+
_registry[content_type]
|
177
|
+
end
|
178
|
+
|
179
|
+
protected
|
180
|
+
|
181
|
+
def _registry
|
182
|
+
# If calling register_for_content_type in a subclass, look up the superclass
|
183
|
+
# chain until we get to the model namespace which defines the registry
|
184
|
+
@registry || superclass._registry
|
180
185
|
end
|
181
186
|
end
|
182
187
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative './link'
|
4
4
|
require_relative './sys'
|
5
|
+
require_relative './rich_text'
|
5
6
|
|
6
7
|
module WCC::Contentful
|
7
8
|
class ModelBuilder
|
@@ -60,7 +61,7 @@ module WCC::Contentful
|
|
60
61
|
ct = content_type_from_raw(raw)
|
61
62
|
if ct != typedef.content_type
|
62
63
|
raise ArgumentError, 'Wrong Content Type - ' \
|
63
|
-
|
64
|
+
"'#{raw.dig('sys', 'id')}' is a #{ct}, expected #{typedef.content_type}"
|
64
65
|
end
|
65
66
|
@raw = raw.freeze
|
66
67
|
|
@@ -95,6 +96,8 @@ module WCC::Contentful
|
|
95
96
|
#
|
96
97
|
# when :DateTime
|
97
98
|
# raw_value = Time.parse(raw_value).localtime
|
99
|
+
when :RichText
|
100
|
+
raw_value = WCC::Contentful::RichText.tokenize(raw_value)
|
98
101
|
when :Int
|
99
102
|
raw_value = Integer(raw_value)
|
100
103
|
when :Float
|
@@ -104,12 +107,13 @@ module WCC::Contentful
|
|
104
107
|
# array fields need to resolve to an empty array when nothing is there
|
105
108
|
raw_value = []
|
106
109
|
end
|
107
|
-
instance_variable_set(
|
110
|
+
instance_variable_set("@#{f.name}", raw_value)
|
108
111
|
end
|
109
112
|
end
|
110
113
|
|
111
114
|
attr_reader :sys
|
112
115
|
attr_reader :raw
|
116
|
+
|
113
117
|
delegate :id, to: :sys
|
114
118
|
delegate :created_at, to: :sys
|
115
119
|
delegate :updated_at, to: :sys
|
@@ -119,11 +123,11 @@ module WCC::Contentful
|
|
119
123
|
# Make a field for each column:
|
120
124
|
typedef.fields.each_value do |f|
|
121
125
|
name = f.name
|
122
|
-
var_name =
|
126
|
+
var_name = "@#{name}"
|
123
127
|
case f.type
|
124
128
|
when :Asset, :Link
|
125
129
|
define_method(name) do
|
126
|
-
val = instance_variable_get(var_name
|
130
|
+
val = instance_variable_get("#{var_name}_resolved")
|
127
131
|
return val if val.present?
|
128
132
|
|
129
133
|
_resolve_field(name)
|
@@ -54,17 +54,15 @@ module WCC::Contentful::ModelMethods
|
|
54
54
|
_instrument 'resolve', id: id, depth: depth, backlinks: backlinked_ids do
|
55
55
|
# use include param to do resolution
|
56
56
|
store.find_by(content_type: self.class.content_type,
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
filter: { 'sys.id' => id },
|
58
|
+
options: context.except(*MODEL_LAYER_CONTEXT_KEYS).merge!({
|
59
|
+
include: [depth, 10].min
|
60
|
+
}))
|
61
61
|
end
|
62
|
-
unless raw
|
63
|
-
raise WCC::Contentful::ResolveError, "Cannot find #{self.class.content_type} with ID #{id}"
|
64
|
-
end
|
62
|
+
raise WCC::Contentful::ResolveError, "Cannot find #{self.class.content_type} with ID #{id}" unless raw
|
65
63
|
|
66
64
|
@raw = raw.freeze
|
67
|
-
links.each { |f| instance_variable_set(
|
65
|
+
links.each { |f| instance_variable_set("@#{f}", raw.dig('fields', f, sys.locale)) }
|
68
66
|
end
|
69
67
|
|
70
68
|
links.each { |f| _resolve_field(f, depth, context, options) }
|
@@ -142,7 +140,7 @@ module WCC::Contentful::ModelMethods
|
|
142
140
|
def _resolve_field(field_name, depth = 1, context = {}, options = {})
|
143
141
|
return if depth <= 0
|
144
142
|
|
145
|
-
var_name =
|
143
|
+
var_name = "@#{field_name}"
|
146
144
|
return unless val = instance_variable_get(var_name)
|
147
145
|
|
148
146
|
context = sys.context.to_h.merge(context)
|
@@ -182,17 +180,17 @@ module WCC::Contentful::ModelMethods
|
|
182
180
|
val = _try_map(val) { |v| load.call(v) }
|
183
181
|
val = val.compact if val.is_a? Array
|
184
182
|
|
185
|
-
instance_variable_set(var_name
|
183
|
+
instance_variable_set("#{var_name}_resolved", val)
|
186
184
|
rescue WCC::Contentful::CircularReferenceError
|
187
185
|
raise unless options[:circular_reference] == :ignore
|
188
186
|
end
|
189
187
|
end
|
190
188
|
|
191
189
|
def _resolved_field?(field_name, depth = 1)
|
192
|
-
var_name =
|
190
|
+
var_name = "@#{field_name}"
|
193
191
|
raw = instance_variable_get(var_name)
|
194
192
|
return true if raw.nil? || (raw.is_a?(Array) && raw.all?(&:nil?))
|
195
|
-
return false unless val = instance_variable_get(var_name
|
193
|
+
return false unless val = instance_variable_get("#{var_name}_resolved")
|
196
194
|
return true if depth <= 1
|
197
195
|
|
198
196
|
return val.resolved?(depth: depth - 1) unless val.is_a? Array
|
@@ -16,7 +16,7 @@ module WCC::Contentful::ModelSingletonMethods
|
|
16
16
|
raw =
|
17
17
|
_instrumentation.instrument 'find.model.contentful.wcc',
|
18
18
|
content_type: content_type, id: id, options: options do
|
19
|
-
store.find(id, { hint: type }.merge!(options.except(:preview)))
|
19
|
+
store.find(id, **{ hint: type }.merge!(options.except(:preview)))
|
20
20
|
end
|
21
21
|
new(raw, options) if raw.present?
|
22
22
|
end
|
@@ -66,12 +66,12 @@ module WCC::Contentful::ModelSingletonMethods
|
|
66
66
|
new(result, options) if result
|
67
67
|
end
|
68
68
|
|
69
|
-
def inherited(subclass)
|
69
|
+
def inherited(subclass) # rubocop:disable Lint/MissingSuper
|
70
70
|
# If another different class is already registered for this content type,
|
71
71
|
# don't auto-register this one.
|
72
|
-
return if
|
72
|
+
return if model_namespace.registered?(content_type)
|
73
73
|
|
74
|
-
|
74
|
+
model_namespace.register_for_content_type(content_type, klass: subclass)
|
75
75
|
end
|
76
76
|
|
77
77
|
class ModelQuery
|