contentful-scheduler 0.4.0 → 0.5.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/CHANGELOG.md +4 -0
- data/README.md +6 -5
- data/lib/contentful/scheduler/queue.rb +89 -14
- data/lib/contentful/scheduler/tasks/unpublish.rb +21 -0
- data/lib/contentful/scheduler/tasks.rb +1 -0
- data/lib/contentful/scheduler/version.rb +1 -1
- data/spec/contentful/scheduler/queue_spec.rb +84 -4
- data/spec/contentful/scheduler/tasks/unpublish_spec.rb +42 -0
- data/spec/spec_helper.rb +5 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff923429648f1e2a809b1f379f99fa592eafc40d
|
4
|
+
data.tar.gz: 4e6c728e653123254f245d3f6e780a3c986e61c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb9ecbbd1524f8da38140c54447d7014aca017a5fa73d6403f5256c82f1e8d7bafe94a43d81d3ca86a002fa5de78006446fe2a16667b6601a4c55a559f052e2b
|
7
|
+
data.tar.gz: 9f62df4f008c1f0c27bca2e1f52a532b132687038e0156e29a5a0a807c8ed70affb1ee85130e4c0b001a82597cf500717f432af74f02853fd2c9c6ae7a535d5b
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -7,18 +7,18 @@ Scheduling Server for Contentful entries.
|
|
7
7
|
|
8
8
|
## What does `contentful-scheduler` do?
|
9
9
|
The aim of `contentful-scheduler` is to have developers setting up their Contentful
|
10
|
-
entries for scheduled publishing.
|
10
|
+
entries for scheduled publishing and unpublishing.
|
11
11
|
|
12
12
|
## How does it work
|
13
13
|
`contentful-scheduler` provides a web endpoint to receive webhook calls from Contentful.
|
14
14
|
|
15
15
|
Every time the endpoint recieves a call it looks for the value of the field defined in the configuration.
|
16
|
-
If the value is a time in the future it will schedule the entry for publishing at the specified time.
|
16
|
+
If the value is a time in the future it will schedule the entry for publishing or unpublishing at the specified time.
|
17
17
|
|
18
18
|
A background worker based on the popular `resque` gem will then proceed to actually make the publish call
|
19
|
-
against the Content Management API at the due time. For this the Entries you wish to publish require a
|
20
|
-
customizable Date field, which we advice to call `publishDate
|
21
|
-
`Rakefile` and is specific per-space.
|
19
|
+
against the Content Management API at the due time. For this the Entries you wish to publish or unpublish require a
|
20
|
+
customizable Date field, which we advice to call `publishDate` for the publishing action and `unpublishDate` for the unpublishing action,
|
21
|
+
this field can be configured inside your `Rakefile` and is specific per-space.
|
22
22
|
|
23
23
|
You can add multiple spaces to your configuration, making it useful if you have a milti-space setup.
|
24
24
|
|
@@ -91,6 +91,7 @@ config = {
|
|
91
91
|
spaces: {
|
92
92
|
'YOUR_SPACE_ID' => {
|
93
93
|
publish_field: 'publishDate', # It specifies the field ID for your Publish Date in your Content Type
|
94
|
+
unpublish_field: 'unpublishDate', # Optional - It specifies the field ID for your Unpublish Date in your Content Type
|
94
95
|
management_token: 'YOUR_TOKEN',
|
95
96
|
auth: { # This is optional
|
96
97
|
# ... content in this section will be explained in a separate section ...
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require_relative "tasks"
|
2
1
|
require 'chronic'
|
3
2
|
require 'contentful/webhook/listener'
|
3
|
+
require_relative "tasks"
|
4
4
|
|
5
5
|
module Contentful
|
6
6
|
module Scheduler
|
@@ -14,28 +14,51 @@ module Contentful
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def update_or_create(webhook)
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
if publishable?(webhook)
|
18
|
+
success = update_or_create_for_publish(webhook)
|
19
|
+
log_event_success(webhook, success, 'publish', 'added to')
|
20
|
+
end
|
21
|
+
|
22
|
+
if unpublishable?(webhook)
|
23
|
+
success = update_or_create_for_unpublish(webhook) && success
|
24
|
+
log_event_success(webhook, success, 'unpublish', 'added to')
|
25
|
+
end
|
26
|
+
end
|
20
27
|
|
21
|
-
|
28
|
+
def update_or_create_for_publish(webhook)
|
29
|
+
remove_publish(webhook) if in_publish_queue?(webhook)
|
30
|
+
return false unless publish_is_future?(webhook)
|
31
|
+
|
32
|
+
return Resque.enqueue_at(
|
22
33
|
publish_date(webhook),
|
23
34
|
::Contentful::Scheduler::Tasks::Publish,
|
24
35
|
webhook.space_id,
|
25
36
|
webhook.id,
|
26
37
|
::Contentful::Scheduler.config[:spaces][webhook.space_id][:management_token]
|
27
38
|
)
|
39
|
+
end
|
28
40
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
41
|
+
def update_or_create_for_unpublish(webhook)
|
42
|
+
remove_unpublish(webhook) if in_unpublish_queue?(webhook)
|
43
|
+
return false unless unpublish_is_future?(webhook)
|
44
|
+
|
45
|
+
return Resque.enqueue_at(
|
46
|
+
unpublish_date(webhook),
|
47
|
+
::Contentful::Scheduler::Tasks::Unpublish,
|
48
|
+
webhook.space_id,
|
49
|
+
webhook.id,
|
50
|
+
::Contentful::Scheduler.config[:spaces][webhook.space_id][:management_token]
|
51
|
+
)
|
34
52
|
end
|
35
53
|
|
36
54
|
def remove(webhook)
|
55
|
+
remove_publish(webhook)
|
56
|
+
remove_unpublish(webhook)
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_publish(webhook)
|
37
60
|
return unless publishable?(webhook)
|
38
|
-
return unless
|
61
|
+
return unless in_publish_queue?(webhook)
|
39
62
|
|
40
63
|
success = Resque.remove_delayed(
|
41
64
|
::Contentful::Scheduler::Tasks::Publish,
|
@@ -44,10 +67,28 @@ module Contentful
|
|
44
67
|
::Contentful::Scheduler.config[:management_token]
|
45
68
|
)
|
46
69
|
|
70
|
+
log_event_success(webhook, success, 'publish', 'removed from')
|
71
|
+
end
|
72
|
+
|
73
|
+
def remove_unpublish(webhook)
|
74
|
+
return unless unpublishable?(webhook)
|
75
|
+
return unless in_unpublish_queue?(webhook)
|
76
|
+
|
77
|
+
success = Resque.remove_delayed(
|
78
|
+
::Contentful::Scheduler::Tasks::Unpublish,
|
79
|
+
webhook.space_id,
|
80
|
+
webhook.id,
|
81
|
+
::Contentful::Scheduler.config[:management_token]
|
82
|
+
)
|
83
|
+
|
84
|
+
log_event_success(webhook, success, 'unpublish', 'removed from')
|
85
|
+
end
|
86
|
+
|
87
|
+
def log_event_success(webhook, success, event_kind, action)
|
47
88
|
if success
|
48
|
-
logger.info "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} successfully
|
89
|
+
logger.info "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} successfully #{action} the #{event_kind} queue"
|
49
90
|
else
|
50
|
-
logger.warn "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} couldn't be
|
91
|
+
logger.warn "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} couldn't be #{action} the #{event_kind} queue"
|
51
92
|
end
|
52
93
|
end
|
53
94
|
|
@@ -61,22 +102,48 @@ module Contentful
|
|
61
102
|
false
|
62
103
|
end
|
63
104
|
|
105
|
+
def unpublishable?(webhook)
|
106
|
+
return false unless spaces.key?(webhook.space_id)
|
107
|
+
|
108
|
+
if webhook_unpublish_field?(webhook)
|
109
|
+
return !webhook_unpublish_field(webhook).nil? && unpublish_is_future?(webhook)
|
110
|
+
end
|
111
|
+
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
64
115
|
def publish_is_future?(webhook)
|
65
116
|
publish_date(webhook) > Time.now.utc
|
66
117
|
end
|
67
118
|
|
68
|
-
def
|
119
|
+
def unpublish_is_future?(webhook)
|
120
|
+
unpublish_date(webhook) > Time.now.utc
|
121
|
+
end
|
122
|
+
|
123
|
+
def in_publish_queue?(webhook)
|
69
124
|
Resque.peek(::Contentful::Scheduler::Tasks::Publish, 0, -1).any? do |job|
|
70
125
|
job['args'][0] == webhook.space_id && job['args'][1] == webhook.id
|
71
126
|
end
|
72
127
|
end
|
73
128
|
|
129
|
+
def in_unpublish_queue?(webhook)
|
130
|
+
Resque.peek(::Contentful::Scheduler::Tasks::Unpublish, 0, -1).any? do |job|
|
131
|
+
job['args'][0] == webhook.space_id && job['args'][1] == webhook.id
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
74
135
|
def publish_date(webhook)
|
75
136
|
date_field = webhook_publish_field(webhook)
|
76
137
|
date_field = date_field[date_field.keys[0]] if date_field.is_a? Hash
|
77
138
|
Chronic.parse(date_field).utc
|
78
139
|
end
|
79
140
|
|
141
|
+
def unpublish_date(webhook)
|
142
|
+
date_field = webhook_unpublish_field(webhook)
|
143
|
+
date_field = date_field[date_field.keys[0]] if date_field.is_a? Hash
|
144
|
+
Chronic.parse(date_field).utc
|
145
|
+
end
|
146
|
+
|
80
147
|
def spaces
|
81
148
|
config[:spaces]
|
82
149
|
end
|
@@ -85,10 +152,18 @@ module Contentful
|
|
85
152
|
webhook.fields.key?(spaces.fetch(webhook.space_id, {})[:publish_field])
|
86
153
|
end
|
87
154
|
|
155
|
+
def webhook_unpublish_field?(webhook)
|
156
|
+
webhook.fields.key?(spaces.fetch(webhook.space_id, {})[:unpublish_field])
|
157
|
+
end
|
158
|
+
|
88
159
|
def webhook_publish_field(webhook)
|
89
160
|
webhook.fields[spaces[webhook.space_id][:publish_field]]
|
90
161
|
end
|
91
162
|
|
163
|
+
def webhook_unpublish_field(webhook)
|
164
|
+
webhook.fields[spaces[webhook.space_id][:unpublish_field]]
|
165
|
+
end
|
166
|
+
|
92
167
|
private
|
93
168
|
|
94
169
|
def initialize(logger)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'contentful/management'
|
2
|
+
|
3
|
+
module Contentful
|
4
|
+
module Scheduler
|
5
|
+
module Tasks
|
6
|
+
class Unpublish
|
7
|
+
@queue = :unpublish
|
8
|
+
|
9
|
+
def self.perform(space_id, entry_id, token)
|
10
|
+
client = ::Contentful::Management::Client.new(
|
11
|
+
token,
|
12
|
+
raise_errors: true,
|
13
|
+
application_name: 'contentful.scheduler',
|
14
|
+
application_version: Contentful::Scheduler::VERSION
|
15
|
+
)
|
16
|
+
client.entries.find(space_id, entry_id).unpublish
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -36,12 +36,32 @@ describe Contentful::Scheduler::Queue do
|
|
36
36
|
)).to be_falsey
|
37
37
|
end
|
38
38
|
|
39
|
+
it '#webhook_unpublish_field?' do
|
40
|
+
expect(subject.webhook_unpublish_field?(
|
41
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'unpublish_field' => 'something'})
|
42
|
+
)).to be_truthy
|
43
|
+
|
44
|
+
expect(subject.webhook_publish_field?(
|
45
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'not_unpublish_field' => 'something'})
|
46
|
+
)).to be_falsey
|
47
|
+
|
48
|
+
expect(subject.webhook_publish_field?(
|
49
|
+
WebhookDouble.new('bar', 'other_space', {}, {'not_my_field' => 'something'})
|
50
|
+
)).to be_falsey
|
51
|
+
end
|
52
|
+
|
39
53
|
it '#webhook_publish_field' do
|
40
54
|
expect(subject.webhook_publish_field(
|
41
55
|
WebhookDouble.new('bar', 'foo', {}, {'my_field' => 'something'})
|
42
56
|
)).to eq 'something'
|
43
57
|
end
|
44
58
|
|
59
|
+
it '#webhook_unpublish_field' do
|
60
|
+
expect(subject.webhook_unpublish_field(
|
61
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'unpublish_field' => 'something'})
|
62
|
+
)).to eq 'something'
|
63
|
+
end
|
64
|
+
|
45
65
|
describe '#publish_date' do
|
46
66
|
it 'works if date field not localized' do
|
47
67
|
expect(subject.publish_date(
|
@@ -60,6 +80,24 @@ describe Contentful::Scheduler::Queue do
|
|
60
80
|
end
|
61
81
|
end
|
62
82
|
|
83
|
+
describe '#unpublish_date' do
|
84
|
+
it 'works if date field not localized' do
|
85
|
+
expect(subject.unpublish_date(
|
86
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'unpublish_field' => '2011-04-04T22:00:00+00:00'})
|
87
|
+
)).to eq DateTime.new(2011, 4, 4, 22, 0, 0).to_time.utc
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'works if date field localized by grabbing first available locale' do
|
91
|
+
expect(subject.unpublish_date(
|
92
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'unpublish_field' => {'en-US': '2011-04-04T22:00:00+00:00'}})
|
93
|
+
)).to eq DateTime.new(2011, 4, 4, 22, 0, 0).to_time.utc
|
94
|
+
|
95
|
+
expect(subject.unpublish_date(
|
96
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'unpublish_field' => {'en-CA': '2011-04-04T23:00:00Z'}})
|
97
|
+
)).to eq DateTime.new(2011, 4, 4, 23, 0, 0).to_time.utc
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
63
101
|
describe '#publishable?' do
|
64
102
|
it 'false if webhook space not present in config' do
|
65
103
|
expect(subject.publishable?(
|
@@ -86,18 +124,60 @@ describe Contentful::Scheduler::Queue do
|
|
86
124
|
end
|
87
125
|
end
|
88
126
|
|
89
|
-
describe '#
|
127
|
+
describe '#unpublishable?' do
|
128
|
+
it 'false if webhook space not present in config' do
|
129
|
+
expect(subject.unpublishable?(
|
130
|
+
WebhookDouble.new('bar', 'not_foo')
|
131
|
+
)).to be_falsey
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'false if unpublish_field is not found' do
|
135
|
+
expect(subject.unpublishable?(
|
136
|
+
WebhookDouble.new('bar', 'unpublish_space')
|
137
|
+
)).to be_falsey
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'false if unpublish_field is nil' do
|
141
|
+
expect(subject.unpublishable?(
|
142
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'unpublish_field' => nil})
|
143
|
+
)).to be_falsey
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'true if unpublish_field is populated' do
|
147
|
+
expect(subject.unpublishable?(
|
148
|
+
WebhookDouble.new('bar', 'unpublish_space', {}, {'unpublish_field' => '2111-04-04T22:00:00+00:00'})
|
149
|
+
)).to be_truthy
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#in_publish_queue?' do
|
90
154
|
it 'false if not in queue' do
|
91
155
|
allow(Resque).to receive(:peek) { [] }
|
92
|
-
expect(subject.
|
156
|
+
expect(subject.in_publish_queue?(
|
93
157
|
WebhookDouble.new('bar', 'foo')
|
94
158
|
)).to be_falsey
|
95
159
|
end
|
96
160
|
|
97
161
|
it 'true if in queue' do
|
98
162
|
allow(Resque).to receive(:peek) { [{'args' => ['foo', 'bar']}] }
|
99
|
-
expect(subject.
|
163
|
+
expect(subject.in_publish_queue?(
|
164
|
+
WebhookDouble.new('bar', 'foo')
|
165
|
+
)).to be_truthy
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#in_unpublish_queue?' do
|
170
|
+
it 'false if not in queue' do
|
171
|
+
allow(Resque).to receive(:peek) { [] }
|
172
|
+
expect(subject.in_unpublish_queue?(
|
100
173
|
WebhookDouble.new('bar', 'foo')
|
174
|
+
)).to be_falsey
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'true if in queue' do
|
178
|
+
allow(Resque).to receive(:peek) { [{'args' => ['unpublish_space', 'bar']}] }
|
179
|
+
expect(subject.in_unpublish_queue?(
|
180
|
+
WebhookDouble.new('bar', 'unpublish_space')
|
101
181
|
)).to be_truthy
|
102
182
|
end
|
103
183
|
end
|
@@ -151,7 +231,7 @@ describe Contentful::Scheduler::Queue do
|
|
151
231
|
'bar',
|
152
232
|
'foo'
|
153
233
|
) { true }
|
154
|
-
expect(subject).to receive(:
|
234
|
+
expect(subject).to receive(:remove_publish)
|
155
235
|
|
156
236
|
subject.update_or_create(WebhookDouble.new('bar', 'foo', {}, {'my_field' => '2099-04-04T22:00:00+00:00'}))
|
157
237
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MockEntry
|
4
|
+
def publish
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class MockClient
|
9
|
+
def entries
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class MockEntries
|
14
|
+
def find
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Contentful::Scheduler::Tasks::Unpublish do
|
19
|
+
let(:mock_client) { MockClient.new }
|
20
|
+
let(:mock_entries) { MockEntries.new }
|
21
|
+
let(:mock_entry) { MockEntry.new }
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
::Contentful::Scheduler.config = base_config
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'class methods' do
|
28
|
+
it '::perform' do
|
29
|
+
expect(::Contentful::Management::Client).to receive(:new).with(
|
30
|
+
'foo',
|
31
|
+
raise_errors: true,
|
32
|
+
application_name: 'contentful.scheduler',
|
33
|
+
application_version: Contentful::Scheduler::VERSION
|
34
|
+
) { mock_client }
|
35
|
+
expect(mock_client).to receive(:entries) { mock_entries }
|
36
|
+
expect(mock_entries).to receive(:find).with('foo', 'bar') { mock_entry }
|
37
|
+
expect(mock_entry).to receive(:unpublish)
|
38
|
+
|
39
|
+
described_class.perform('foo', 'bar', ::Contentful::Scheduler.config[:spaces]['foo'][:management_token])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -80,6 +80,11 @@ def base_config
|
|
80
80
|
publish_field: 'my_field',
|
81
81
|
management_token: 'foo'
|
82
82
|
},
|
83
|
+
'unpublish_space' => {
|
84
|
+
publish_field: 'my_field',
|
85
|
+
unpublish_field: 'unpublish_field',
|
86
|
+
management_token: 'foo'
|
87
|
+
},
|
83
88
|
'no_auth' => {
|
84
89
|
publish_field: 'my_field',
|
85
90
|
management_token: 'foo'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contentful-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Contentful GmbH (David Litvak Bruno0
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: contentful-webhook-listener
|
@@ -206,11 +206,13 @@ files:
|
|
206
206
|
- lib/contentful/scheduler/queue.rb
|
207
207
|
- lib/contentful/scheduler/tasks.rb
|
208
208
|
- lib/contentful/scheduler/tasks/publish.rb
|
209
|
+
- lib/contentful/scheduler/tasks/unpublish.rb
|
209
210
|
- lib/contentful/scheduler/version.rb
|
210
211
|
- spec/contentful/scheduler/auth_spec.rb
|
211
212
|
- spec/contentful/scheduler/controller_spec.rb
|
212
213
|
- spec/contentful/scheduler/queue_spec.rb
|
213
214
|
- spec/contentful/scheduler/tasks/publish_spec.rb
|
215
|
+
- spec/contentful/scheduler/tasks/unpublish_spec.rb
|
214
216
|
- spec/contentful/scheduler_spec.rb
|
215
217
|
- spec/spec_helper.rb
|
216
218
|
homepage: https://www.contentful.com
|
@@ -242,5 +244,6 @@ test_files:
|
|
242
244
|
- spec/contentful/scheduler/controller_spec.rb
|
243
245
|
- spec/contentful/scheduler/queue_spec.rb
|
244
246
|
- spec/contentful/scheduler/tasks/publish_spec.rb
|
247
|
+
- spec/contentful/scheduler/tasks/unpublish_spec.rb
|
245
248
|
- spec/contentful/scheduler_spec.rb
|
246
249
|
- spec/spec_helper.rb
|