{"id":15143808,"url":"https://github.com/contentful/contentful-scheduler.rb","last_synced_at":"2025-09-29T12:31:18.630Z","repository":{"id":66184181,"uuid":"53047704","full_name":"contentful/contentful-scheduler.rb","owner":"contentful","description":"Scheduling Server for Contentful entries","archived":true,"fork":false,"pushed_at":"2019-11-06T15:40:33.000Z","size":52,"stargazers_count":15,"open_issues_count":0,"forks_count":8,"subscribers_count":33,"default_branch":"master","last_synced_at":"2024-09-27T10:04:18.434Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/contentful.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-03-03T12:13:03.000Z","updated_at":"2023-01-28T16:43:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"a5be462a-5dcd-4e9c-a047-d4fc8ba26d4d","html_url":"https://github.com/contentful/contentful-scheduler.rb","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-scheduler.rb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-scheduler.rb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-scheduler.rb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-scheduler.rb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/contentful","download_url":"https://codeload.github.com/contentful/contentful-scheduler.rb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234619472,"owners_count":18861456,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-26T10:04:22.993Z","updated_at":"2025-09-29T12:31:13.350Z","avatar_url":"https://github.com/contentful.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Contentful Scheduler\n\n\u003e This gem has been superseded by a product feature. You can read more here: https://www.contentful.com/blog/2019/10/22/content-operations-easier-scheduled-publishing-comments-tasks/\n\nScheduling Server for Contentful entries.\n\n## Contentful\n[Contentful](https://www.contentful.com) provides a content infrastructure for digital teams to power content in websites, apps, and devices. Unlike a CMS, Contentful was built to integrate with the modern software stack. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enable developers and content creators to ship digital products faster.\n\n## What does `contentful-scheduler` do?\nThe aim of `contentful-scheduler` is to have developers setting up their Contentful\nentries for scheduled publishing and unpublishing.\n\n## How does it work\n`contentful-scheduler` provides a web endpoint to receive webhook calls from Contentful.\n\nEvery time the endpoint recieves a call it looks for the value of the field defined in the configuration.\nIf the value is a time in the future it will schedule the entry for publishing or unpublishing at the specified time.\n\nA background worker based on the popular `resque` gem will then proceed to actually make the publish call\nagainst the Content Management API at the due time. For this the Entries you wish to publish or unpublish require a\ncustomizable Date field, which we advice to call `publishDate` for the publishing action and `unpublishDate` for the unpublishing action,\nthis field can be configured inside your `Rakefile` and is specific per-space.\n\nYou can add multiple spaces to your configuration, making it useful if you have a milti-space setup.\n\n## Requirements\n\n* [Redis](http://redis.io/)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'contentful-scheduler'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install contentful-scheduler\n\n## Usage\n\nThe best way to use Scheduler is as a stand-alone application that wraps Scheduler and Resque on an execution pipe using [Foreman](http://ddollar.github.io/foreman/).\n\nYou can get the template for this setup in the [`/example`](./example) directory.\n\nIf you want to roll out your own, you need to follow the next steps:\n\n* Create a new folder\n* Create a `Gemfile` with the following:\n\n```ruby\nsource 'https://rubygems.org'\n\ngem 'contentful-scheduler', '~\u003e 0.1'\ngem 'contentful-management', '~\u003e 1.0'\ngem 'resque', '~\u003e 1.0'\ngem 'resque-scheduler', '~\u003e 4.0'\ngem 'rake'\n```\n\n* Create a `Procfile` with the following:\n\n```\nweb: env bundle exec rake contentful:scheduler\nmonitor: env bundle exec rackup\nresque: env bundle exec rake resque:work\nresque_scheduler: env bundle exec rake resque:scheduler\n```\n\n* Create a `Rakefile` with the following:\n\n```ruby\nrequire 'contentful/scheduler'\n\n$stdout.sync = true\n\nconfig = {\n  logger: Logger.new(STDOUT), # Defaults to NullLogger\n  port: 32123, # Defaults to 32123\n  endpoint: '/scheduler', # Defaults to /scheduler\n  redis: {\n    host: 'YOUR_REDIS_HOST',\n    port: 'YOUR_REDIS_PORT',\n    password: 'YOUR_REDIS_PASSWORD'\n  },\n  spaces: {\n    'YOUR_SPACE_ID' =\u003e {\n      publish_field: 'publishDate', # It specifies the field ID for your Publish Date in your Content Type\n      unpublish_field: 'unpublishDate', # Optional - It specifies the field ID for your Unpublish Date in your Content Type\n      management_token: 'YOUR_TOKEN',\n      auth: { # This is optional\n        # ... content in this section will be explained in a separate section ...\n      }\n    }\n  },\n}\n\nnamespace :contentful do\n  task :setup do\n    Contentful::Scheduler.config = config\n  end\n\n  task :scheduler =\u003e :setup do\n    Contentful::Scheduler.start\n  end\nend\n\nrequire 'resque/tasks'\nrequire 'resque/scheduler/tasks'\n\nnamespace :resque do\n  task :setup =\u003e 'contentful:setup' do\n    ENV['QUEUE'] = '*'\n  end\n\n  task :setup_schedule =\u003e :setup do\n    require 'resque-scheduler'\n  end\n\n  task :scheduler =\u003e :setup_schedule\nend\n```\n\n* Create a `config.ru` with the following for the Resque monitoring server:\n\n```ruby\nrequire 'resque'\nrequire 'resque/server'\nrequire 'resque/scheduler/server'\n\nconfig = {\n  host: 'YOUR_REDIS_HOST',\n  port: 'YOUR_REDIS_PORT',\n  password: 'YOUR_REDIS_PASSWORD'\n}\nResque.redis = config\n\nrun Rack::URLMap.new \\\n  \"/\" =\u003e Resque::Server.new\n```\n\n* Run the Application:\n\n```bash\n$ foreman start\n```\n\n* Configure the webhook in Contentful:\n\nUnder the space settings menu choose webhook and add a new webhook pointing to `http://YOUR_SERVER:32123/scheduler`.\n\nKeep in mind that if you modify the defaults, the URL should be changed to the values specified in the configuration.\n\n## Authentication\n\nYou may want to provide an additional layer of security to your scheduler server, therefore an additional option to add space based authentication is provided.\n\nThere are two available authentication methods. Static string matching and lambda validations, which will be explained in the next section.\n\nAny 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),\nor through the [CMA](https://www.contentful.com/developers/docs/references/content-management-api/#/reference/webhooks/webhook/create-update-a-webhook/console/ruby).\n\n### Authentication via static token matching\n\nThe simplest authentication mechanism, is to provide a static set of valid strings that are considered valid when found in a determined header.\n\nFor example:\n\n```ruby\nconfig = {\n  # ... the rest of the config ...\n  spaces: {\n    'my_space' =\u003e {\n      # ... the rest of the space specific configuration ...\n      auth: {\n        key: 'X-Webhook-Server-Auth-Header',\n        valid_tokens: ['some_valid_static_token']\n      }\n    }\n  }\n}\n```\n\nThe above example, whenever your webhook sends the `X-Webhook-Server-Auth-Header` with a value of `some_valid_static_token`,\nit will accept the request and queue your webhook for processing.\n\nYou can provide multiple or a single token. If a single token is provided, it's not necessary to include it in an array.\n\n### Authentication via lambda\n\nA more complicated solution, but far more secure, is the ability to execute a lambda as the validator function.\nThis allows you define a function for authentication. This function can call an external authentication service,\nmake checks against a database or do internal processing.\n\nThe function must return a truthy/falsey value in order for the authentication to be successful/unsuccessful.\n\nFor example, we validate that the token provided is either `foo` or `bar`:\n\n```ruby\nconfig = {\n  # ... the rest of the config ...\n  spaces: {\n    'my_space' =\u003e {\n      # ... the rest of the space specific configuration ...\n      auth: {\n        key: 'X-Webhook-Server-Auth-Header',\n        validation: -\u003e (value) { /^(foo|bar)$/ =~ value }\n      }\n    }\n  }\n}\n```\n\nOr a more complicated example, checking if the header is a valid OAuth token, and then making a request to our OAuth database.\nFor this example we'll consider you have a table called `tokens` and are using [DataMapper](https://datamapper.org) as a ORM,\nand have a `valid?` method checking if the token is not expired.\n\n```ruby\nconfig = {\n  # ... the rest of the config ...\n  spaces: {\n    'my_space' =\u003e {\n      # ... the rest of the space specific configuration ...\n      auth: {\n        key: 'X-Webhook-Server-Auth-Header',\n        validation: proc do |value|\n          return false unless /^Bearer \\w+/ =~ value\n\n          token = Token.first(token: value.gsub('Bearer ', ''))\n\n          return false if token.nil?\n\n          token.valid?\n        end\n      }\n    }\n  }\n}\n```\n\nIf you have multiple spaces and all share the same auth strategy, you can extract the authentication method to a variable,\nand assign it to all the applicable spaces in order to reduce the code duplication.\n\n## Running in Heroku\n\nHeroku offers various Redis plugins, select the one of your liking, add the credentials into your configuration, and proceed to\n`git heroku push master`.\n\nThis will get your application set up and running. It will require 4 dynos, so a free plan isn't enough for it to run.\n\nTo run the `monitor` process, you'll require to run it from a different application pointing to the same Redis instance.\n\nMake sure to change the `Procfile`'s `web` process to the following:\n\n```\nweb: PORT=$PORT bundle exec env rake contentful:scheduler\n```\n\nThat will allow Heroku to set it's own Port according to their policy.\n\nThe URL for the webhook then will be on port 80, so you should change it to: `http://YOUR_APPLICATION/scheduler`.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/contentful/contentful-scheduler.rb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fcontentful-scheduler.rb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontentful%2Fcontentful-scheduler.rb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fcontentful-scheduler.rb/lists"}