wcc-contentful 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: '[![Gem Version](https://badge.fury.io/rb/wcc-contentful.svg)](https://rubygems.org/gems/wcc-contentful)
|