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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f3171ffa6b6fee5eb141d60da86e954868dbfd5e
4
- data.tar.gz: 2afa33f5a560b6f330b337b10a9cd4edb2082fd1
3
+ metadata.gz: ff923429648f1e2a809b1f379f99fa592eafc40d
4
+ data.tar.gz: 4e6c728e653123254f245d3f6e780a3c986e61c1
5
5
  SHA512:
6
- metadata.gz: 91dd5e9622d174eefaccb065e09a27d40d683b978567f795ebba156679bb624aa7eb0500ae93245186eea6756519c6178861c14b8d0d1b52fa13879db62ad818
7
- data.tar.gz: ba0f65329a151445be938e9301e5710c895e0a61569afc20a83c5d2ece65864230c49bdd21de8a195a093068895db3c4af587ed59c47c3853597db625db83791
6
+ metadata.gz: eb9ecbbd1524f8da38140c54447d7014aca017a5fa73d6403f5256c82f1e8d7bafe94a43d81d3ca86a002fa5de78006446fe2a16667b6601a4c55a559f052e2b
7
+ data.tar.gz: 9f62df4f008c1f0c27bca2e1f52a532b132687038e0156e29a5a0a807c8ed70affb1ee85130e4c0b001a82597cf500717f432af74f02853fd2c9c6ae7a535d5b
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.5.0
6
+ ### Added
7
+ * Added support for scheduled unpublishing. [#10](https://github.com/contentful/contentful-scheduler.rb/issues/10)
8
+
5
9
  ## 0.4.0
6
10
  ### Fixed
7
11
  * Fixed User Agent Header to comply with specification.
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`, this field can be configured inside your
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
- return unless publishable?(webhook)
18
- remove(webhook) if in_queue?(webhook)
19
- return unless publish_is_future?(webhook)
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
- success = Resque.enqueue_at(
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
- if success
30
- logger.info "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} successfully added to queue"
31
- else
32
- logger.warn "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} couldn't be added to queue"
33
- end
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 in_queue?(webhook)
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 removed from queue"
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 removed from queue"
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 in_queue?(webhook)
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
@@ -1 +1,2 @@
1
1
  require_relative 'tasks/publish'
2
+ require_relative 'tasks/unpublish'
@@ -1,5 +1,5 @@
1
1
  module Contentful
2
2
  module Scheduler
3
- VERSION = "0.4.0"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  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 '#in_queue?' do
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.in_queue?(
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.in_queue?(
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(:remove)
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.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-02-26 00:00:00.000000000 Z
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