contentful-scheduler-custom 1.5.67 → 1.5.68
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/.gitignore +0 -2
- data/CHANGELOG.md +15 -4
- data/README.md +97 -71
- data/contentful-scheduler.gemspec +2 -2
- data/example/Gemfile +2 -3
- data/example/Procfile +2 -2
- data/example/Rakefile +8 -7
- data/example/config.ru +3 -3
- data/lib/contentful/scheduler/auth.rb +64 -0
- data/lib/contentful/scheduler/controller.rb +11 -0
- data/lib/contentful/scheduler/queue.rb +82 -93
- data/lib/contentful/scheduler/tasks/publish.rb +1 -1
- data/lib/contentful/scheduler/tasks/unpublish.rb +2 -2
- data/lib/contentful/scheduler/version.rb +1 -1
- data/spec/contentful/scheduler/auth_spec.rb +67 -0
- data/spec/contentful/scheduler/controller_spec.rb +18 -0
- data/spec/contentful/scheduler/queue_spec.rb +1 -3
- data/spec/contentful/scheduler/tasks/publish_spec.rb +2 -2
- data/spec/contentful/scheduler/tasks/unpublish_spec.rb +5 -5
- data/spec/spec_helper.rb +1 -1
- metadata +8 -6
- data/example/Dockerfile +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b23896e7b4befb25af78b9981bca9d0edb92b06a
|
4
|
+
data.tar.gz: 2393d45d988a62464cc77e0bdb06c33daf2413dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f6103151ddb2b5528ad24ece7ac9ceba192a279bca59d70528e5573d37bdbd5202ad59773b1cc3d29d3dcd338fdc78249f4721ea3530f05d66758014fcd1210
|
7
|
+
data.tar.gz: b1d756756538ea4beb14583978b48b23e58e64fe5972481d6efe21f8407c96544b7c3657aca816d80c4042f47e3ca24131782f7ad378db9f935469eb04dcd01c
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,14 +2,25 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
-
##
|
6
|
-
|
7
|
-
|
5
|
+
## 0.5.0
|
6
|
+
### Added
|
7
|
+
* Added support for scheduled unpublishing. [#10](https://github.com/contentful/contentful-scheduler.rb/issues/10)
|
8
|
+
|
9
|
+
## 0.4.0
|
10
|
+
### Fixed
|
11
|
+
* Fixed User Agent Header to comply with specification.
|
12
|
+
|
13
|
+
### Added
|
14
|
+
* Added authentication mechanisms. [#9](https://github.com/contentful/contentful-scheduler.rb/issues/9)
|
15
|
+
|
16
|
+
## 0.3.0
|
17
|
+
### Added
|
18
|
+
* Added possibility to republish already published content. [#5](https://github.com/contentful/contentful-scheduler.rb/issues/5)
|
8
19
|
|
9
20
|
## 0.2.1
|
10
21
|
|
11
22
|
### Fixed
|
12
|
-
*
|
23
|
+
* Fixed time parsing.
|
13
24
|
|
14
25
|
## 0.2.0
|
15
26
|
|
data/README.md
CHANGED
@@ -7,17 +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
|
-
`contentful-scheduler` provides a web endpoint to receive webhook calls from Contentful
|
14
|
-
|
15
|
-
|
13
|
+
`contentful-scheduler` provides a web endpoint to receive webhook calls from Contentful.
|
14
|
+
|
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 or unpublishing at the specified time.
|
17
|
+
|
16
18
|
A background worker based on the popular `resque` gem will then proceed to actually make the publish call
|
17
|
-
against the Content Management API at the due time. For this the Entries you wish to publish require a
|
18
|
-
customizable Date field, which we advice to call `publishDate
|
19
|
-
`Rakefile` and is specific per-space
|
20
|
-
as per logic contentBlocks are published first then the page is published, so if there is any contentBlocks missing publishing won't work.
|
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.
|
21
22
|
|
22
23
|
You can add multiple spaces to your configuration, making it useful if you have a milti-space setup.
|
23
24
|
|
@@ -30,7 +31,7 @@ You can add multiple spaces to your configuration, making it useful if you have
|
|
30
31
|
Add this line to your application's Gemfile:
|
31
32
|
|
32
33
|
```ruby
|
33
|
-
gem 'contentful-scheduler
|
34
|
+
gem 'contentful-scheduler'
|
34
35
|
```
|
35
36
|
|
36
37
|
And then execute:
|
@@ -39,7 +40,7 @@ And then execute:
|
|
39
40
|
|
40
41
|
Or install it yourself as:
|
41
42
|
|
42
|
-
$ gem install contentful-scheduler
|
43
|
+
$ gem install contentful-scheduler
|
43
44
|
|
44
45
|
## Usage
|
45
46
|
|
@@ -55,7 +56,7 @@ If you want to roll out your own, you need to follow the next steps:
|
|
55
56
|
```ruby
|
56
57
|
source 'https://rubygems.org'
|
57
58
|
|
58
|
-
gem 'contentful-scheduler
|
59
|
+
gem 'contentful-scheduler', '~> 0.1'
|
59
60
|
gem 'contentful-management', '~> 1.0'
|
60
61
|
gem 'resque', '~> 1.0'
|
61
62
|
gem 'resque-scheduler', '~> 4.0'
|
@@ -90,15 +91,11 @@ config = {
|
|
90
91
|
spaces: {
|
91
92
|
'YOUR_SPACE_ID' => {
|
92
93
|
publish_field: 'publishDate', # It specifies the field ID for your Publish Date in your Content Type
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
},
|
99
|
-
'YOUR_SPACE_ID' => {
|
100
|
-
publish_field: 'publishDate', # It specifies the field ID for your Publish Date in your Content Type
|
101
|
-
management_token: 'YOUR_TOKEN'
|
94
|
+
unpublish_field: 'unpublishDate', # Optional - It specifies the field ID for your Unpublish Date in your Content Type
|
95
|
+
management_token: 'YOUR_TOKEN',
|
96
|
+
auth: { # This is optional
|
97
|
+
# ... content in this section will be explained in a separate section ...
|
98
|
+
}
|
102
99
|
}
|
103
100
|
},
|
104
101
|
}
|
@@ -159,85 +156,114 @@ Under the space settings menu choose webhook and add a new webhook pointing to `
|
|
159
156
|
|
160
157
|
Keep in mind that if you modify the defaults, the URL should be changed to the values specified in the configuration.
|
161
158
|
|
162
|
-
##
|
159
|
+
## Authentication
|
163
160
|
|
164
|
-
|
165
|
-
`git heroku push master`.
|
161
|
+
You may want to provide an additional layer of security to your scheduler server, therefore an additional option to add space based authentication is provided.
|
166
162
|
|
167
|
-
|
163
|
+
There are two available authentication methods. Static string matching and lambda validations, which will be explained in the next section.
|
168
164
|
|
169
|
-
|
165
|
+
Any of both mechanisms require you to add additional headers to your webhook set up, which can be done through the [Contentful Web App](https://app.contentful.com),
|
166
|
+
or through the [CMA](https://www.contentful.com/developers/docs/references/content-management-api/#/reference/webhooks/webhook/create-update-a-webhook/console/ruby).
|
170
167
|
|
171
|
-
|
168
|
+
### Authentication via static token matching
|
172
169
|
|
173
|
-
|
174
|
-
web: PORT=$PORT bundle exec env rake contentful:scheduler
|
175
|
-
```
|
170
|
+
The simplest authentication mechanism, is to provide a static set of valid strings that are considered valid when found in a determined header.
|
176
171
|
|
177
|
-
|
172
|
+
For example:
|
178
173
|
|
179
|
-
|
174
|
+
```ruby
|
175
|
+
config = {
|
176
|
+
# ... the rest of the config ...
|
177
|
+
spaces: {
|
178
|
+
'my_space' => {
|
179
|
+
# ... the rest of the space specific configuration ...
|
180
|
+
auth: {
|
181
|
+
key: 'X-Webhook-Server-Auth-Header',
|
182
|
+
valid_tokens: ['some_valid_static_token']
|
183
|
+
}
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
```
|
180
188
|
|
181
|
-
|
189
|
+
The above example, whenever your webhook sends the `X-Webhook-Server-Auth-Header` with a value of `some_valid_static_token`,
|
190
|
+
it will accept the request and queue your webhook for processing.
|
182
191
|
|
183
|
-
|
192
|
+
You can provide multiple or a single token. If a single token is provided, it's not necessary to include it in an array.
|
184
193
|
|
185
|
-
|
194
|
+
### Authentication via lambda
|
186
195
|
|
187
|
-
|
196
|
+
A more complicated solution, but far more secure, is the ability to execute a lambda as the validator function.
|
197
|
+
This allows you define a function for authentication. This function can call an external authentication service,
|
198
|
+
make checks against a database or do internal processing.
|
188
199
|
|
189
|
-
|
200
|
+
The function must return a truthy/falsey value in order for the authentication to be successful/unsuccessful.
|
190
201
|
|
191
|
-
|
192
|
-
[foreman export] writing: stp-web@.service
|
193
|
-
[foreman export] creating: stp-web.target.wants
|
194
|
-
[foreman export] symlinking: stp-web.target.wants/stp-web@5000.service -> ../stp-web@.service
|
195
|
-
[foreman export] writing: stp-web.target
|
196
|
-
[foreman export] writing: stp-celery@.service
|
197
|
-
[foreman export] creating: stp-celery.target.wants
|
198
|
-
[foreman export] symlinking: stp-celery.target.wants/stp-celery@5100.service -> ../stp-celery@.service
|
199
|
-
[foreman export] writing: stp-celery.target
|
200
|
-
[foreman export] writing: stp.target
|
202
|
+
For example, we validate that the token provided is either `foo` or `bar`:
|
201
203
|
|
202
|
-
|
204
|
+
```ruby
|
205
|
+
config = {
|
206
|
+
# ... the rest of the config ...
|
207
|
+
spaces: {
|
208
|
+
'my_space' => {
|
209
|
+
# ... the rest of the space specific configuration ...
|
210
|
+
auth: {
|
211
|
+
key: 'X-Webhook-Server-Auth-Header',
|
212
|
+
validation: -> (value) { /^(foo|bar)$/ =~ value }
|
213
|
+
}
|
214
|
+
}
|
215
|
+
}
|
216
|
+
}
|
217
|
+
```
|
203
218
|
|
204
|
-
|
219
|
+
Or a more complicated example, checking if the header is a valid OAuth token, and then making a request to our OAuth database.
|
220
|
+
For this example we'll consider you have a table called `tokens` and are using [DataMapper](https://datamapper.org) as a ORM,
|
221
|
+
and have a `valid?` method checking if the token is not expired.
|
205
222
|
|
206
|
-
|
207
|
-
|
223
|
+
```ruby
|
224
|
+
config = {
|
225
|
+
# ... the rest of the config ...
|
226
|
+
spaces: {
|
227
|
+
'my_space' => {
|
228
|
+
# ... the rest of the space specific configuration ...
|
229
|
+
auth: {
|
230
|
+
key: 'X-Webhook-Server-Auth-Header',
|
231
|
+
validation: proc do |value|
|
232
|
+
return false unless /^Bearer \w+/ =~ value
|
208
233
|
|
209
|
-
|
210
|
-
User=username_to_start_from
|
211
|
-
WorkingDirectory=/srv/stp/
|
212
|
-
Environment=PORT=%i
|
213
|
-
... HERE OTHER DIRICTIVES FROM YOUR .env file ...
|
214
|
-
Environment=DEBUG=0
|
215
|
-
...
|
234
|
+
token = Token.first(token: value.gsub('Bearer ', ''))
|
216
235
|
|
236
|
+
return false if token.nil?
|
217
237
|
|
218
|
-
|
238
|
+
token.valid?
|
239
|
+
end
|
240
|
+
}
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
```
|
219
245
|
|
220
|
-
|
246
|
+
If you have multiple spaces and all share the same auth strategy, you can extract the authentication method to a variable,
|
247
|
+
and assign it to all the applicable spaces in order to reduce the code duplication.
|
221
248
|
|
222
|
-
|
249
|
+
## Running in Heroku
|
223
250
|
|
224
|
-
|
251
|
+
Heroku offers various Redis plugins, select the one of your liking, add the credentials into your configuration, and proceed to
|
252
|
+
`git heroku push master`.
|
225
253
|
|
226
|
-
|
254
|
+
This will get your application set up and running. It will require 4 dynos, so a free plan isn't enough for it to run.
|
227
255
|
|
228
|
-
|
256
|
+
To run the `monitor` process, you'll require to run it from a different application pointing to the same Redis instance.
|
229
257
|
|
230
|
-
|
258
|
+
Make sure to change the `Procfile`'s `web` process to the following:
|
231
259
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
7. Optional
|
260
|
+
```
|
261
|
+
web: PORT=$PORT bundle exec env rake contentful:scheduler
|
262
|
+
```
|
237
263
|
|
238
|
-
|
239
|
-
e.g. 1000, 2000, 3000, 45000 etc.
|
264
|
+
That will allow Heroku to set it's own Port according to their policy.
|
240
265
|
|
266
|
+
The URL for the webhook then will be on port 80, so you should change it to: `http://YOUR_APPLICATION/scheduler`.
|
241
267
|
|
242
268
|
## Contributing
|
243
269
|
|
@@ -21,8 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_runtime_dependency "contentful-webhook-listener", "~> 0.2"
|
23
23
|
spec.add_runtime_dependency "contentful-management", "~> 1.8"
|
24
|
-
spec.add_runtime_dependency "resque", "~> 1.
|
25
|
-
spec.add_runtime_dependency "resque-scheduler", "~> 4.
|
24
|
+
spec.add_runtime_dependency "resque", "~> 1.0"
|
25
|
+
spec.add_runtime_dependency "resque-scheduler", "~> 4.0"
|
26
26
|
spec.add_runtime_dependency "redis", "~> 3.0"
|
27
27
|
spec.add_runtime_dependency "chronic", "~> 0.10"
|
28
28
|
|
data/example/Gemfile
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'contentful-scheduler
|
3
|
+
gem 'contentful-scheduler', '~> 0.1'
|
4
4
|
gem 'contentful-management', '~> 1.0'
|
5
5
|
gem 'resque', '~> 1.0'
|
6
|
-
gem 'resque-scheduler', '4.
|
6
|
+
gem 'resque-scheduler', '~> 4.0'
|
7
7
|
gem 'rake'
|
8
|
-
gem 'foreman', '~> 0.84.0'
|
data/example/Procfile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
web:
|
1
|
+
web: env bundle exec rake contentful:scheduler
|
2
2
|
monitor: env bundle exec rackup
|
3
3
|
resque: env bundle exec rake resque:work
|
4
|
-
resque_scheduler: env bundle exec rake resque:scheduler
|
4
|
+
resque_scheduler: env bundle exec rake resque:scheduler
|
data/example/Rakefile
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
require 'contentful/scheduler'
|
2
|
+
require 'logger' # Optional
|
2
3
|
|
3
4
|
$stdout.sync = true
|
4
5
|
|
5
6
|
config = {
|
6
7
|
logger: Logger.new(STDOUT), # Defaults to NullLogger
|
7
|
-
port:
|
8
|
+
port: 32123, # Defaults to 32123
|
8
9
|
endpoint: '/scheduler', # Defaults to /scheduler
|
9
10
|
redis: {
|
10
|
-
host: '
|
11
|
-
port: '
|
12
|
-
password: '
|
11
|
+
host: 'YOUR_REDIS_HOST',
|
12
|
+
port: 'YOUR_REDIS_PORT',
|
13
|
+
password: 'YOUR_REDIS_PASSWORD'
|
13
14
|
},
|
14
15
|
spaces: {
|
15
|
-
'
|
16
|
+
'YOUR_SPACE_ID' => {
|
16
17
|
publish_field: 'publishDate', # It specifies the field ID for your Publish Date in your Content Type
|
17
|
-
management_token: '
|
18
|
+
management_token: 'YOUR_TOKEN'
|
18
19
|
}
|
19
|
-
}
|
20
|
+
}
|
20
21
|
}
|
21
22
|
|
22
23
|
namespace :contentful do
|
data/example/config.ru
CHANGED
@@ -3,9 +3,9 @@ require 'resque/server'
|
|
3
3
|
require 'resque/scheduler/server'
|
4
4
|
|
5
5
|
config = {
|
6
|
-
host: '
|
7
|
-
port: '
|
8
|
-
password: '
|
6
|
+
host: 'YOUR_REDIS_HOST',
|
7
|
+
port: 'YOUR_REDIS_PORT',
|
8
|
+
password: 'YOUR_REDIS_PASSWORD'
|
9
9
|
}
|
10
10
|
Resque.redis = config
|
11
11
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Contentful
|
2
|
+
module Scheduler
|
3
|
+
class Auth
|
4
|
+
attr_reader :webhook
|
5
|
+
|
6
|
+
def initialize(webhook)
|
7
|
+
@webhook = webhook
|
8
|
+
end
|
9
|
+
|
10
|
+
def auth
|
11
|
+
return true if auth_config.nil?
|
12
|
+
|
13
|
+
return verify_key_value_config if key_value_config?
|
14
|
+
return verify_lambda_config if lambda_config?
|
15
|
+
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def key_value_config?
|
22
|
+
auth_config.key?(:key) && auth_config.key?(:valid_tokens)
|
23
|
+
end
|
24
|
+
|
25
|
+
def verify_key_value_config
|
26
|
+
value = webhook.raw_headers[auth_config[:key]]
|
27
|
+
|
28
|
+
return false if value.nil?
|
29
|
+
|
30
|
+
valid_tokens = auth_config[:valid_tokens]
|
31
|
+
|
32
|
+
return valid_tokens.include?(value) if valid_tokens.is_a?(::Array)
|
33
|
+
valid_tokens == value
|
34
|
+
end
|
35
|
+
|
36
|
+
def lambda_config?
|
37
|
+
auth_config.key?(:key) && auth_config.key?(:validation)
|
38
|
+
end
|
39
|
+
|
40
|
+
def verify_lambda_config
|
41
|
+
value = webhook.raw_headers[auth_config[:key]]
|
42
|
+
|
43
|
+
return false if value.nil?
|
44
|
+
|
45
|
+
validation = auth_config[:validation]
|
46
|
+
|
47
|
+
return false unless validation.is_a?(::Proc)
|
48
|
+
|
49
|
+
validation[value]
|
50
|
+
end
|
51
|
+
|
52
|
+
def auth_config
|
53
|
+
::Contentful::Scheduler.config
|
54
|
+
.fetch(:spaces, {})
|
55
|
+
.fetch(space_id, {})
|
56
|
+
.fetch(:auth, nil)
|
57
|
+
end
|
58
|
+
|
59
|
+
def space_id
|
60
|
+
webhook.space_id
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'contentful/webhook/listener'
|
2
|
+
require_relative 'auth'
|
2
3
|
require_relative 'queue'
|
3
4
|
|
4
5
|
module Contentful
|
@@ -7,6 +8,11 @@ module Contentful
|
|
7
8
|
def create
|
8
9
|
return unless webhook.entry?
|
9
10
|
|
11
|
+
if !Auth.new(webhook).auth
|
12
|
+
logger.warn "Skipping - Authentication failed for Space: #{webhook.space_id} - Entry: #{webhook.id}"
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
10
16
|
logger.info "Queueing - Space: #{webhook.space_id} - Entry: #{webhook.id}"
|
11
17
|
|
12
18
|
Queue.instance(logger).update_or_create(webhook)
|
@@ -18,6 +24,11 @@ module Contentful
|
|
18
24
|
def delete
|
19
25
|
return unless webhook.entry?
|
20
26
|
|
27
|
+
if !Auth.new(webhook).auth
|
28
|
+
logger.warn "Skipping - Authentication failed for Space: #{webhook.space_id} - Entry: #{webhook.id}"
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
21
32
|
logger.info "Unqueueing - Space: #{webhook.space_id} - Entry: #{webhook.id}"
|
22
33
|
|
23
34
|
Queue.instance(logger).remove(webhook)
|
@@ -21,11 +21,10 @@ module Contentful
|
|
21
21
|
end
|
22
22
|
|
23
23
|
if unpublishable?(webhook)
|
24
|
-
success = update_or_create_for_unpublish(webhook)
|
24
|
+
success = update_or_create_for_unpublish(webhook) && success
|
25
25
|
updateContentBlocks(webhook, 'unpublish')
|
26
26
|
log_event_success(webhook, success, 'unpublish', 'added to')
|
27
27
|
end
|
28
|
-
|
29
28
|
end
|
30
29
|
|
31
30
|
def update_or_create_for_publish(webhook)
|
@@ -55,17 +54,14 @@ module Contentful
|
|
55
54
|
end
|
56
55
|
|
57
56
|
def remove(webhook)
|
58
|
-
remove_unpublish(webhook)
|
59
57
|
remove_publish(webhook)
|
58
|
+
remove_unpublish(webhook)
|
60
59
|
end
|
61
60
|
|
62
61
|
def remove_publish(webhook)
|
63
|
-
logger.info "In publish remove block #{publishable?(webhook)} with #{in_publish_queue?(webhook)}"
|
64
62
|
return unless publishable?(webhook)
|
65
63
|
return unless in_publish_queue?(webhook)
|
66
64
|
|
67
|
-
logger.info "before before publish content block remove block"
|
68
|
-
|
69
65
|
success = Resque.remove_delayed(
|
70
66
|
::Contentful::Scheduler::Tasks::Publish,
|
71
67
|
webhook.space_id,
|
@@ -73,67 +69,50 @@ module Contentful
|
|
73
69
|
::Contentful::Scheduler.config[:management_token]
|
74
70
|
)
|
75
71
|
|
76
|
-
logger.info "before publish content block remove block"
|
77
|
-
|
78
72
|
removeContentBlocks(webhook, 'publish')
|
79
73
|
|
80
74
|
log_event_success(webhook, success, 'publish', 'removed from')
|
81
75
|
end
|
82
76
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
77
|
+
def remove_unpublish(webhook)
|
78
|
+
return unless unpublishable?(webhook)
|
79
|
+
return unless in_unpublish_queue?(webhook)
|
80
|
+
|
81
|
+
success = Resque.remove_delayed(
|
82
|
+
::Contentful::Scheduler::Tasks::Unpublish,
|
83
|
+
webhook.space_id,
|
84
|
+
webhook.id,
|
85
|
+
::Contentful::Scheduler.config[:management_token]
|
86
|
+
)
|
87
|
+
|
88
|
+
removeContentBlocks(webhook, 'unpublish')
|
89
|
+
|
90
|
+
log_event_success(webhook, success, 'unpublish', 'removed from')
|
100
91
|
end
|
101
92
|
|
102
|
-
def
|
103
|
-
if
|
104
|
-
|
93
|
+
def log_event_success(webhook, success, event_kind, action)
|
94
|
+
if success
|
95
|
+
logger.info "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} successfully #{action} the #{event_kind} queue"
|
105
96
|
else
|
106
|
-
|
97
|
+
logger.warn "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} couldn't be #{action} the #{event_kind} queue"
|
107
98
|
end
|
108
99
|
end
|
109
100
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
webhook.space_id,
|
116
|
-
sys['sys']['id'],
|
117
|
-
::Contentful::Scheduler.config[:management_token]
|
118
|
-
)
|
119
|
-
if success
|
120
|
-
logger.info "Webhook Content Block {id: #{sys['sys']['id']}, space_id: #{webhook.space_id}} successfully removed from " + type + " queue"
|
121
|
-
else
|
122
|
-
logger.warn "Webhook Content Block {id: #{sys['sys']['id']}, space_id: #{webhook.space_id}} couldn't be removed from " + type + " queue"
|
123
|
-
end
|
124
|
-
end
|
101
|
+
def publishable?(webhook)
|
102
|
+
return false unless spaces.key?(webhook.space_id)
|
103
|
+
|
104
|
+
if webhook_publish_field?(webhook)
|
105
|
+
return !webhook_publish_field(webhook).nil? && publish_is_future?(webhook)
|
125
106
|
end
|
126
|
-
end
|
127
107
|
|
128
|
-
|
129
|
-
return !webhook.fields['contentBlocks'].nil?
|
108
|
+
false
|
130
109
|
end
|
131
110
|
|
132
|
-
def
|
111
|
+
def unpublishable?(webhook)
|
133
112
|
return false unless spaces.key?(webhook.space_id)
|
134
113
|
|
135
|
-
if
|
136
|
-
return !
|
114
|
+
if webhook_unpublish_field?(webhook)
|
115
|
+
return !webhook_unpublish_field(webhook).nil? && unpublish_is_future?(webhook)
|
137
116
|
end
|
138
117
|
|
139
118
|
false
|
@@ -143,53 +122,22 @@ module Contentful
|
|
143
122
|
publish_date(webhook) > Time.now.utc
|
144
123
|
end
|
145
124
|
|
125
|
+
def unpublish_is_future?(webhook)
|
126
|
+
unpublish_date(webhook) > Time.now.utc
|
127
|
+
end
|
128
|
+
|
146
129
|
def in_publish_queue?(webhook)
|
147
130
|
Resque.peek(::Contentful::Scheduler::Tasks::Publish, 0, -1).any? do |job|
|
148
131
|
job['args'][0] == webhook.space_id && job['args'][1] == webhook.id
|
149
132
|
end
|
150
133
|
end
|
151
134
|
|
152
|
-
def remove_unpublish(webhook)
|
153
|
-
logger.info "In unpublish remove block #{unpublishable?(webhook)} with #{in_unpublish_queue?(webhook)}"
|
154
|
-
return unless unpublishable?(webhook)
|
155
|
-
return unless in_unpublish_queue?(webhook)
|
156
|
-
|
157
|
-
logger.info "before before unpublish content block remove block"
|
158
|
-
|
159
|
-
success = Resque.remove_delayed(
|
160
|
-
::Contentful::Scheduler::Tasks::Unpublish,
|
161
|
-
webhook.space_id,
|
162
|
-
webhook.id,
|
163
|
-
::Contentful::Scheduler.config[:management_token]
|
164
|
-
)
|
165
|
-
|
166
|
-
logger.info "before unpublish content block remove block"
|
167
|
-
|
168
|
-
removeContentBlocks(webhook, 'unpublish')
|
169
|
-
|
170
|
-
log_event_success(webhook, success, 'unpublish', 'removed from')
|
171
|
-
end
|
172
|
-
|
173
135
|
def in_unpublish_queue?(webhook)
|
174
136
|
Resque.peek(::Contentful::Scheduler::Tasks::Unpublish, 0, -1).any? do |job|
|
175
137
|
job['args'][0] == webhook.space_id && job['args'][1] == webhook.id
|
176
138
|
end
|
177
139
|
end
|
178
140
|
|
179
|
-
def unpublish_is_future?(webhook)
|
180
|
-
unpublish_date(webhook) > Time.now.utc
|
181
|
-
end
|
182
|
-
|
183
|
-
def unpublishable?(webhook)
|
184
|
-
return false unless spaces.key?(webhook.space_id)
|
185
|
-
|
186
|
-
if webhook_unpublish_field?(webhook)
|
187
|
-
return !webhook_unpublish_field(webhook).nil? && unpublish_is_future?(webhook)
|
188
|
-
end
|
189
|
-
|
190
|
-
false
|
191
|
-
end
|
192
|
-
|
193
141
|
def publish_date(webhook)
|
194
142
|
date_field = webhook_publish_field(webhook)
|
195
143
|
date_field = date_field[date_field.keys[0]] if date_field.is_a? Hash
|
@@ -202,6 +150,55 @@ module Contentful
|
|
202
150
|
Chronic.parse(date_field).utc
|
203
151
|
end
|
204
152
|
|
153
|
+
def updateContentBlocks(webhook, type)
|
154
|
+
if isContentBlockAvailable(webhook)
|
155
|
+
webhook.fields['contentBlocks']['fi-FI'].each do |sys|
|
156
|
+
success = Resque.enqueue_at(
|
157
|
+
publish_date(webhook),
|
158
|
+
getScheduleType(type),
|
159
|
+
webhook.space_id,
|
160
|
+
sys['sys']['id'],
|
161
|
+
::Contentful::Scheduler.config[:spaces][webhook.space_id][:management_token]
|
162
|
+
)
|
163
|
+
if success
|
164
|
+
logger.info "Webhook Content block {id: #{sys['sys']['id']}, space_id: #{webhook.space_id}} successfully added to " + type + " queue"
|
165
|
+
else
|
166
|
+
logger.warn "Webhook Content block {id: #{sys['sys']['id']}, space_id: #{webhook.space_id}} couldn't be added to " + type + " queue"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def removeContentBlocks(webhook, type)
|
173
|
+
if isContentBlockAvailable(webhook)
|
174
|
+
webhook.fields['contentBlocks']['fi-FI'].each do |sys|
|
175
|
+
success = Resque.remove_delayed(
|
176
|
+
getScheduleType(type),
|
177
|
+
webhook.space_id,
|
178
|
+
sys['sys']['id'],
|
179
|
+
::Contentful::Scheduler.config[:management_token]
|
180
|
+
)
|
181
|
+
if success
|
182
|
+
logger.info "Webhook Content Block {id: #{sys['sys']['id']}, space_id: #{webhook.space_id}} successfully removed from " + type + " queue"
|
183
|
+
else
|
184
|
+
logger.warn "Webhook Content Block {id: #{sys['sys']['id']}, space_id: #{webhook.space_id}} couldn't be removed from " + type + " queue"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def isContentBlockAvailable(webhook)
|
191
|
+
return !webhook.fields['contentBlocks'].nil?
|
192
|
+
end
|
193
|
+
|
194
|
+
def getScheduleType(type)
|
195
|
+
if type == 'unpublish'
|
196
|
+
return ::Contentful::Scheduler::Tasks::Unpublish
|
197
|
+
else
|
198
|
+
return ::Contentful::Scheduler::Tasks::Publish
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
205
202
|
def spaces
|
206
203
|
config[:spaces]
|
207
204
|
end
|
@@ -222,14 +219,6 @@ module Contentful
|
|
222
219
|
webhook.fields[spaces[webhook.space_id][:unpublish_field]]
|
223
220
|
end
|
224
221
|
|
225
|
-
def log_event_success(webhook, success, event_kind, action)
|
226
|
-
if success
|
227
|
-
logger.info "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} successfully #{action} the #{event_kind} queue"
|
228
|
-
else
|
229
|
-
logger.warn "Webhook {id: #{webhook.id}, space_id: #{webhook.space_id}} couldn't be #{action} the #{event_kind} queue"
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
222
|
private
|
234
223
|
|
235
224
|
def initialize(logger)
|
@@ -10,7 +10,7 @@ module Contentful
|
|
10
10
|
client = ::Contentful::Management::Client.new(
|
11
11
|
token,
|
12
12
|
raise_errors: true,
|
13
|
-
application_name: 'contentful
|
13
|
+
application_name: 'contentful.scheduler',
|
14
14
|
application_version: Contentful::Scheduler::VERSION
|
15
15
|
)
|
16
16
|
client.entries.find(space_id, entry_id).publish
|
@@ -10,7 +10,7 @@ module Contentful
|
|
10
10
|
client = ::Contentful::Management::Client.new(
|
11
11
|
token,
|
12
12
|
raise_errors: true,
|
13
|
-
application_name: 'contentful
|
13
|
+
application_name: 'contentful.scheduler',
|
14
14
|
application_version: Contentful::Scheduler::VERSION
|
15
15
|
)
|
16
16
|
client.entries.find(space_id, entry_id).unpublish
|
@@ -18,4 +18,4 @@ module Contentful
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Contentful::Scheduler::Auth do
|
4
|
+
before :each do
|
5
|
+
Contentful::Scheduler.config = base_config
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'auth' do
|
9
|
+
context 'when no auth is provided' do
|
10
|
+
it 'always returns true' do
|
11
|
+
webhook = WebhookDouble.new('id', 'no_auth')
|
12
|
+
expect(described_class.new(webhook).auth).to be_truthy
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when providing token array auth' do
|
17
|
+
it 'false when key not found' do
|
18
|
+
webhook = WebhookDouble.new('id', 'valid_token_array')
|
19
|
+
expect(described_class.new(webhook).auth).to be_falsey
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'false when key found but value not matched' do
|
23
|
+
webhook = WebhookDouble.new('id', 'valid_token_array', {}, {}, {'auth' => 'not_valid'})
|
24
|
+
expect(described_class.new(webhook).auth).to be_falsey
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'true when key found and value matched' do
|
28
|
+
webhook = WebhookDouble.new('id', 'valid_token_array', {}, {}, {'auth' => 'test_1'})
|
29
|
+
expect(described_class.new(webhook).auth).to be_truthy
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when providing token string auth' do
|
34
|
+
it 'false when key not found' do
|
35
|
+
webhook = WebhookDouble.new('id', 'valid_token_string')
|
36
|
+
expect(described_class.new(webhook).auth).to be_falsey
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'false when key found but value not matched' do
|
40
|
+
webhook = WebhookDouble.new('id', 'valid_token_string', {}, {}, {'auth' => 'not_valid'})
|
41
|
+
expect(described_class.new(webhook).auth).to be_falsey
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'true when key found and value matched' do
|
45
|
+
webhook = WebhookDouble.new('id', 'valid_token_string', {}, {}, {'auth' => 'test_2'})
|
46
|
+
expect(described_class.new(webhook).auth).to be_truthy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when providing lambda auth' do
|
51
|
+
it 'false when key not found' do
|
52
|
+
webhook = WebhookDouble.new('id', 'lambda_auth')
|
53
|
+
expect(described_class.new(webhook).auth).to be_falsey
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'false when key found but value not matched' do
|
57
|
+
webhook = WebhookDouble.new('id', 'lambda_auth', {}, {}, {'auth' => 'not_valid'})
|
58
|
+
expect(described_class.new(webhook).auth).to be_falsey
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'true when key found and value matched' do
|
62
|
+
webhook = WebhookDouble.new('id', 'lambda_auth', {}, {}, {'auth' => 'test'})
|
63
|
+
expect(described_class.new(webhook).auth).to be_truthy
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -9,6 +9,10 @@ describe Contentful::Scheduler::Controller do
|
|
9
9
|
let(:queue) { ::Contentful::Scheduler::Queue.instance }
|
10
10
|
subject { described_class.new server, logger, timeout }
|
11
11
|
|
12
|
+
before :each do
|
13
|
+
Contentful::Scheduler.config = base_config
|
14
|
+
end
|
15
|
+
|
12
16
|
describe 'events' do
|
13
17
|
[:create, :save, :auto_save, :unarchive].each do |event|
|
14
18
|
it "creates or updates webhook metadata in publish queue on #{event}" do
|
@@ -42,5 +46,19 @@ describe Contentful::Scheduler::Controller do
|
|
42
46
|
end
|
43
47
|
end
|
44
48
|
end
|
49
|
+
|
50
|
+
describe 'auth' do
|
51
|
+
context 'on auth failure' do
|
52
|
+
let(:body) { {sys: { id: 'invalid_auth', space: { sys: { id: 'valid_token_string' } } }, fields: {} } }
|
53
|
+
|
54
|
+
it 'will stop the queueing process' do
|
55
|
+
expect(queue).not_to receive(:update_or_create)
|
56
|
+
|
57
|
+
headers['X-Contentful-Topic'] = "ContentfulManagement.Entry.save"
|
58
|
+
request = RequestDummy.new(headers, body)
|
59
|
+
subject.respond(request, MockResponse.new).join
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
45
63
|
end
|
46
64
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
require 'contentful/management'
|
4
|
-
|
5
3
|
describe Contentful::Scheduler::Queue do
|
6
4
|
let(:config) { base_config }
|
7
5
|
subject { described_class.instance }
|
@@ -262,4 +260,4 @@ describe Contentful::Scheduler::Queue do
|
|
262
260
|
end
|
263
261
|
end
|
264
262
|
end
|
265
|
-
end
|
263
|
+
end
|
@@ -29,7 +29,7 @@ describe Contentful::Scheduler::Tasks::Publish do
|
|
29
29
|
expect(::Contentful::Management::Client).to receive(:new).with(
|
30
30
|
'foo',
|
31
31
|
raise_errors: true,
|
32
|
-
application_name: 'contentful
|
32
|
+
application_name: 'contentful.scheduler',
|
33
33
|
application_version: Contentful::Scheduler::VERSION
|
34
34
|
) { mock_client }
|
35
35
|
expect(mock_client).to receive(:entries) { mock_entries }
|
@@ -39,4 +39,4 @@ describe Contentful::Scheduler::Tasks::Publish do
|
|
39
39
|
described_class.perform('foo', 'bar', ::Contentful::Scheduler.config[:spaces]['foo'][:management_token])
|
40
40
|
end
|
41
41
|
end
|
42
|
-
end
|
42
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
class MockEntry
|
4
|
-
def
|
4
|
+
def publish
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
@@ -21,22 +21,22 @@ describe Contentful::Scheduler::Tasks::Unpublish do
|
|
21
21
|
let(:mock_entry) { MockEntry.new }
|
22
22
|
|
23
23
|
before :each do
|
24
|
-
::Contentful::Scheduler.
|
24
|
+
::Contentful::Scheduler.config = base_config
|
25
25
|
end
|
26
26
|
|
27
27
|
describe 'class methods' do
|
28
28
|
it '::perform' do
|
29
29
|
expect(::Contentful::Management::Client).to receive(:new).with(
|
30
|
-
'
|
30
|
+
'foo',
|
31
31
|
raise_errors: true,
|
32
|
-
application_name: 'contentful
|
32
|
+
application_name: 'contentful.scheduler',
|
33
33
|
application_version: Contentful::Scheduler::VERSION
|
34
34
|
) { mock_client }
|
35
35
|
expect(mock_client).to receive(:entries) { mock_entries }
|
36
36
|
expect(mock_entries).to receive(:find).with('foo', 'bar') { mock_entry }
|
37
37
|
expect(mock_entry).to receive(:unpublish)
|
38
38
|
|
39
|
-
described_class.perform('foo', 'bar', ::Contentful::Scheduler.config[:management_token])
|
39
|
+
described_class.perform('foo', 'bar', ::Contentful::Scheduler.config[:spaces]['foo'][:management_token])
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contentful-scheduler-custom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.68
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Contentful GmbH (David Litvak Bruno0
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.
|
47
|
+
version: '1.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.
|
54
|
+
version: '1.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: resque-scheduler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 4.
|
61
|
+
version: '4.0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 4.
|
68
|
+
version: '4.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: redis
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -196,18 +196,19 @@ files:
|
|
196
196
|
- README.md
|
197
197
|
- Rakefile
|
198
198
|
- contentful-scheduler.gemspec
|
199
|
-
- example/Dockerfile
|
200
199
|
- example/Gemfile
|
201
200
|
- example/Procfile
|
202
201
|
- example/Rakefile
|
203
202
|
- example/config.ru
|
204
203
|
- lib/contentful/scheduler.rb
|
204
|
+
- lib/contentful/scheduler/auth.rb
|
205
205
|
- lib/contentful/scheduler/controller.rb
|
206
206
|
- lib/contentful/scheduler/queue.rb
|
207
207
|
- lib/contentful/scheduler/tasks.rb
|
208
208
|
- lib/contentful/scheduler/tasks/publish.rb
|
209
209
|
- lib/contentful/scheduler/tasks/unpublish.rb
|
210
210
|
- lib/contentful/scheduler/version.rb
|
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
|
@@ -239,6 +240,7 @@ signing_key:
|
|
239
240
|
specification_version: 4
|
240
241
|
summary: Customizable Scheduler for Contentful Entries.
|
241
242
|
test_files:
|
243
|
+
- spec/contentful/scheduler/auth_spec.rb
|
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
|
data/example/Dockerfile
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
FROM ruby:2.5
|
2
|
-
MAINTAINER Bugs Bunny <bbunny@rubyplus.com>
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# Install gems
|
7
|
-
ENV APP_HOME /app
|
8
|
-
ENV HOME /root
|
9
|
-
RUN mkdir $APP_HOME
|
10
|
-
RUN chmod -R 777 /app
|
11
|
-
RUN chmod -R 777 /root
|
12
|
-
|
13
|
-
|
14
|
-
WORKDIR $APP_HOME
|
15
|
-
COPY Gemfile* $APP_HOME/
|
16
|
-
RUN bundle install
|
17
|
-
|
18
|
-
# Upload source
|
19
|
-
COPY . $APP_HOME
|
20
|
-
|
21
|
-
# Start server
|
22
|
-
ENV PORT 8080
|
23
|
-
EXPOSE 8080
|
24
|
-
USER 1001
|
25
|
-
CMD ["foreman", "start"]
|