contentful-scheduler 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|