wcc-contentful 1.1.2 → 1.2.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/app/jobs/wcc/contentful/webhook_enable_job.rb +1 -1
- data/lib/wcc/contentful/active_record_shim.rb +2 -2
- data/lib/wcc/contentful/content_type_indexer.rb +2 -0
- data/lib/wcc/contentful/event.rb +2 -8
- data/lib/wcc/contentful/indexed_representation.rb +1 -0
- data/lib/wcc/contentful/middleware/store/caching_middleware.rb +1 -1
- data/lib/wcc/contentful/model_api.rb +1 -1
- data/lib/wcc/contentful/model_builder.rb +3 -0
- data/lib/wcc/contentful/model_singleton_methods.rb +1 -1
- data/lib/wcc/contentful/rich_text/node.rb +62 -0
- data/lib/wcc/contentful/rich_text.rb +105 -0
- data/lib/wcc/contentful/simple_client/response.rb +25 -29
- data/lib/wcc/contentful/store/cdn_adapter.rb +10 -6
- data/lib/wcc/contentful/version.rb +1 -1
- data/lib/wcc/contentful.rb +1 -1
- data/wcc-contentful.gemspec +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f05ea585d54f7ca83c96e018021f88776ebb148cc40b7d719d6199b3a3cd9d0c
|
4
|
+
data.tar.gz: 0da490d12beb9938ec51c0ac40e140420966667b72afebc5c9895cf197308ea8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 979360a35356b041dbe0f8bbd4f0e5bfcaf91909ccc567322ceef8493de25300ea09a081e755142fe23bf9cfd5ea86ca7f919d3218d5dec44a98bcab5921af7d
|
7
|
+
data.tar.gz: 9e5f29e70fdcbccffd72aeb8b896e3518d2236cd699704289e8a3fdf1392201dc49671bab0eb83948e79ee079366de90b46add4ce2027e065db2da28b2be4e85
|
@@ -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
|
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
|
@@ -134,12 +134,6 @@ class WCC::Contentful::Event::SyncComplete
|
|
134
134
|
include WCC::Contentful::Event
|
135
135
|
|
136
136
|
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
137
|
@items = items.freeze
|
144
138
|
@source = source
|
145
139
|
@sys = WCC::Contentful::Sys.new(
|
@@ -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
|
|
@@ -68,7 +68,7 @@ module WCC::Contentful::ModelAPI
|
|
68
68
|
store = options[:preview] ? services.preview_store : services.store
|
69
69
|
raw =
|
70
70
|
_instrumentation.instrument 'find.model.contentful.wcc', id: id, options: options do
|
71
|
-
store.find(id, options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
|
71
|
+
store.find(id, **options.except(*WCC::Contentful::ModelMethods::MODEL_LAYER_CONTEXT_KEYS))
|
72
72
|
end
|
73
73
|
|
74
74
|
new_from_raw(raw, options) if raw.present?
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WCC::Contentful::RichText
|
4
|
+
module Node
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def keys
|
8
|
+
members.map(&:to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
included do
|
12
|
+
include Enumerable
|
13
|
+
|
14
|
+
alias_method :node_type, :nodeType
|
15
|
+
|
16
|
+
# Make the nodes read-only
|
17
|
+
undef_method :[]=
|
18
|
+
members.each do |member|
|
19
|
+
undef_method("#{member}=")
|
20
|
+
end
|
21
|
+
|
22
|
+
# Override each so it has a Hash-like interface rather than Struct-like.
|
23
|
+
# The goal being to mimic a JSON-parsed hash representation of the raw
|
24
|
+
def each
|
25
|
+
members.map do |key|
|
26
|
+
tuple = [key.to_s, self.[](key)]
|
27
|
+
yield tuple if block_given?
|
28
|
+
tuple
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class_methods do
|
34
|
+
# Default value for node_type covers most cases
|
35
|
+
def node_type
|
36
|
+
name.demodulize.underscore.dasherize
|
37
|
+
end
|
38
|
+
|
39
|
+
def tokenize(raw, context = nil)
|
40
|
+
unless raw['nodeType'] == node_type
|
41
|
+
raise ArgumentError, "Expected '#{node_type}', got '#{raw['nodeType']}'"
|
42
|
+
end
|
43
|
+
|
44
|
+
values =
|
45
|
+
members.map do |symbol|
|
46
|
+
val = raw[symbol.to_s]
|
47
|
+
|
48
|
+
case symbol
|
49
|
+
when :content
|
50
|
+
WCC::Contentful::RichText.tokenize(val, context)
|
51
|
+
# when :data
|
52
|
+
# TODO: resolve links...
|
53
|
+
else
|
54
|
+
val
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
new(*values)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './rich_text/node'
|
4
|
+
|
5
|
+
##
|
6
|
+
# This module contains a number of structs representing nodes in a Contentful
|
7
|
+
# rich text field. When the Model layer parses a Rich Text field from
|
8
|
+
# Contentful, it is turned into a WCC::Contentful::RichText::Document node.
|
9
|
+
# The {WCC::Contentful::RichText::Document#content content} method of this
|
10
|
+
# node is an Array containing paragraph, blockquote, entry, and other nodes.
|
11
|
+
#
|
12
|
+
# The various structs in the RichText object model are designed to mimic the
|
13
|
+
# Hash interface, so that the indexing operator `#[]` and the `#dig` method
|
14
|
+
# can be used to traverse the data. The data can also be accessed by the
|
15
|
+
# attribute reader methods defined on the structs. Both of these are considered
|
16
|
+
# part of the public API of the model and will not change.
|
17
|
+
#
|
18
|
+
# In a future release we plan to implement automatic link resolution. When that
|
19
|
+
# happens, the `.data` attribute of embedded entries and assets will return a
|
20
|
+
# new class that is able to resolve the `.target` automatically into a full
|
21
|
+
# entry or asset. This future class will still respect the hash accessor methods
|
22
|
+
# `#[]`, `#dig`, `#keys`, and `#each`, so it is safe to use those.
|
23
|
+
module WCC::Contentful::RichText
|
24
|
+
##
|
25
|
+
# Recursively converts a raw JSON-parsed hash into the RichText object model.
|
26
|
+
def self.tokenize(raw, context = nil)
|
27
|
+
return unless raw
|
28
|
+
return raw.map { |c| tokenize(c, context) } if raw.is_a?(Array)
|
29
|
+
|
30
|
+
klass =
|
31
|
+
case raw['nodeType']
|
32
|
+
when 'document'
|
33
|
+
Document
|
34
|
+
when 'paragraph'
|
35
|
+
Paragraph
|
36
|
+
when 'blockquote'
|
37
|
+
Blockquote
|
38
|
+
when 'text'
|
39
|
+
Text
|
40
|
+
when 'embedded-entry-inline'
|
41
|
+
EmbeddedEntryInline
|
42
|
+
when 'embedded-entry-block'
|
43
|
+
EmbeddedEntryBlock
|
44
|
+
when 'embedded-asset-block'
|
45
|
+
EmbeddedAssetBlock
|
46
|
+
when /heading\-(\d+)/
|
47
|
+
size = Regexp.last_match(1)
|
48
|
+
const_get("Heading#{size}")
|
49
|
+
else
|
50
|
+
Unknown
|
51
|
+
end
|
52
|
+
|
53
|
+
klass.tokenize(raw, context)
|
54
|
+
end
|
55
|
+
|
56
|
+
Document =
|
57
|
+
Struct.new(:nodeType, :data, :content) do
|
58
|
+
include WCC::Contentful::RichText::Node
|
59
|
+
end
|
60
|
+
|
61
|
+
Paragraph =
|
62
|
+
Struct.new(:nodeType, :data, :content) do
|
63
|
+
include WCC::Contentful::RichText::Node
|
64
|
+
end
|
65
|
+
|
66
|
+
Blockquote =
|
67
|
+
Struct.new(:nodeType, :data, :content) do
|
68
|
+
include WCC::Contentful::RichText::Node
|
69
|
+
end
|
70
|
+
|
71
|
+
Text =
|
72
|
+
Struct.new(:nodeType, :value, :marks, :data) do
|
73
|
+
include WCC::Contentful::RichText::Node
|
74
|
+
end
|
75
|
+
|
76
|
+
EmbeddedEntryInline =
|
77
|
+
Struct.new(:nodeType, :data, :content) do
|
78
|
+
include WCC::Contentful::RichText::Node
|
79
|
+
end
|
80
|
+
|
81
|
+
EmbeddedEntryBlock =
|
82
|
+
Struct.new(:nodeType, :data, :content) do
|
83
|
+
include WCC::Contentful::RichText::Node
|
84
|
+
end
|
85
|
+
|
86
|
+
EmbeddedAssetBlock =
|
87
|
+
Struct.new(:nodeType, :data, :content) do
|
88
|
+
include WCC::Contentful::RichText::Node
|
89
|
+
end
|
90
|
+
|
91
|
+
(1..5).each do |i|
|
92
|
+
struct =
|
93
|
+
Struct.new(:nodeType, :data, :content) do
|
94
|
+
include WCC::Contentful::RichText::Node
|
95
|
+
end
|
96
|
+
sz = i
|
97
|
+
struct.define_singleton_method(:node_type) { "heading-#{sz}" }
|
98
|
+
const_set("Heading#{sz}", struct)
|
99
|
+
end
|
100
|
+
|
101
|
+
Unknown =
|
102
|
+
Struct.new(:nodeType, :data, :content) do
|
103
|
+
include WCC::Contentful::RichText::Node
|
104
|
+
end
|
105
|
+
end
|
@@ -49,7 +49,6 @@ class WCC::Contentful::SimpleClient
|
|
49
49
|
|
50
50
|
def next_page
|
51
51
|
return unless next_page?
|
52
|
-
return @next_page if @next_page
|
53
52
|
|
54
53
|
query = (@request[:query] || {}).merge({
|
55
54
|
skip: page_items.length + skip
|
@@ -58,7 +57,7 @@ class WCC::Contentful::SimpleClient
|
|
58
57
|
_instrument 'page', url: @request[:url], query: query do
|
59
58
|
@client.get(@request[:url], query)
|
60
59
|
end
|
61
|
-
|
60
|
+
np.assert_ok!
|
62
61
|
end
|
63
62
|
|
64
63
|
def initialize(client, request, raw_response)
|
@@ -77,16 +76,7 @@ class WCC::Contentful::SimpleClient
|
|
77
76
|
def each_page(&block)
|
78
77
|
raise ArgumentError, 'Not a collection response' unless page_items
|
79
78
|
|
80
|
-
ret =
|
81
|
-
Enumerator.new do |y|
|
82
|
-
y << self
|
83
|
-
|
84
|
-
if next_page?
|
85
|
-
next_page.each_page.each do |page|
|
86
|
-
y << page
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
79
|
+
ret = PaginatingEnumerable.new(self)
|
90
80
|
|
91
81
|
if block_given?
|
92
82
|
ret.map(&block)
|
@@ -118,11 +108,6 @@ class WCC::Contentful::SimpleClient
|
|
118
108
|
raw.dig('includes')&.each_with_object({}) do |(_t, entries), h|
|
119
109
|
entries.each { |e| h[e.dig('sys', 'id')] = e }
|
120
110
|
end || {}
|
121
|
-
|
122
|
-
return @includes unless @next_page
|
123
|
-
|
124
|
-
# This could be more efficient - maybe not worth worrying about
|
125
|
-
@includes.merge(@next_page.includes)
|
126
111
|
end
|
127
112
|
end
|
128
113
|
|
@@ -144,8 +129,8 @@ class WCC::Contentful::SimpleClient
|
|
144
129
|
@client.get(url)
|
145
130
|
end
|
146
131
|
|
147
|
-
|
148
|
-
|
132
|
+
next_page = SyncResponse.new(next_page)
|
133
|
+
next_page.assert_ok!
|
149
134
|
end
|
150
135
|
|
151
136
|
def next_sync_token
|
@@ -160,16 +145,7 @@ class WCC::Contentful::SimpleClient
|
|
160
145
|
def each_page
|
161
146
|
raise ArgumentError, 'Not a collection response' unless page_items
|
162
147
|
|
163
|
-
ret =
|
164
|
-
Enumerator.new do |y|
|
165
|
-
y << self
|
166
|
-
|
167
|
-
if next_page?
|
168
|
-
next_page.each_page.each do |page|
|
169
|
-
y << page
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
148
|
+
ret = PaginatingEnumerable.new(self)
|
173
149
|
|
174
150
|
if block_given?
|
175
151
|
ret.map(&block)
|
@@ -190,6 +166,26 @@ class WCC::Contentful::SimpleClient
|
|
190
166
|
end
|
191
167
|
end
|
192
168
|
|
169
|
+
class PaginatingEnumerable
|
170
|
+
include Enumerable
|
171
|
+
|
172
|
+
def initialize(initial_page)
|
173
|
+
raise ArgumentError, 'Must provide initial page' unless initial_page.present?
|
174
|
+
|
175
|
+
@initial_page = initial_page
|
176
|
+
end
|
177
|
+
|
178
|
+
def each
|
179
|
+
page = @initial_page
|
180
|
+
yield page
|
181
|
+
|
182
|
+
while page.next_page?
|
183
|
+
page = page.next_page
|
184
|
+
yield page
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
193
189
|
class ApiError < StandardError
|
194
190
|
attr_reader :response
|
195
191
|
|
@@ -72,9 +72,13 @@ module WCC::Contentful::Store
|
|
72
72
|
delegate :count, to: :response
|
73
73
|
|
74
74
|
def to_enum
|
75
|
-
return response.
|
75
|
+
return response.each_page.flat_map(&:page_items) unless @options[:include]
|
76
76
|
|
77
|
-
response.
|
77
|
+
response.each_page
|
78
|
+
.flat_map { |page| page.page_items.each_with_object(page).to_a }
|
79
|
+
.map do |e, page|
|
80
|
+
resolve_includes(e, page.includes, depth: @options[:include])
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
def initialize(store, client:, relation:, options: nil, **extra)
|
@@ -160,18 +164,18 @@ module WCC::Contentful::Store
|
|
160
164
|
end
|
161
165
|
end
|
162
166
|
|
163
|
-
def resolve_includes(entry, depth)
|
167
|
+
def resolve_includes(entry, includes, depth:)
|
164
168
|
return entry unless entry && depth && depth > 0
|
165
169
|
|
166
170
|
# Dig links out of response.includes and insert them into the entry
|
167
171
|
WCC::Contentful::LinkVisitor.new(entry, :Link, depth: depth - 1).map! do |val|
|
168
|
-
resolve_link(val)
|
172
|
+
resolve_link(val, includes)
|
169
173
|
end
|
170
174
|
end
|
171
175
|
|
172
|
-
def resolve_link(val)
|
176
|
+
def resolve_link(val, includes)
|
173
177
|
return val unless val.is_a?(Hash) && val.dig('sys', 'type') == 'Link'
|
174
|
-
return val unless included =
|
178
|
+
return val unless included = includes[val.dig('sys', 'id')]
|
175
179
|
|
176
180
|
included
|
177
181
|
end
|
data/lib/wcc/contentful.rb
CHANGED
data/wcc-contentful.gemspec
CHANGED
@@ -54,7 +54,7 @@ Gem::Specification.new do |spec|
|
|
54
54
|
spec.add_development_dependency 'generator_spec', '~> 0.9.4'
|
55
55
|
# spec.add_development_dependency 'rails', '~> 5.0'
|
56
56
|
# spec.add_development_dependency 'rspec-rails', '~> 3.7'
|
57
|
-
spec.add_development_dependency 'sqlite3', '~> 1.
|
57
|
+
spec.add_development_dependency 'sqlite3', '~> 1.4'
|
58
58
|
spec.add_development_dependency 'timecop', '~> 0.9.1'
|
59
59
|
|
60
60
|
# optional dependencies
|
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.2.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-
|
11
|
+
date: 2022-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -282,14 +282,14 @@ dependencies:
|
|
282
282
|
requirements:
|
283
283
|
- - "~>"
|
284
284
|
- !ruby/object:Gem::Version
|
285
|
-
version: 1.
|
285
|
+
version: '1.4'
|
286
286
|
type: :development
|
287
287
|
prerelease: false
|
288
288
|
version_requirements: !ruby/object:Gem::Requirement
|
289
289
|
requirements:
|
290
290
|
- - "~>"
|
291
291
|
- !ruby/object:Gem::Version
|
292
|
-
version: 1.
|
292
|
+
version: '1.4'
|
293
293
|
- !ruby/object:Gem::Dependency
|
294
294
|
name: timecop
|
295
295
|
requirement: !ruby/object:Gem::Requirement
|
@@ -461,6 +461,8 @@ files:
|
|
461
461
|
- lib/wcc/contentful/model_singleton_methods.rb
|
462
462
|
- lib/wcc/contentful/rails.rb
|
463
463
|
- lib/wcc/contentful/rake.rb
|
464
|
+
- lib/wcc/contentful/rich_text.rb
|
465
|
+
- lib/wcc/contentful/rich_text/node.rb
|
464
466
|
- lib/wcc/contentful/rspec.rb
|
465
467
|
- lib/wcc/contentful/services.rb
|
466
468
|
- lib/wcc/contentful/simple_client.rb
|
@@ -501,7 +503,7 @@ homepage: https://github.com/watermarkchurch/wcc-contentful/wcc-contentful
|
|
501
503
|
licenses:
|
502
504
|
- MIT
|
503
505
|
metadata:
|
504
|
-
documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.
|
506
|
+
documentation_uri: https://watermarkchurch.github.io/wcc-contentful/1.2/wcc-contentful
|
505
507
|
post_install_message:
|
506
508
|
rdoc_options: []
|
507
509
|
require_paths:
|
@@ -517,8 +519,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
517
519
|
- !ruby/object:Gem::Version
|
518
520
|
version: '0'
|
519
521
|
requirements: []
|
520
|
-
|
521
|
-
rubygems_version: 2.7.6.2
|
522
|
+
rubygems_version: 3.3.7
|
522
523
|
signing_key:
|
523
524
|
specification_version: 4
|
524
525
|
summary: '[](https://rubygems.org/gems/wcc-contentful)
|