├── .ci.yml ├── .ghe-bot.yml ├── .gitignore ├── .ruby-version ├── .travis.yml ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Procfile ├── README.md ├── Rakefile ├── bin ├── docker_console ├── run-tests.sh ├── sidekiq └── startup ├── config.ru ├── config ├── database.yml ├── defaults │ ├── database.yml │ ├── options.yml │ └── redis.yml ├── environment.rb ├── options.yml ├── redis.yml ├── sidekiq.yml ├── sidekiq_service.rb └── torquebox.rb ├── db ├── migrations │ ├── 001_initial_migrations.rb │ ├── 002_add_indexes_to_workflow_and_node.rb │ ├── 003_add_paused_to_workflows.rb │ ├── 004_add_complete_by_to_node_detail.rb │ ├── 005_remove_client_detail_result.rb │ ├── 006_allow_null_decision_endpoint.rb │ ├── 007_remove_current_server_status_index.rb │ ├── 008_add_parent_link_id_to_node.rb │ ├── 009_add_workflows_created_at_index.rb │ ├── 010_change_status_change_id_to_big_int.rb │ ├── 011_add_user_name_and_token.rb │ ├── 012_change_int_columns_to_big_int.rb │ ├── 013_add_nodes_workflow_id_seq_parent_id_index.rb │ └── 014_remove_foreign_keys.rb └── schema.rb ├── docker-compose.yml ├── docker ├── backbeat_user.env.example ├── common.yml ├── docker-compose.ci.yml ├── docker-compose.deploy.yml └── docker-compose.local.yml ├── documentation ├── images │ ├── backbeat_flow_payment.png │ ├── backbeat_logo.png │ ├── camera_purchase_walkthrough │ │ ├── camera_purchase_walkthrough_1.png │ │ ├── camera_purchase_walkthrough_2.png │ │ ├── create_purchase_walkthrough_3.png │ │ ├── create_purchase_walkthrough_4.png │ │ ├── create_purchase_walkthrough_5.png │ │ └── create_purchase_walkthrough_6.png │ ├── make_payment_workflow.png │ └── parallel_activities_example.png └── plantuml │ ├── Makefile │ ├── backbeat_flow_payment.txt │ └── plantuml.jar ├── lib ├── backbeat.rb └── backbeat │ ├── cache.rb │ ├── client.rb │ ├── config.rb │ ├── errors.rb │ ├── events.rb │ ├── events │ └── event.rb │ ├── instrument.rb │ ├── logging.rb │ ├── models │ ├── child_queries.rb │ ├── client_node_detail.rb │ ├── node.rb │ ├── node_detail.rb │ ├── status_change.rb │ ├── user.rb │ └── workflow.rb │ ├── presenters.rb │ ├── schedulers.rb │ ├── search │ ├── activity_search.rb │ ├── filter.rb │ └── workflow_search.rb │ ├── server.rb │ ├── state_manager.rb │ ├── util.rb │ ├── web.rb │ ├── web │ ├── activities_api.rb │ ├── debug_api.rb │ ├── helpers │ │ └── current_user_helper.rb │ ├── middleware │ │ ├── health.rb │ │ ├── heartbeat.rb │ │ ├── log.rb │ │ └── sidekiq_stats.rb │ ├── users_api.rb │ ├── versioned_api.rb │ └── workflows_api.rb │ ├── workers │ ├── async_worker.rb │ ├── daily_activity.rb │ ├── daily_activity │ │ ├── report.html.erb │ │ └── table.html.erb │ ├── heal_nodes.rb │ ├── log_queues.rb │ └── middleware │ │ └── transaction_id.rb │ ├── workflow_tree.rb │ └── workflow_tree │ └── colorize.rb ├── log └── .gitkeep ├── public └── heartbeat.txt ├── script ├── add_user.rb ├── console.rb ├── console_helpers.rb ├── inconsistent_node_fixes.rb └── seed.rb ├── spec ├── factories │ ├── node.rb │ ├── user.rb │ └── workflow.rb ├── integration │ ├── web │ │ ├── activities_api_spec.rb │ │ ├── authentication_spec.rb │ │ ├── debug_api_spec.rb │ │ ├── middleware │ │ │ ├── health_spec.rb │ │ │ ├── heartbeat_spec.rb │ │ │ ├── log_spec.rb │ │ │ └── sidekiq_stats_spec.rb │ │ ├── users_api_spec.rb │ │ └── workflows_api_spec.rb │ └── workflows │ │ ├── flags_spec.rb │ │ ├── link_nodes_spec.rb │ │ ├── mode_spec.rb │ │ ├── multi_client_spec.rb │ │ ├── pause_workflow_spec.rb │ │ ├── retry_node_spec.rb │ │ ├── send_signal_spec.rb │ │ └── timers_spec.rb ├── spec_helper.rb ├── support │ ├── capture.rb │ ├── request_helper.rb │ └── sidekiq_helper.rb └── unit │ ├── client_spec.rb │ ├── events_spec.rb │ ├── instrument_spec.rb │ ├── logging_spec.rb │ ├── models │ ├── node_detail_spec.rb │ ├── node_spec.rb │ ├── user_spec.rb │ └── workflow_spec.rb │ ├── presenters_spec.rb │ ├── schedulers_spec.rb │ ├── server_spec.rb │ ├── state_manager_spec.rb │ ├── web │ └── helpers │ │ └── current_user_helper_spec.rb │ ├── workers │ ├── async_worker_spec.rb │ ├── daily_activity_spec.rb │ ├── heal_nodes_spec.rb │ ├── log_queues_spec.rb │ └── middleware │ │ └── transaction_id_spec.rb │ └── workflow_tree_spec.rb └── torquebox_init.rb /.ci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | ci: 3 | <% if( DOTCI_BRANCH =~ /deploy.*/) { %> 4 | plugins: 5 | - publish: 'ci' 6 | <% } %> 7 | -------------------------------------------------------------------------------- /.ghe-bot.yml: -------------------------------------------------------------------------------- 1 | mergebot: 2 | hipchat_room_id: 353533 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | shared/log/unicorn.stderr.log 2 | shared/log/unicorn.stdout.log 3 | shared/pids/unicorn.pid 4 | shared/sockets/unicorn.sock 5 | coverage 6 | public/resources.json 7 | *.gem 8 | *.rbc 9 | .bundle 10 | .config 11 | InstalledFiles 12 | lib/bundler/man 13 | pkg 14 | rdoc 15 | spec/reports 16 | spec/data 17 | test/tmp 18 | test/version_tmp 19 | tmp 20 | .DS_store 21 | log/*.log 22 | docker/*.env 23 | .idea 24 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | jruby-1.7.19 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: ruby 4 | 5 | rvm: 6 | - 2.0.0 7 | - 2.1.6 8 | - 2.2.3 9 | - 2.3.0 10 | - jruby-1.7.20 11 | 12 | services: 13 | - postgresql 14 | - redis 15 | 16 | before_script: 17 | - gem install bundler 18 | - bundle install 19 | - psql -c "CREATE ROLE backbeat with SUPERUSER LOGIN PASSWORD 'backbeat';" -U postgres 20 | - RACK_ENV=test bundle exec rake db:create 21 | - RACK_ENV=test bundle exec rake db:migrate 22 | 23 | script: 24 | - bundle exec rspec spec 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jruby:1.7 2 | 3 | MAINTAINER opensource@groupon.com 4 | 5 | RUN apt-get -q update 6 | RUN apt-get -q -q -y install git 7 | 8 | RUN mkdir /app 9 | WORKDIR /app 10 | 11 | ADD Gemfile /app/ 12 | ADD Gemfile.lock /app/ 13 | RUN bundle install --without development torquebox 14 | 15 | ADD . /app 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV.fetch('GEM_SOURCE', 'https://rubygems.org') 2 | 3 | gem 'rake' 4 | 5 | gem 'grape', '~> 0.13.0' 6 | gem 'puma', '~> 2.13.4' 7 | gem 'httparty' 8 | 9 | group :torquebox do 10 | platform :jruby do 11 | gem 'jruby-openssl', :require => false 12 | gem 'torquebox', '3.0.0' 13 | gem 'torquebox-messaging', '3.0.0' 14 | gem 'warbler' 15 | gem 'torquebox-server' 16 | end 17 | end 18 | 19 | gem 'activerecord', '~> 4.1.0', require: 'active_record' 20 | platform :jruby do 21 | gem 'activerecord-jdbcpostgresql-adapter' 22 | gem 'jdbc-postgres' 23 | end 24 | platform :mri do 25 | gem 'activerecord-postgresql-adapter' 26 | end 27 | gem 'foreigner' 28 | gem 'enumerize' 29 | gem 'redis-activesupport', '~> 4.1.0' 30 | 31 | gem 'awesome_print' 32 | gem 'mail' 33 | gem 'sidekiq', '~> 3.5.0' 34 | gem 'sidekiq-failures', '~> 0.4.0' 35 | gem 'sinatra', require: false # for the Sidekiq UI 36 | gem 'sidekiq_schedulable', '~> 0.1.1' 37 | 38 | group :development, :test do 39 | gem 'pry' 40 | gem 'foreman' 41 | end 42 | 43 | group :test do 44 | gem 'database_cleaner' 45 | gem 'rack-test' 46 | gem 'rspec', '~> 3.2.0' 47 | gem 'rspec-sidekiq' 48 | gem 'factory_girl' 49 | gem 'timecop' 50 | gem 'webmock' 51 | gem 'zip' 52 | end 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Groupon, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of GROUPON nor the names of its contributors may be 16 | used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | server: bundle exec rackup 2 | sidekiq: bin/sidekiq 3 | redis: redis-server -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | 3 | if defined?(TorqueBox) 4 | require 'torquebox-rake-support' 5 | end 6 | 7 | module DB 8 | def self.config 9 | env = Backbeat::Config.environment 10 | database_config = YAML::load(IO.read('config/database.yml')) 11 | @config ||= (database_config["#{env}_dba"] || database_config[env]) 12 | end 13 | 14 | def self.with_connection(options = {}) 15 | ActiveRecord::Base.establish_connection(config.merge(options)) 16 | yield ActiveRecord::Base.connection 17 | end 18 | end 19 | 20 | task :env do 21 | require File.expand_path('../config/environment', __FILE__) 22 | end 23 | 24 | namespace :db do 25 | require "active_record" 26 | require "foreigner" 27 | 28 | desc "drops and recreates the db" 29 | task :reset => :env do 30 | DB.with_connection('database' => 'postgres') do |connection| 31 | connection.recreate_database(DB.config['database'], DB.config) 32 | end 33 | end 34 | 35 | desc "drop the db" 36 | task :drop => :env do 37 | DB.with_connection('database' => 'postgres') do |connection| 38 | connection.drop_database(DB.config['database']) 39 | end 40 | end 41 | 42 | desc "create the db" 43 | task :create => :env do 44 | DB.with_connection('database' => 'postgres') do |connection| 45 | connection.create_database(DB.config['database'], DB.config) 46 | end 47 | end 48 | 49 | desc "create the db if it doesn't already exist" 50 | task :create_if_not_exists => :env do 51 | DB.with_connection('database' => 'postgres') do |connection| 52 | connection.execute("CREATE DATABASE IF NOT EXISTS #{DB.config['database']}") 53 | end 54 | end 55 | 56 | desc "migrate the db" 57 | task :migrate => :env do 58 | DB.with_connection do |c| 59 | Foreigner.load 60 | ActiveRecord::Migrator.migrate('db/migrations', nil) 61 | end 62 | end 63 | 64 | desc "rollback one migration" 65 | task :rollback => :env do 66 | DB.with_connection do |c| 67 | Foreigner.load 68 | ActiveRecord::Migrator.rollback('db/migrations') 69 | end 70 | end 71 | 72 | desc "seed the db for development testing" 73 | task :seed => :env do 74 | return unless Backbeat::Config.environment == 'development' 75 | load File.expand_path('../script/seed.rb', __FILE__) 76 | end 77 | 78 | namespace :schema do 79 | task :dump => :env do 80 | require 'active_record/schema_dumper' 81 | DB.with_connection do |c| 82 | File.open('db/schema.rb', 'w:utf-8') do |file| 83 | ActiveRecord::SchemaDumper.dump(c, file) 84 | end 85 | end 86 | end 87 | end 88 | end 89 | 90 | namespace :app do 91 | task :routes => :env do 92 | require 'backbeat/web' 93 | 94 | Backbeat::Web::API.routes.each do |api| 95 | method = api.route_method.ljust(10) 96 | path = api.route_path 97 | puts " #{method} #{path}" 98 | end 99 | end 100 | end 101 | 102 | task :console do 103 | require_relative 'script/console' 104 | end 105 | 106 | task :add_user do 107 | require_relative 'script/add_user' 108 | end 109 | -------------------------------------------------------------------------------- /bin/docker_console: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker-compose run web /bin/bash 4 | -------------------------------------------------------------------------------- /bin/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bundle exec rake db:reset 4 | bundle exec rake db:migrate 5 | bundle exec rspec 6 | -------------------------------------------------------------------------------- /bin/sidekiq: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bundle exec sidekiq -r ./config/environment.rb 4 | -------------------------------------------------------------------------------- /bin/startup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bundle exec rake db:migrate 4 | bundle exec rake add_user 5 | 6 | webserver=$1 7 | 8 | if [[ -n "$webserver" ]]; then 9 | bundle exec $webserver 10 | else 11 | bundle exec rackup 12 | fi 13 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require File.expand_path('../config/environment', __FILE__) 2 | 3 | require 'backbeat/web' 4 | require 'sidekiq/web' 5 | 6 | class SidekiqUI < Grape::API 7 | mount Sidekiq::Web => '/sidekiq' 8 | end 9 | 10 | run Rack::Cascade.new([Backbeat::Web::App, SidekiqUI]) 11 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | common: &common 2 | reconnect: true 3 | adapter: postgresql 4 | username: backbeat 5 | password: backbeat 6 | 7 | development: 8 | <<: *common 9 | database: backbeat_dev 10 | 11 | test: 12 | <<: *common 13 | database: backbeat_test 14 | 15 | docker: &docker 16 | <<: *common 17 | reconnect: true 18 | adapter: postgresql 19 | username: backbeat 20 | password: backbeat 21 | database: backbeat_docker 22 | host: database 23 | schema_search_path: public 24 | 25 | ci: 26 | <<: *docker 27 | -------------------------------------------------------------------------------- /config/defaults/database.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | reconnect: true 3 | adapter: postgresql 4 | username: backbeat 5 | password: backbeat 6 | pool: 25 7 | -------------------------------------------------------------------------------- /config/defaults/options.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | log_level: INFO 3 | async_queue: backbeat_server 4 | log: ~ 5 | client_timeout: 172800 6 | require_auth_token: false 7 | connection_error_wait: 30 8 | alerts: 9 | email_to: ~ 10 | email_from: ~ 11 | schedules: 12 | heal_nodes: '0 */2 * * * *' 13 | daily_activity: '0 12 * * * *' 14 | log_queues: '0 */5 * * * *' 15 | reporting: 16 | intervals: ~ 17 | -------------------------------------------------------------------------------- /config/defaults/redis.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | namespace: backbeat 3 | size: 100 4 | network_timeout: 5 5 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | $: << File.expand_path('../../lib', __FILE__) 32 | 33 | require 'rubygems' 34 | require 'bundler/setup' 35 | 36 | require 'active_record' 37 | require 'sidekiq' 38 | require 'sidekiq_schedulable' 39 | require 'backbeat' 40 | 41 | puts "*** Environment is #{Backbeat::Config.environment} ***" 42 | 43 | I18n.enforce_available_locales = false 44 | 45 | ActiveRecord::Base.include_root_in_json = false 46 | ActiveRecord::Base.establish_connection(Backbeat::Config.database) 47 | 48 | Sidekiq.configure_client do |config| 49 | config.logger.level = Backbeat::Config.log_level 50 | config.redis = Backbeat::Config.redis 51 | end 52 | 53 | Sidekiq.configure_server do |config| 54 | config.redis = Backbeat::Config.redis 55 | end 56 | -------------------------------------------------------------------------------- /config/options.yml: -------------------------------------------------------------------------------- 1 | development: 2 | log: log/development.log 3 | 4 | test: 5 | log_level: DEBUG 6 | log: log/test.log 7 | require_auth_token: true 8 | 9 | docker: 10 | log: ~ 11 | 12 | ci: 13 | log: /dev/null 14 | require_auth_token: true 15 | -------------------------------------------------------------------------------- /config/redis.yml: -------------------------------------------------------------------------------- 1 | common: &common 2 | host: localhost 3 | port: 6379 4 | 5 | development: 6 | <<: *common 7 | namespace: backbeat_development 8 | 9 | test: 10 | <<: *common 11 | namespace: backbeat_test 12 | 13 | docker: &docker 14 | host: redis 15 | port: 6379 16 | 17 | ci: 18 | <<: *docker 19 | -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | :queues: 2 | - backbeat_server 3 | -------------------------------------------------------------------------------- /config/sidekiq_service.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'sidekiq' 32 | require 'sidekiq-failures' 33 | require 'celluloid' 34 | require_relative '../config/environment.rb' 35 | require 'backbeat/workers/middleware/transaction_id' 36 | 37 | module Sidekiq 38 | class Shutdown < RuntimeError; end 39 | class CLI; end 40 | end 41 | 42 | require 'celluloid/autostart' 43 | require 'sidekiq/processor' 44 | 45 | require 'sidekiq/launcher' 46 | require 'backbeat/logging' 47 | require 'backbeat/workers/middleware/transaction_id' 48 | 49 | module Services 50 | class SidekiqService 51 | include Sidekiq::Util 52 | 53 | attr_accessor :config, :launcher 54 | 55 | CONFIG_OPTIONS_TO_STRIP = ['config_file', 'daemon', 'environment', 'pidfile', 'require', 'tag', 'options'] 56 | 57 | def initialize(opts = {}) 58 | @config = opts.reject { |k, _| CONFIG_OPTIONS_TO_STRIP.include?(k) }.merge(opts['options']).symbolize_keys 59 | @mutex = Mutex.new 60 | 61 | Sidekiq.configure_server do |config| 62 | config.redis = Backbeat::Config.redis 63 | config.poll_interval = 5 64 | config.failures_max_count = false 65 | config.failures_default_mode = :exhausted 66 | config.server_middleware do |chain| 67 | chain.add Backbeat::Workers::Middleware::TransactionId 68 | end 69 | end 70 | end 71 | 72 | def start 73 | # we have to manually boot sidekiq schedulable because of a torquebox sidekiq loading issue 74 | SidekiqSchedulable.boot! 75 | fire_event(:startup) 76 | 77 | Thread.new do 78 | @mutex.synchronize { run } 79 | end 80 | end 81 | 82 | def stop 83 | @mutex.synchronize { launcher.stop } if launcher 84 | end 85 | 86 | def run 87 | Sidekiq.options.merge!(config) 88 | Sidekiq.options[:queues] = Sidekiq.options[:queues].to_a 89 | raise 'Sidekiq workers must have at least 1 queue!' if Sidekiq.options[:queues].size < 1 90 | 91 | Sidekiq.logger = Backbeat::SidekiqLogger 92 | Celluloid.logger = Backbeat::SidekiqLogger 93 | 94 | @launcher = Sidekiq::Launcher.new(Sidekiq.options) 95 | launcher.run 96 | rescue => e 97 | puts e.message 98 | puts e.backtrace 99 | end 100 | end 101 | end 102 | 103 | -------------------------------------------------------------------------------- /config/torquebox.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require File.expand_path('../environment', __FILE__) 32 | require File.expand_path('../sidekiq_service', __FILE__) 33 | Bundler.require(:torquebox) 34 | 35 | TorqueBox.configure do 36 | ruby do 37 | version '1.9' 38 | compile_mode 'jit' 39 | end 40 | 41 | web do 42 | context '/' 43 | end 44 | 45 | pool :web do 46 | type :shared 47 | lazy false 48 | end 49 | 50 | pool :services do 51 | type :bounded 52 | min 2 53 | max 2 54 | end 55 | 56 | service Services::SidekiqService do 57 | name "backbeat_sidekiq" 58 | config do 59 | queues [Backbeat::Config.options['async_queue']] 60 | strict true 61 | concurrency 15 62 | index 2 63 | options timeout: 10 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /db/migrations/001_initial_migrations.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class InitialMigrations < ActiveRecord::Migration 32 | def change 33 | enable_extension('uuid-ossp') 34 | 35 | create_table :users, id: :uuid do |t| 36 | t.string :decision_endpoint, null: false 37 | t.string :activity_endpoint, null: false 38 | t.string :notification_endpoint, null: false 39 | end 40 | 41 | create_table :workflows, id: :uuid do |t| 42 | t.string :name, null: false 43 | t.string :decider 44 | t.text :subject 45 | t.uuid :user_id, null: false 46 | t.boolean :migrated, default: false 47 | t.boolean :complete, default: false 48 | t.timestamps null: false 49 | end 50 | add_foreign_key(:workflows, :users) 51 | 52 | create_table :nodes, id: :uuid do |t| 53 | t.string :mode, null: false 54 | t.string :current_server_status, null: false 55 | t.string :current_client_status, null: false 56 | t.string :name, null: false 57 | t.datetime :fires_at 58 | t.uuid :parent_id 59 | t.uuid :workflow_id, null: false 60 | t.uuid :user_id, null: false 61 | t.timestamps null: false 62 | end 63 | execute("alter table nodes add column seq serial") 64 | add_index(:nodes, :seq, unique: true) 65 | add_index(:nodes, :workflow_id) 66 | add_index(:nodes, :parent_id) 67 | add_foreign_key(:nodes, :users) 68 | add_foreign_key(:nodes, :nodes, column: 'parent_id') 69 | 70 | create_table :client_node_details do |t| 71 | t.uuid :node_id, null: false 72 | t.text :metadata 73 | t.text :data 74 | t.text :result 75 | end 76 | add_index(:client_node_details, :node_id, unique: true) 77 | add_foreign_key(:client_node_details, :nodes) 78 | 79 | create_table :status_changes do |t| 80 | t.uuid :node_id, null: false 81 | t.string :from_status 82 | t.string :to_status 83 | t.string :status_type 84 | t.text :result 85 | t.datetime :created_at 86 | end 87 | add_index(:status_changes, :node_id, unique: false) 88 | add_foreign_key(:status_changes, :nodes) 89 | 90 | create_table :node_details do |t| 91 | t.uuid :node_id, null: false 92 | t.integer :retries_remaining, null: false 93 | t.integer :retry_interval, null: false 94 | t.string :legacy_type 95 | t.text :valid_next_events 96 | end 97 | add_index(:node_details, :node_id, unique: true) 98 | add_foreign_key(:node_details, :nodes) 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /db/migrations/002_add_indexes_to_workflow_and_node.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AddIndexesToWorkflowAndNode < ActiveRecord::Migration 32 | def change 33 | add_index(:workflows, [:subject, :name, :user_id, :decider], unique: true) 34 | add_index(:nodes, :fires_at) 35 | add_index(:nodes, :current_server_status) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /db/migrations/003_add_paused_to_workflows.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AddPausedToWorkflows < ActiveRecord::Migration 32 | def change 33 | add_column :workflows, :paused, :boolean 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /db/migrations/004_add_complete_by_to_node_detail.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AddCompleteByToNodeDetail < ActiveRecord::Migration 32 | disable_ddl_transaction! 33 | 34 | def change 35 | add_column :node_details, :complete_by, :datetime 36 | add_index :node_details, :complete_by, algorithm: :concurrently 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /db/migrations/005_remove_client_detail_result.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class RemoveClientDetailResult < ActiveRecord::Migration 32 | def up 33 | remove_column :client_node_details, :result 34 | rename_column :status_changes, :result, :response 35 | end 36 | 37 | def down 38 | add_column :client_node_details, :result, :text 39 | rename_column :status_changes, :response, :result 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /db/migrations/006_allow_null_decision_endpoint.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AllowNullDecisionEndpoint < ActiveRecord::Migration 32 | def up 33 | change_column_null :users, :decision_endpoint, true 34 | end 35 | 36 | def down 37 | change_column_null :users, :decision_endpoint, false 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /db/migrations/007_remove_current_server_status_index.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class RemoveCurrentServerStatusIndex < ActiveRecord::Migration 32 | disable_ddl_transaction! 33 | 34 | def up 35 | remove_index :nodes, name: :index_nodes_on_current_server_status, algorithm: :concurrently 36 | end 37 | 38 | def down 39 | add_index :nodes, :current_server_status, algorithm: :concurrently 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /db/migrations/008_add_parent_link_id_to_node.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AddParentLinkIdToNode < ActiveRecord::Migration 32 | disable_ddl_transaction! 33 | 34 | def change 35 | add_column :nodes, :parent_link_id, :uuid 36 | add_index :nodes, :parent_link_id, algorithm: :concurrently 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /db/migrations/009_add_workflows_created_at_index.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AddWorkflowsCreatedAtIndex < ActiveRecord::Migration 32 | disable_ddl_transaction! 33 | 34 | def change 35 | add_index :workflows, [:created_at, :id], algorithm: :concurrently 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /db/migrations/010_change_status_change_id_to_big_int.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class ChangeStatusChangeIdToBigInt < ActiveRecord::Migration 32 | def change 33 | change_column :status_changes, :id, :bigint 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /db/migrations/011_add_user_name_and_token.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AddUserNameAndToken < ActiveRecord::Migration 32 | def change 33 | add_column :users, :name, :string 34 | add_index :users, :name, unique: true 35 | 36 | add_column :users, :auth_token, :string 37 | add_index :users, :auth_token, unique: true 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /db/migrations/012_change_int_columns_to_big_int.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class ChangeIntColumnsToBigInt < ActiveRecord::Migration 32 | def change 33 | change_column :nodes, :seq, :bigint 34 | change_column :node_details, :id, :bigint 35 | change_column :client_node_details, :id, :bigint 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /db/migrations/013_add_nodes_workflow_id_seq_parent_id_index.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class AddNodesWorkflowIdSeqParentIdIndex < ActiveRecord::Migration 32 | disable_ddl_transaction! 33 | 34 | def change 35 | add_index :nodes, [:workflow_id, :seq, :parent_id], algorithm: :concurrently 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /db/migrations/014_remove_foreign_keys.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | class RemoveForeignKeys < ActiveRecord::Migration 32 | def up 33 | remove_foreign_key(:client_node_details, :nodes) 34 | remove_foreign_key(:node_details, :nodes) 35 | remove_foreign_key(:status_changes, :nodes) 36 | remove_foreign_key(:nodes, name: 'nodes_parent_id_fk') 37 | end 38 | 39 | def down 40 | add_foreign_key(:client_node_details, :nodes) 41 | add_foreign_key(:node_details, :nodes) 42 | add_foreign_key(:status_changes, :nodes) 43 | add_foreign_key(:nodes, :nodes, column: 'parent_id') 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | docker/docker-compose.ci.yml -------------------------------------------------------------------------------- /docker/backbeat_user.env.example: -------------------------------------------------------------------------------- 1 | BACKBEAT_USER_ID=9ab09c2f-68d5-4e0e-8844-3637eea44254 2 | BACKBEAT_USER_NAME="sample_user" 3 | BACKBEAT_CLIENT_URL=http://127.0.0.1:9090/api 4 | -------------------------------------------------------------------------------- /docker/common.yml: -------------------------------------------------------------------------------- 1 | app: 2 | build: ../ 3 | restart: always 4 | 5 | database: 6 | image: library/postgres:9.4 7 | environment: 8 | POSTGRES_USER: backbeat 9 | POSTGRES_PASSWORD: backbeat 10 | POSTGRES_DB: backbeat_docker 11 | 12 | redis: 13 | image: library/redis 14 | -------------------------------------------------------------------------------- /docker/docker-compose.ci.yml: -------------------------------------------------------------------------------- 1 | ci: 2 | extends: 3 | file: docker/common.yml 4 | service: app 5 | command: bin/run-tests.sh 6 | restart: never 7 | environment: 8 | RACK_ENV: ci 9 | links: 10 | - database 11 | - redis 12 | 13 | database: 14 | extends: 15 | file: docker/common.yml 16 | service: database 17 | 18 | redis: 19 | extends: 20 | file: docker/common.yml 21 | service: redis 22 | -------------------------------------------------------------------------------- /docker/docker-compose.deploy.yml: -------------------------------------------------------------------------------- 1 | web: 2 | extends: 3 | file: common.yml 4 | service: app 5 | image: docker.groupondev.com/backbeat/backbeat_server:latest 6 | command: bin/startup 7 | env_file: backbeat_user.env 8 | env_file: deploy.env 9 | ports: 10 | - 80:9292 11 | 12 | workers: 13 | extends: 14 | file: common.yml 15 | service: app 16 | image: docker.groupondev.com/backbeat/backbeat_server:latest 17 | command: bin/sidekiq 18 | env_file: deploy.env 19 | -------------------------------------------------------------------------------- /docker/docker-compose.local.yml: -------------------------------------------------------------------------------- 1 | web: 2 | extends: 3 | file: common.yml 4 | service: app 5 | command: bin/startup 6 | ports: 7 | - 9292:9292 8 | environment: 9 | RACK_ENV: docker 10 | env_file: backbeat_user.env 11 | links: 12 | - database 13 | - redis 14 | 15 | workers: 16 | extends: 17 | file: common.yml 18 | service: app 19 | command: bin/sidekiq 20 | environment: 21 | RACK_ENV: docker 22 | links: 23 | - database 24 | - redis 25 | 26 | database: 27 | extends: 28 | file: common.yml 29 | service: database 30 | 31 | redis: 32 | extends: 33 | file: common.yml 34 | service: redis 35 | -------------------------------------------------------------------------------- /documentation/images/backbeat_flow_payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/backbeat_flow_payment.png -------------------------------------------------------------------------------- /documentation/images/backbeat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/backbeat_logo.png -------------------------------------------------------------------------------- /documentation/images/camera_purchase_walkthrough/camera_purchase_walkthrough_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/camera_purchase_walkthrough/camera_purchase_walkthrough_1.png -------------------------------------------------------------------------------- /documentation/images/camera_purchase_walkthrough/camera_purchase_walkthrough_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/camera_purchase_walkthrough/camera_purchase_walkthrough_2.png -------------------------------------------------------------------------------- /documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_3.png -------------------------------------------------------------------------------- /documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_4.png -------------------------------------------------------------------------------- /documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_5.png -------------------------------------------------------------------------------- /documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/camera_purchase_walkthrough/create_purchase_walkthrough_6.png -------------------------------------------------------------------------------- /documentation/images/make_payment_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/make_payment_workflow.png -------------------------------------------------------------------------------- /documentation/images/parallel_activities_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/images/parallel_activities_example.png -------------------------------------------------------------------------------- /documentation/plantuml/Makefile: -------------------------------------------------------------------------------- 1 | TARGETS = $(shell ls -1 | sed -n 's/\.txt$$/\.png/p') 2 | 3 | all: $(TARGETS) 4 | 5 | %.png: %.txt 6 | java -Djava.awt.headless=true -jar plantuml.jar -pipe < $< > $@ 7 | 8 | kqwait: kqwait.c 9 | gcc -std=c99 -Wall -g -o kqwait kqwait.c 10 | 11 | .PHONY: clean 12 | clean: 13 | @rm -f $(TARGETS) kqwait kqwait.dSYM core 14 | -------------------------------------------------------------------------------- /documentation/plantuml/backbeat_flow_payment.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | "Backbeat Client" -> "Backbeat Server" : Create Workflow for Contract 4 | 5 | "Backbeat Client" -> "Backbeat Server" : Signal "Make Payment" 6 | 7 | "Backbeat Client" <- "Backbeat Server" : Run "Make Payment" 8 | 9 | "Backbeat Client" -> "Backbeat Server" : Add Make Payment Child Activities 10 | 11 | "Backbeat Client" <- "Backbeat Server" : Run "Get Sold Vouchers" 12 | 13 | "Backbeat Client" -> "Backbeat Server" : "Get Sold Vouchers" Completed 14 | 15 | "Backbeat Client" <- "Backbeat Server" : Run "Create Payment" 16 | 17 | "Backbeat Client" -> "Backbeat Server" : "Create Payment" Completed 18 | 19 | "Backbeat Client" <- "Backbeat Server" : Run "Send to Bank" 20 | 21 | "Backbeat Client" -> "Backbeat Server" : "Send to Bank" Complete 22 | 23 | @enduml 24 | -------------------------------------------------------------------------------- /documentation/plantuml/plantuml.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/documentation/plantuml/plantuml.jar -------------------------------------------------------------------------------- /lib/backbeat.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'backbeat/config' 32 | require 'backbeat/logging' 33 | require 'backbeat/instrument' 34 | require 'backbeat/presenters' 35 | require 'backbeat/schedulers' 36 | require 'backbeat/events' 37 | require 'backbeat/errors' 38 | require 'backbeat/client' 39 | require 'backbeat/server' 40 | require 'backbeat/state_manager' 41 | require 'backbeat/util' 42 | require 'backbeat/workflow_tree' 43 | require 'backbeat/workers/async_worker' 44 | require 'backbeat/workers/log_queues' 45 | require 'backbeat/workers/daily_activity' 46 | require 'backbeat/workers/heal_nodes' 47 | require 'backbeat/workers/middleware/transaction_id' 48 | require 'backbeat/models/user' 49 | require 'backbeat/models/node' 50 | require 'backbeat/models/node_detail' 51 | require 'backbeat/models/client_node_detail' 52 | require 'backbeat/models/status_change' 53 | require 'backbeat/models/workflow' 54 | -------------------------------------------------------------------------------- /lib/backbeat/cache.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'active_support/cache/redis_store' 32 | 33 | module Backbeat 34 | Cache = ActiveSupport::Cache::RedisStore.new(Config.redis.merge(pool_size: 5)) 35 | end 36 | -------------------------------------------------------------------------------- /lib/backbeat/client.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'httparty' 32 | 33 | module Backbeat 34 | module Client 35 | class << self 36 | def notify_of(node, message, error = nil) 37 | Instrument.instrument("client_notify", { node: node.id }) do 38 | user = node.user 39 | if url = user.notification_endpoint 40 | notification = NotificationPresenter.new(message, error).present(node) 41 | response = post(url, notification) 42 | raise HttpError.new("HTTP request for notification failed", response) unless response.success? 43 | end 44 | end 45 | end 46 | 47 | def perform(node) 48 | Instrument.instrument("client_perform_activity", { node: node.id }) do 49 | if node.decision? && node.user.decision_endpoint 50 | make_decision(NodePresenter.present(node), node.user) 51 | else 52 | perform_activity(NodePresenter.present(node), node.user) 53 | end 54 | end 55 | end 56 | 57 | private 58 | 59 | def perform_activity(activity, user) 60 | if url = user.activity_endpoint 61 | response = post(url, { activity: activity }) 62 | raise HttpError.new("HTTP request for activity failed", response) unless response.success? 63 | end 64 | end 65 | 66 | def make_decision(decision, user) 67 | if url = user.decision_endpoint 68 | response = post(url, { decision: decision }) 69 | raise HttpError.new("HTTP request for decision failed", response) unless response.success? 70 | end 71 | end 72 | 73 | def post(url, params = {}) 74 | HTTParty.post(url, { 75 | body: params.to_json, 76 | headers: { "Content-Type" => "application/json" } 77 | }) 78 | rescue => e 79 | raise NetworkError.new("Could not POST #{url}, error: #{e.class}, #{e.message}") 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/backbeat/config.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class Config 33 | def self.environment 34 | @environment ||= ENV.fetch('RACK_ENV', 'development') 35 | end 36 | 37 | def self.root 38 | @root ||= File.expand_path('../../../', __FILE__) 39 | end 40 | 41 | def self.log_file 42 | @log_file ||= ( 43 | ENV['LOG_FILE'] || options[:log] || STDOUT 44 | ) 45 | end 46 | 47 | def self.log_level 48 | @log_level ||= ( 49 | level = ENV['LOG_LEVEL'] || options[:log_level] || 'INFO' 50 | ::Logger.const_get(level) 51 | ) 52 | end 53 | 54 | def self.options 55 | @options ||= config_with_defaults("options") 56 | end 57 | 58 | def self.database 59 | @database ||= config_with_defaults("database") 60 | end 61 | 62 | def self.redis 63 | @redis ||= config_with_defaults("redis") 64 | end 65 | 66 | def self.hostname 67 | @hostname ||= `hostname -f` 68 | end 69 | 70 | def self.revision 71 | @revision ||= ( 72 | file_path = "#{root}/REVISION" 73 | File.read(file_path).chomp if File.exists?(file_path) 74 | ) 75 | end 76 | 77 | def self.config_with_defaults(name) 78 | defaults = YAML.load_file("#{root}/config/defaults/#{name}.yml").fetch("defaults") 79 | defaults.deep_merge(YAML.load_file("#{root}/config/#{name}.yml").fetch(environment, {})).deep_symbolize_keys 80 | end 81 | private_class_method :config_with_defaults 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/backbeat/errors.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class InvalidStatusChange < StandardError; end 33 | class UnknownStatus < StandardError; end 34 | class InvalidServerStatusChange < InvalidStatusChange; end 35 | class InvalidClientStatusChange < InvalidStatusChange 36 | attr_reader :data 37 | 38 | def initialize(message, data = {}) 39 | @data = data 40 | super(message) 41 | end 42 | end 43 | 44 | class WorkflowComplete < StandardError; end 45 | class StaleStatusChange < StandardError; end 46 | class DeserializeError < StandardError; end 47 | 48 | class InvalidParameters < StandardError 49 | def initialize(raw_message) 50 | @raw_message = raw_message 51 | super 52 | end 53 | 54 | def message 55 | @raw_message 56 | end 57 | end 58 | 59 | class HttpError < StandardError 60 | attr_reader :response 61 | def initialize(message, response = nil) 62 | @response = response 63 | super(message) 64 | end 65 | end 66 | 67 | class NetworkError < StandardError 68 | attr_reader :response 69 | def initialize(message, response = nil) 70 | @response = response 71 | super(message) 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/backbeat/events/event.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Events 33 | class Event 34 | def self.scheduler(type = nil) 35 | if type 36 | @scheduler = type 37 | else 38 | @scheduler 39 | end 40 | end 41 | 42 | def self.call(node) 43 | new.call(node) 44 | end 45 | 46 | def scheduler 47 | self.class.scheduler 48 | end 49 | 50 | def name 51 | self.class.name 52 | end 53 | end 54 | 55 | module ResponseHandler 56 | attr_reader :response 57 | 58 | def initialize(response = {}) 59 | @response = response 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/backbeat/instrument.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Instrument 33 | extend Logging 34 | 35 | def self.instrument(event_name, *args) 36 | t0 = Time.now 37 | log_msg("#{event_name}_started", args) 38 | result = yield 39 | log_msg("#{event_name}_succeeded", args, duration: Time.now - t0) 40 | return result 41 | rescue Exception => error 42 | handle_exception(event_name, error, t0, args) 43 | raise error 44 | end 45 | 46 | def self.handle_exception(event_name, error, t0, args) 47 | log_msg( 48 | "#{event_name}_errored", 49 | args, 50 | error_class: error.class.name, 51 | error: error.to_s, 52 | backtrace: error.backtrace, 53 | duration: Time.now - t0 54 | ) 55 | rescue Exception => error 56 | info({ message: :error_logging_error, event_name: event_name }) 57 | raise error 58 | end 59 | 60 | 61 | def self.log_msg(message, args, options = {}) 62 | info({ 63 | message: message, 64 | args: args 65 | }.merge(options)) 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/backbeat/logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'securerandom' 32 | 33 | module Backbeat 34 | module Logging 35 | LEVELS = [:debug, :info, :warn, :error, :fatal] 36 | 37 | LEVELS.each_with_index do |level, level_num| 38 | define_method(level) do |message = nil, &block| 39 | if block 40 | message = block.call 41 | end 42 | message_with_metadata = { 43 | time: Time.now.utc.iso8601(6), 44 | source: logging_source, 45 | data: message, 46 | pid: Process.pid, 47 | thread_id: Thread.current.object_id, 48 | tid: Logger.tid || 'none', 49 | revision: Config.revision 50 | } 51 | Logger.add(level_num, message_with_metadata) 52 | end 53 | end 54 | 55 | private 56 | 57 | def logging_source 58 | case self 59 | when Class 60 | self.to_s 61 | when Module 62 | self.to_s 63 | else 64 | self.class.to_s 65 | end 66 | end 67 | end 68 | 69 | class Logger 70 | extend Logging 71 | 72 | def self.logger 73 | @logger ||= create_logger 74 | end 75 | 76 | def self.logger=(logger) 77 | @logger = logger 78 | end 79 | 80 | def self.add(level_num, message) 81 | level = (Logging::LEVELS[level_num] || 'ANY').downcase 82 | log_data = message.merge({ level: level }).to_json + "\n" 83 | logger.add(level_num, log_data, nil) 84 | end 85 | 86 | def self.create_logger 87 | if defined?(TorqueBox) 88 | TorqueBox::Logger.new('backbeat_logger') 89 | else 90 | logger = ::Logger.new(Config.log_file) 91 | logger.level = Config.log_level 92 | logger.formatter = lambda { |_, _, _, msg| msg } 93 | logger 94 | end 95 | end 96 | 97 | def self.tid(option = nil) 98 | if option == :set 99 | Thread.current[:tid] = SecureRandom.uuid.to_s.slice(0,7) 100 | elsif option == :clear 101 | Thread.current[:tid] = nil 102 | end 103 | Thread.current[:tid] 104 | end 105 | end 106 | 107 | class SidekiqLogger 108 | extend Logging 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /lib/backbeat/models/child_queries.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module ChildQueries 33 | def all_children_ready? 34 | !children.where(current_server_status: :pending).exists? 35 | end 36 | 37 | def not_complete_children 38 | children.where("current_server_status != 'complete' AND current_server_status != 'deactivated'") 39 | end 40 | 41 | def active_children 42 | children.where("current_server_status != 'deactivated'") 43 | end 44 | 45 | def direct_children_complete? 46 | not_complete_children.where("mode != 'fire_and_forget'").count == 0 47 | end 48 | 49 | def print_tree 50 | puts WorkflowTree.to_string(self) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/backbeat/models/client_node_detail.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class ClientNodeDetail < ActiveRecord::Base 33 | belongs_to :node 34 | 35 | serialize :metadata, JSON 36 | serialize :data, JSON 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/backbeat/models/node_detail.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class NodeDetail < ActiveRecord::Base 33 | belongs_to :node 34 | 35 | validates :retries_remaining, numericality: { greater_than_or_equal_to: 0 } 36 | 37 | serialize :valid_next_events, JSON 38 | 39 | before_validation do 40 | self.retries_remaining ||= 6 41 | self.retry_interval ||= 20.minutes 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/backbeat/models/status_change.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class StatusChange < ActiveRecord::Base 33 | belongs_to :node 34 | default_scope { order("id asc") } 35 | serialize :response, JSON 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/backbeat/models/user.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'securerandom' 32 | 33 | module Backbeat 34 | class User < ActiveRecord::Base 35 | has_many :workflows 36 | has_many :nodes 37 | belongs_to :user 38 | 39 | validates :name, presence: true 40 | validates :activity_endpoint, presence: true 41 | validates :notification_endpoint, presence: true 42 | 43 | before_create :generate_auth_token 44 | 45 | def generate_auth_token 46 | self.auth_token ||= SecureRandom.urlsafe_base64 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/backbeat/models/workflow.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'backbeat/models/child_queries' 32 | 33 | module Backbeat 34 | class Workflow < ActiveRecord::Base 35 | belongs_to :user 36 | has_many :nodes 37 | serialize :subject, JSON 38 | 39 | validates :subject, presence: true 40 | validates :decider, presence: true 41 | validates :user_id, presence: true 42 | 43 | include ChildQueries 44 | 45 | def parent 46 | nil 47 | end 48 | 49 | def children 50 | nodes.where(parent_id: nil) 51 | end 52 | 53 | def workflow_id 54 | id 55 | end 56 | 57 | def deactivated? 58 | false 59 | end 60 | 61 | def complete! 62 | update_attributes(complete: true) 63 | end 64 | 65 | def pause! 66 | update_attributes(paused: true) 67 | end 68 | 69 | def resume! 70 | update_attributes(paused: false) 71 | end 72 | 73 | def destroy 74 | children.map(&:destroy) 75 | super 76 | end 77 | 78 | def all_children_complete? 79 | direct_children_complete? 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/backbeat/schedulers.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'backbeat/instrument' 32 | 33 | module Backbeat 34 | module Schedulers 35 | class AsyncEvent 36 | def initialize(&timer) 37 | @timer = timer 38 | end 39 | 40 | def call(event, node) 41 | time = @timer.call(node) 42 | Workers::AsyncWorker.schedule_async_event(event, node, { time: time }) 43 | end 44 | end 45 | 46 | ScheduleNow = AsyncEvent.new { Time.now } 47 | ScheduleAt = AsyncEvent.new { |node| node.fires_at } 48 | 49 | EXPONENTIAL_THRESHOLD = 6 50 | 51 | ScheduleRetry = AsyncEvent.new do |node| 52 | exponent = EXPONENTIAL_THRESHOLD - node.retries_remaining 53 | exponent = 0 if exponent < 0 54 | 55 | time = Time.now + (rand(0.8..1.2) * node.retry_interval) * (2**exponent) 56 | node.update_attributes(fires_at: time) 57 | time 58 | end 59 | 60 | class PerformEvent 61 | def self.call(event, node) 62 | Instrument.instrument(event.name, { node: node }) do 63 | event.call(node) 64 | end 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/backbeat/search/activity_search.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'backbeat/search/filter' 32 | 33 | module Backbeat 34 | module Search 35 | class ActivitySearch 36 | def initialize(params, user_id) 37 | @params = params 38 | @user_id = user_id 39 | end 40 | 41 | def result 42 | filter.apply_filters( 43 | params, 44 | filter.name, 45 | metadata_filter, 46 | filter.current_status, 47 | filter.past_status, 48 | filter.status_start, 49 | filter.status_end, 50 | filter.per_page, 51 | filter.page, 52 | filter.last_id 53 | ) 54 | end 55 | 56 | private 57 | 58 | attr_reader :params, :user_id 59 | 60 | def filter 61 | @filter ||= Filter.new(Node.where(user_id: user_id).order({ created_at: :desc, id: :desc })) 62 | end 63 | 64 | def metadata_filter 65 | filter.filter_for(:metadata) do |relation, params| 66 | relation.joins("JOIN client_node_details ON client_node_details.node_id = nodes.id") 67 | .where("client_node_details.metadata LIKE ?", "%#{params[:metadata]}%") 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/backbeat/search/workflow_search.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'backbeat/search/filter' 32 | 33 | module Backbeat 34 | module Search 35 | class WorkflowSearch 36 | def initialize(params, user_id) 37 | @params = params 38 | @user_id = user_id 39 | end 40 | 41 | def result 42 | filter.apply_filters( 43 | params, 44 | filter.name, 45 | subject_filter, 46 | filter.current_status, 47 | filter.past_status, 48 | filter.status_start, 49 | filter.status_end, 50 | filter.per_page, 51 | filter.page, 52 | filter.last_id 53 | ) 54 | end 55 | 56 | private 57 | 58 | attr_reader :params, :user_id 59 | 60 | def filter 61 | @filter ||= Filter.new( 62 | Workflow.where(user_id: user_id).order({ created_at: :desc, id: :desc }).joins(:nodes) 63 | ) 64 | end 65 | 66 | def subject_filter 67 | filter.filter_for(:subject) do |relation, params| 68 | relation.where("workflows.subject LIKE ?", "%#{params[:subject]}%") 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/backbeat/server.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class Server 33 | extend Logging 34 | 35 | def self.create_workflow(params, user) 36 | find_workflow(params, user) || Workflow.create!( 37 | name: params[:workflow_type] || params[:name], 38 | subject: params[:subject], 39 | decider: params[:decider], 40 | user_id: user.id, 41 | migrated: true 42 | ) 43 | rescue ActiveRecord::RecordNotUnique 44 | find_workflow(params, user) 45 | end 46 | 47 | def self.find_workflow(params, user) 48 | Workflow.where( 49 | name: params[:workflow_type] || params[:name], 50 | subject: params[:subject].to_json, 51 | user_id: user.id 52 | ).first 53 | end 54 | 55 | def self.signal(workflow, params) 56 | raise WorkflowComplete if workflow.complete? 57 | node = add_node( 58 | workflow.user, 59 | workflow, 60 | params.merge( 61 | current_server_status: :ready, 62 | current_client_status: :ready, 63 | legacy_type: 'decision', 64 | mode: :blocking 65 | ) 66 | ) 67 | node 68 | end 69 | 70 | def self.add_node(user, parent_node, params) 71 | Node.transaction do 72 | options = params[:options] || params 73 | node = Node.create!( 74 | mode: (params[:mode] || :blocking).to_sym, 75 | current_server_status: params[:current_server_status] || :pending, 76 | current_client_status: params[:current_client_status] || :pending, 77 | name: params[:name], 78 | fires_at: params[:fires_at] || Time.now - 1.second, 79 | parent: parent_node, 80 | workflow_id: parent_node.workflow_id, 81 | user_id: params[:client_id] || user.id, 82 | parent_link_id: options[:parent_link_id] 83 | ) 84 | ClientNodeDetail.create!( 85 | node: node, 86 | metadata: options[:metadata] || {}, 87 | data: options[:client_data] || {} 88 | ) 89 | NodeDetail.create!( 90 | node: node, 91 | legacy_type: params[:legacy_type] || :activity, 92 | retry_interval: params[:retry_interval], 93 | retries_remaining: params[:retry] 94 | ) 95 | node 96 | end 97 | end 98 | 99 | def self.resume_workflow(workflow) 100 | workflow.resume! 101 | workflow.nodes.where(current_server_status: :paused).each do |node| 102 | StateManager.transition(node, current_server_status: :started) 103 | fire_event(Events::StartNode, node) 104 | end 105 | end 106 | 107 | def self.fire_event(event, node, scheduler = event.scheduler) 108 | return if node.deactivated? 109 | scheduler.call(event, node) 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/backbeat/util.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class Util 33 | def self.camelize(object, options = {}) 34 | transform_keys(object, options) do |key| 35 | key.to_s.camelize(:lower).to_sym 36 | end 37 | end 38 | 39 | def self.underscore(object, options = {}) 40 | transform_keys(object, options) do |key| 41 | key.to_s.underscore.to_sym 42 | end 43 | end 44 | 45 | def self.transform_keys(object, options, &block) 46 | ignored_keys = options.fetch(:ignore, []) 47 | case object 48 | when Hash 49 | object.reduce({}) do |memo, (key, value)| 50 | new_key = block.call(key) 51 | if ignored_keys.include?(new_key) 52 | memo[new_key] = value 53 | else 54 | memo[new_key] = transform_keys(value, options, &block) 55 | end 56 | memo 57 | end 58 | when Array 59 | object.map do |value| 60 | transform_keys(value, options, &block) 61 | end 62 | else 63 | if object.respond_to?(:to_hash) 64 | transform_keys(object.to_hash, options, &block) 65 | else 66 | object 67 | end 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/backbeat/web.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'grape' 32 | require 'backbeat/web/middleware/log' 33 | require 'backbeat/web/middleware/health' 34 | require 'backbeat/web/middleware/heartbeat' 35 | require 'backbeat/web/middleware/sidekiq_stats' 36 | require 'backbeat/web/workflows_api' 37 | require 'backbeat/web/activities_api' 38 | require 'backbeat/web/debug_api' 39 | require 'backbeat/web/users_api' 40 | 41 | module Backbeat 42 | module Web 43 | class API < Grape::API 44 | format :json 45 | 46 | helpers CurrentUserHelper 47 | 48 | before do 49 | @params = Util.underscore(params, { ignore: [:client_data, :metadata] }) 50 | end 51 | 52 | rescue_from :all do |e| 53 | Logger.error({ error_type: e.class.to_s, error: e.message, backtrace: e.backtrace }) 54 | error!(ErrorPresenter.present(e), 500) 55 | end 56 | 57 | rescue_from ActiveRecord::RecordNotFound do |e| 58 | Logger.info(e.message) 59 | error!(ErrorPresenter.present(e), 404) 60 | end 61 | 62 | RESCUED_ERRORS = [ 63 | WorkflowComplete, 64 | Grape::Exceptions::Validation, 65 | Grape::Exceptions::ValidationErrors, 66 | ActiveRecord::StatementInvalid 67 | ] 68 | 69 | rescue_from *RESCUED_ERRORS do |e| 70 | Logger.info(e.message) 71 | error!(ErrorPresenter.present(e), 400) 72 | end 73 | 74 | rescue_from InvalidServerStatusChange do |e| 75 | Logger.info(e.message) 76 | error!(ErrorPresenter.present(e), 500) 77 | end 78 | 79 | rescue_from InvalidClientStatusChange do |e| 80 | Logger.info(e.message) 81 | error!(ErrorPresenter.present(e), 409) 82 | end 83 | 84 | rescue_from UnknownStatus do |e| 85 | error!(ErrorPresenter.present(e), 400) 86 | end 87 | 88 | mount WorkflowsAPI.versioned('/') 89 | mount ActivitiesAPI.versioned('/activities') 90 | mount UsersAPI 91 | 92 | # Deprecated V2 API 93 | mount WorkflowsAPI.versioned('/v2') 94 | mount ActivitiesAPI.versioned('/v2/events') 95 | mount DebugAPI.versioned('/v2') 96 | end 97 | 98 | App = Rack::Builder.new do 99 | use Middleware::Log 100 | use Middleware::Heartbeat 101 | use ActiveRecord::ConnectionAdapters::ConnectionManagement 102 | use Middleware::Health 103 | use Middleware::SidekiqStats 104 | 105 | run API 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /lib/backbeat/web/debug_api.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'backbeat/models/node' 32 | require 'backbeat/models/workflow' 33 | require 'backbeat/web/versioned_api' 34 | require 'backbeat/web/helpers/current_user_helper' 35 | 36 | module Backbeat 37 | module Web 38 | class DebugAPI < VersionedAPI 39 | api do 40 | helpers CurrentUserHelper 41 | 42 | before do 43 | authenticate! 44 | end 45 | 46 | resource 'debug' do 47 | get "/error_workflows" do 48 | workflow_ids = Node.where( 49 | user_id: current_user.id, 50 | current_client_status: :errored 51 | ).pluck(:workflow_id).uniq 52 | 53 | Workflow.where("id IN (?)", workflow_ids) 54 | end 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/backbeat/web/helpers/current_user_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Web 33 | module CurrentUserHelper 34 | def authenticate! 35 | unauthorized unless current_user 36 | end 37 | 38 | def require_auth_token! 39 | unauthorized unless current_user && has_auth_token? 40 | end 41 | 42 | def has_auth_token? 43 | if Config.options[:require_auth_token] 44 | current_user.auth_token == auth_token 45 | else 46 | true 47 | end 48 | end 49 | 50 | def auth_token 51 | if auth = request.headers['Authorization'] 52 | if match = auth.match(/Token token="(?.+)"$/) 53 | match['token'] 54 | end 55 | end 56 | end 57 | 58 | def unauthorized 59 | error!({ error: 'Unauthorized' }, 401) 60 | end 61 | 62 | def current_user 63 | @current_user ||= find_user(env['HTTP_CLIENT_ID']) 64 | end 65 | 66 | def find_user(id) 67 | User.find(id) 68 | rescue => e 69 | Logger.info(message: "Error occurred while finding user", error: e.message, backtrace: e.backtrace) 70 | false 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/backbeat/web/middleware/health.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Web 33 | module Middleware 34 | class Health 35 | def initialize(app) 36 | @app = app 37 | end 38 | 39 | ENDPOINT = '/health'.freeze 40 | 41 | def call(env) 42 | if env['PATH_INFO'] == ENDPOINT 43 | result = { 44 | sha: Config.revision, 45 | time: Time.now.iso8601, 46 | status: db_ok? ? 'OK' : 'DATABASE_UNREACHABLE' 47 | } 48 | [200, { "Content-Type" => "application/json" }, [result.to_json]] 49 | else 50 | @app.call(env) 51 | end 52 | end 53 | 54 | def db_ok? 55 | ActiveRecord::Base.connection 56 | ActiveRecord::Base.connected? 57 | rescue 58 | false 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/backbeat/web/middleware/heartbeat.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Web 33 | module Middleware 34 | class Heartbeat 35 | def initialize(app) 36 | @app = app 37 | end 38 | 39 | ENDPOINT = '/heartbeat.txt'.freeze 40 | HEARTBEAT = "#{Config.root}/public/heartbeat.txt".freeze 41 | HEARTBEAT_OK = [ 42 | 200, 43 | { "Content-Type" => "text/plain", "Cache-Control" => "private, no-cache, no-store, must-revalidate" }, 44 | ["We have a pulse."] 45 | ].freeze 46 | HEARTBEAT_DOWN = [ 47 | 503, 48 | { "Content-Type" => "text/plain" }, 49 | ["It's dead, Jim."] 50 | ].freeze 51 | 52 | def call(env) 53 | if env['PATH_INFO'] == ENDPOINT 54 | if File.exists?(HEARTBEAT) && File.file?(HEARTBEAT) 55 | HEARTBEAT_OK 56 | else 57 | HEARTBEAT_DOWN 58 | end 59 | else 60 | @app.call(env) 61 | end 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/backbeat/web/middleware/log.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Web 33 | module Middleware 34 | class Log 35 | TRANSACTION_ID_HEADER = 'X-backbeat-tid'.freeze 36 | 37 | def initialize(app) 38 | @app = app 39 | end 40 | 41 | def call(env) 42 | t0 = Time.now 43 | tid = Logger.tid(:set) 44 | request = Rack::Request.new(env) 45 | request_data = { 46 | path: request.path_info, 47 | method: request.request_method, 48 | params: request.params 49 | } 50 | 51 | Logger.info({ message: "Request Start" }.merge(request_data)) 52 | 53 | status, headers, body = response = @app.call(env) 54 | 55 | Logger.info({ 56 | message: "Request Complete", 57 | request: request_data, 58 | response: { 59 | status: status, 60 | duration: Time.now - t0, 61 | } 62 | }) 63 | 64 | headers[TRANSACTION_ID_HEADER] = tid 65 | Logger.tid(:clear) 66 | 67 | response 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/backbeat/web/middleware/sidekiq_stats.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Web 33 | module Middleware 34 | class SidekiqStats 35 | def initialize(app) 36 | @app = app 37 | end 38 | 39 | ENDPOINT = '/sidekiq_stats' 40 | 41 | def call(env) 42 | if env['PATH_INFO'] == ENDPOINT 43 | stats = Sidekiq::Stats.new 44 | history = Sidekiq::Stats::History.new(1) 45 | data = { 46 | latency: latency(stats), 47 | today: { 48 | processed: history.processed.values[0], 49 | failed: history.failed.values[0] 50 | }, 51 | processed: stats.processed, 52 | failed: stats.failed, 53 | enqueued: stats.enqueued, 54 | scheduled: stats.scheduled_size, 55 | retry_size: stats.retry_size 56 | } 57 | [200, { "Content-Type" => "application/json" }, [data.to_json]] 58 | else 59 | @app.call(env) 60 | end 61 | end 62 | 63 | def latency(stats) 64 | stats.queues.keys.inject({}) do |h, q| 65 | h[q] = Sidekiq::Queue.new(q).latency 66 | h 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/backbeat/web/users_api.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | module Web 33 | class UsersAPI < Grape::API 34 | resource 'users' do 35 | get '/' do 36 | present Backbeat::User.order(:name), with: UserPresenter 37 | end 38 | 39 | get '/:id' do 40 | present Backbeat::User.find(params[:id]), with: UserPresenter 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/backbeat/web/versioned_api.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'grape' 32 | 33 | class VersionedAPI 34 | def self.versioned(version = '/') 35 | Class.new(Grape::API).tap do |klass| 36 | klass.namespace(version, &@api) 37 | end 38 | end 39 | 40 | def self.api(&block) 41 | @api = block 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/backbeat/workers/daily_activity/report.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Backbeat Activity Report

5 | 6 | <% if report[:inconsistent][:counts].empty? %> 7 |

No inconsistent workflows for this period

8 | <% else %> 9 | <%= render_table "Workflow Inconsistencies", report[:inconsistent][:counts] %> 10 |

Inconsistent workflow data stored at <%= report[:inconsistent][:filename] %> on <%= report[:inconsistent][:hostname] %>

11 | <% end %> 12 | 13 | <% if report[:completed][:counts].empty? %> 14 |

No completed workflows for this period

15 | <% else %> 16 | <%= render_table "Workflow Activity", report[:completed][:counts] %> 17 | <% end %> 18 | 19 |

Report finished in <%= report[:time_elapsed] %> seconds

20 |

Processing window: <%= report[:range][:lower_bound].to_s %> to <%= report[:range][:upper_bound].to_s %>

21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/backbeat/workers/daily_activity/table.html.erb: -------------------------------------------------------------------------------- 1 |

<%= title %>

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <% counts.each do |name, detail| %> 10 | 11 | 12 | 13 | 14 | 15 | <% end %> 16 |
Workflow NameWorkflow CountNode Count
<%= name %><%= detail[:workflow_type_count] %><%= detail[:node_count] %>
17 | -------------------------------------------------------------------------------- /lib/backbeat/workers/heal_nodes.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'sidekiq' 32 | require 'sidekiq/schedulable' 33 | 34 | module Backbeat 35 | module Workers 36 | class HealNodes 37 | include Logging 38 | include Sidekiq::Worker 39 | include Sidekiq::Schedulable 40 | 41 | sidekiq_options retry: false, queue: Config.options[:async_queue] 42 | sidekiq_schedule Config.options[:schedules][:heal_nodes], last_run: true 43 | 44 | CLIENT_TIMEOUT_ERROR = "Client did not respond within the specified 'complete_by' time" 45 | UNEXPECTED_STATE_MESSAGE = "Node with expired 'complete_by' is not in expected state" 46 | 47 | def perform(last_run) 48 | last_run_time = Time.at(last_run) 49 | expired_node_details(last_run_time).each do |node_detail| 50 | node = node_detail.node 51 | 52 | if received_by_client?(node) 53 | info(message: CLIENT_TIMEOUT_ERROR, node: node.id, complete_by: node_detail.complete_by) 54 | Server.fire_event(Events::ClientError.new({ error: CLIENT_TIMEOUT_ERROR }), node) 55 | else 56 | info(message: UNEXPECTED_STATE_MESSAGE, node: node.id, complete_by: node_detail.complete_by) 57 | end 58 | end 59 | end 60 | 61 | private 62 | 63 | def expired_node_details(last_run_time) 64 | NodeDetail.where(complete_by: last_run_time..Time.now).select(:node_id, :complete_by) 65 | end 66 | 67 | def received_by_client?(node) 68 | node.current_server_status == "sent_to_client" && node.current_client_status == "received" 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/backbeat/workers/log_queues.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'sidekiq' 32 | require 'sidekiq/schedulable' 33 | 34 | module Backbeat 35 | module Workers 36 | class LogQueues 37 | include Logging 38 | include Sidekiq::Worker 39 | include Sidekiq::Schedulable 40 | 41 | sidekiq_options retry: false, queue: Config.options[:async_queue] 42 | sidekiq_schedule Config.options[:schedules][:log_queues] 43 | 44 | def perform 45 | log_count(:queue, "retry", Sidekiq::RetrySet.new.size) 46 | log_count(:queue, "schedule", Sidekiq::ScheduledSet.new.size) 47 | 48 | Sidekiq::Stats.new.queues.each do |queue, size| 49 | log_count(:queue, queue, size) 50 | end 51 | end 52 | 53 | private 54 | 55 | def log_count(type, subject, count) 56 | info({ type: type, subject: subject, count: count || 0 }) 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/backbeat/workers/middleware/transaction_id.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'backbeat' 32 | 33 | module Backbeat 34 | module Workers 35 | module Middleware 36 | class TransactionId 37 | def call(*args) 38 | Backbeat::Logger.tid(:set) 39 | yield 40 | ensure 41 | Backbeat::Logger.tid(:clear) 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/backbeat/workflow_tree/colorize.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Backbeat 32 | class WorkflowTree 33 | module Colorize 34 | def colorize(text, color_code) 35 | "\e[#{color_code}m#{text}\e[0m" 36 | end 37 | 38 | COLORS = { 39 | black: 30, 40 | red: 31, 41 | green: 32, 42 | yellow: 33, 43 | blue: 34, 44 | magenta: 35, 45 | cyan: 36, 46 | white: 37 47 | } 48 | 49 | COLORS.each_pair do |color, number| 50 | define_method color do |text| 51 | colorize(text, number) 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/log/.gitkeep -------------------------------------------------------------------------------- /public/heartbeat.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/groupon/backbeat/821ad8537f4da9f4fc0296bbbcd3b66c6ed1183f/public/heartbeat.txt -------------------------------------------------------------------------------- /script/add_user.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require File.expand_path('../../config/environment', __FILE__) 32 | require 'logger' 33 | 34 | user_id = ENV['BACKBEAT_USER_ID'] 35 | client_url = ENV['BACKBEAT_CLIENT_URL'] 36 | name = ENV['BACKBEAT_USER_NAME'] 37 | 38 | logger = Logger.new(STDOUT) 39 | 40 | if user_id 41 | logger.info "Looking for user with id: #{user_id}" 42 | user = Backbeat::User.where(id: user_id).first 43 | 44 | if user 45 | logger.info "User exists: " 46 | else 47 | user = Backbeat::User.new( 48 | decision_endpoint: "#{client_url}/activity", 49 | activity_endpoint: "#{client_url}/activity", 50 | notification_endpoint: "#{client_url}/notification", 51 | name: name 52 | ) 53 | user.id = user_id 54 | user.save! 55 | 56 | logger.info "Created new user with id #{user.id}. Attributes:" 57 | end 58 | 59 | user.attributes.each do |attr, val| 60 | logger.info "#{attr}: #{val}" 61 | end 62 | else 63 | logger.info "No user provided" 64 | end 65 | -------------------------------------------------------------------------------- /script/console.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require File.expand_path('../../config/environment', __FILE__) 32 | require 'irb' 33 | require 'redis-namespace' 34 | require 'ap' 35 | require_relative 'console_helpers' 36 | 37 | include Backbeat 38 | 39 | ActiveRecord::Base.logger = Logger.new(STDOUT) 40 | ARGV.clear 41 | IRB.start 42 | -------------------------------------------------------------------------------- /script/inconsistent_node_fixes.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | # NOTE: 32 | # This is not a script meant to run all at once. 33 | # These are separate scenarios in which nodes may become stuck. 34 | # Run one fix at a time, then check the counts. 35 | 36 | time = Time.now - 20.hours 37 | 38 | # For checking inconsistent nodes 39 | Backbeat::Node 40 | .where("fires_at < ?", time) 41 | .where("(current_server_status <> 'complete' OR current_client_status <> 'complete') AND current_server_status <> 'deactivated'") 42 | .count 43 | 44 | Backbeat::Node 45 | .where(current_server_status: :processing_children, current_client_status: :complete) 46 | .where("fires_at < ?", time) 47 | .each { |n| Backbeat::Events::ScheduleNextNode.call(n) } 48 | 49 | Backbeat::Node 50 | .where("fires_at < ?", time) 51 | .where(current_server_status: :started, current_client_status: :ready) 52 | .each { |n| Backbeat::Events::StartNode.call(n) } 53 | 54 | Backbeat::Node 55 | .where("fires_at < ?", time) 56 | .where(current_server_status: :sent_to_client, current_client_status: :received) 57 | .each { |n| Backbeat::Events::ScheduleNextNode.call(n.parent) } 58 | 59 | Backbeat::Node 60 | .where(current_server_status: :sent_to_client, current_client_status: :received) 61 | .where("fires_at < ?", time) 62 | .each { |n| Backbeat::Client.perform(n) if n.children.count == 0 } 63 | 64 | Backbeat::Node 65 | .where("fires_at < ?", time) 66 | .where(current_server_status: :ready, current_client_status: :ready) 67 | .each { |n| Backbeat::Events::ScheduleNextNode.call(n.parent) } 68 | -------------------------------------------------------------------------------- /script/seed.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | 3.times do |i| 32 | user = Backbeat::User.create!({ 33 | name: "User #{i + 1}", 34 | activity_endpoint: "http://localhost:5000/#{i + 1}/activity", 35 | notification_endpoint: "http://localhost:5000/#{i + 1}/notification" 36 | }) 37 | puts "Created user #{user.id}" 38 | workflow = Backbeat::Server.create_workflow({ 39 | name: "Workflow #{i + 1}", 40 | subject: "Subject #{i + 1}", 41 | decider: "Decider #{i + 1}" 42 | }, user) 43 | puts "Created workflow #{workflow.id}" 44 | signal_params = { 45 | name: "Signal #{i + 1}", 46 | client_data: { 47 | params: [1, 2, 3], 48 | method: 'testing' 49 | } 50 | } 51 | node = Backbeat::Server.signal(workflow, signal_params) 52 | puts "Created node #{node.id}" 53 | end 54 | -------------------------------------------------------------------------------- /spec/factories/node.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | FactoryGirl.define do 32 | factory :node, class: Backbeat::Node do 33 | mode :blocking 34 | current_server_status :pending 35 | current_client_status :ready 36 | name "Test-Node" 37 | fires_at Time.now 38 | 39 | after(:create) do |node| 40 | Backbeat::NodeDetail.create!({ node: node, legacy_type: 'activity' }) 41 | Backbeat::ClientNodeDetail.create!({ 42 | node: node, 43 | metadata: {}, 44 | data: { method: "foo", params: ["bar"] } 45 | }) 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/factories/user.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | FactoryGirl.define do 32 | BACKBEAT_CLIENT_ENDPOINT = 'http://backbeat-client:9000' 33 | 34 | factory :user, class: Backbeat::User do 35 | name "Test user" 36 | decision_endpoint "#{BACKBEAT_CLIENT_ENDPOINT}/decision" 37 | activity_endpoint "#{BACKBEAT_CLIENT_ENDPOINT}/activity" 38 | notification_endpoint "#{BACKBEAT_CLIENT_ENDPOINT}/notifications" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/factories/workflow.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | FactoryGirl.define do 32 | factory :workflow, class: Backbeat::Workflow do 33 | name 'WFType' 34 | subject({'subject_klass'=>'PaymentTerm', 'subject_id'=>'100'}) 35 | decider 'PaymentDecider' 36 | 37 | factory :workflow_with_node do 38 | after(:create) do |workflow| 39 | FactoryGirl.create( 40 | :node, 41 | parent: workflow, 42 | workflow_id: workflow.workflow_id, 43 | user_id: workflow.user_id 44 | ) 45 | end 46 | end 47 | 48 | factory :workflow_with_node_running do 49 | after(:create) do |workflow| 50 | signal_node = FactoryGirl.create( 51 | :node, 52 | parent: workflow, 53 | workflow_id: workflow.id, 54 | user_id: workflow.user_id, 55 | current_server_status: :processing_children, 56 | current_client_status: :complete 57 | ) 58 | 59 | FactoryGirl.create( 60 | :node, 61 | workflow_id: workflow.id, 62 | user_id: workflow.user_id, 63 | parent_id: signal_node.id, 64 | current_server_status: :sent_to_client, 65 | current_client_status: :received 66 | ) 67 | end 68 | end 69 | end 70 | end 71 | 72 | 73 | -------------------------------------------------------------------------------- /spec/integration/web/authentication_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Web::API, :api_test do 34 | it "returns 401 if client id header is missing" do 35 | response = get "/workflows/names" 36 | 37 | expect(response.status).to eq(401) 38 | expect(JSON.parse(response.body)).to eq({ 'error' => 'Unauthorized' }) 39 | end 40 | 41 | it "returns 401 if client id header is incorrect" do 42 | header "CLIENT_ID", "XX_TT" 43 | 44 | response = get "/workflows/names" 45 | 46 | expect(response.status).to eq(401) 47 | expect(JSON.parse(response.body)).to eq({ 'error' => 'Unauthorized' }) 48 | end 49 | 50 | it "calls the app if the client id is provided" do 51 | user = FactoryGirl.create(:user) 52 | header "CLIENT_ID", user.id 53 | 54 | response = get "/workflows/names" 55 | 56 | expect(response.status).to eq(200) 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/integration/web/debug_api_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | require 'support/request_helper' 33 | 34 | describe Backbeat::Web::DebugAPI, :api_test do 35 | include RequestHelper 36 | 37 | let(:user) { FactoryGirl.create(:user) } 38 | let(:workflow) { FactoryGirl.create(:workflow_with_node, user: user) } 39 | 40 | context "GET /debug/error_workflows" do 41 | it "returns an empty collection if there are no error nodes" do 42 | workflow 43 | 44 | response = get("v2/debug/error_workflows") 45 | body = JSON.parse(response.body) 46 | 47 | expect(response.status).to eq(200) 48 | expect(body.size).to eq(0) 49 | end 50 | 51 | it "returns workflows with nodes in client error state" do 52 | not_errored_workflow = workflow 53 | 54 | errored_workflow = FactoryGirl.create( 55 | :workflow_with_node, 56 | name: :a_unique_name, 57 | user_id: user.id 58 | ) 59 | 60 | errored_workflow.children.first.update_attributes( 61 | current_client_status: :errored, 62 | ) 63 | 64 | response = get("v2/debug/error_workflows") 65 | body = JSON.parse(response.body) 66 | 67 | expect(body.size).to eq(1) 68 | expect(body.first["id"]).to eq(errored_workflow.id) 69 | end 70 | 71 | it "returns workflows scoped to the user" do 72 | user_workflow = workflow 73 | user_workflow.children.first.update_attributes( 74 | current_client_status: :errored, 75 | ) 76 | 77 | other_user_workflow = FactoryGirl.create( 78 | :workflow_with_node, 79 | user: FactoryGirl.create(:user, name: "Other user") 80 | ) 81 | other_user_workflow.children.first.update_attributes( 82 | current_client_status: :errored, 83 | ) 84 | 85 | response = get("v2/debug/error_workflows") 86 | body = JSON.parse(response.body) 87 | 88 | expect(body.size).to eq(1) 89 | expect(body.first["id"]).to eq(user_workflow.id) 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /spec/integration/web/middleware/health_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Web::Middleware::Health, :api_test do 34 | 35 | context "/health" do 36 | it "includes running SHA, current time and status" do 37 | response = get '/health' 38 | 39 | expect(response.status).to eq(200) 40 | expect(response.headers["Content-Type"]).to eq("application/json") 41 | expect(JSON.parse(response.body)).to eq({ 42 | "sha" => Backbeat::Config.revision, 43 | "time" => Time.now.iso8601, 44 | "status" => "OK" 45 | }) 46 | end 47 | 48 | it "returns the status when the db cannot be reached" do 49 | allow(ActiveRecord::Base).to receive(:connection) { raise "Cannot connect" } 50 | 51 | response = get '/health' 52 | 53 | expect(response.status).to eq(200) 54 | expect(response.headers["Content-Type"]).to eq("application/json") 55 | expect(JSON.parse(response.body)).to eq({ 56 | "sha" => Backbeat::Config.revision, 57 | "time" => Time.now.iso8601, 58 | "status" => "DATABASE_UNREACHABLE" 59 | }) 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/integration/web/middleware/heartbeat_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Web::Middleware::Heartbeat, :api_test do 34 | 35 | def with_no_heartbeat 36 | heartbeat = "#{File.dirname(__FILE__)}/../../../../public/heartbeat.txt" 37 | begin 38 | File.delete(heartbeat) 39 | yield 40 | ensure 41 | File.open(heartbeat, 'w') 42 | end 43 | end 44 | 45 | context "/heartbeat.txt" do 46 | it "returns 200 if heartbeat present" do 47 | response = get '/heartbeat.txt' 48 | expect(response.status).to eq(200) 49 | expect(response.headers["Content-Type"]).to eq("text/plain") 50 | expect(response.body).to eq("We have a pulse.") 51 | end 52 | 53 | it "returns 503 if heartbeat missing" do 54 | with_no_heartbeat do 55 | response = get '/heartbeat.txt' 56 | expect(response.status).to eq(503) 57 | expect(response.headers["Content-Type"]).to eq("text/plain") 58 | expect(response.body).to eq("It's dead, Jim.") 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/integration/web/middleware/log_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Web::Middleware::Log, :api_test do 34 | let(:user) { FactoryGirl.create(:user) } 35 | let(:wf) { FactoryGirl.create(:workflow, user: user) } 36 | 37 | before do 38 | allow(Backbeat::Client).to receive(:make_decision) 39 | end 40 | 41 | it "includes the transaction id in the response" do 42 | response = get "v2/workflows/#{wf.id}" 43 | expect(response.status).to eq(200) 44 | expect(response.headers.keys).to include("X-backbeat-tid") 45 | end 46 | 47 | it "logs route details" do 48 | log_count = 0 49 | expect(Backbeat::Logger).to receive(:info).twice do |response_info| 50 | log_count += 1 51 | if log_count == 2 52 | expect(response_info).to eq({ 53 | message: "Request Complete", 54 | request: { 55 | method: "GET", 56 | path: "/v2/workflows/#{wf.id}", 57 | params: {} 58 | }, 59 | response: { 60 | status: 200, 61 | duration: 0.0, 62 | } 63 | }) 64 | end 65 | end 66 | response = get "v2/workflows/#{wf.id}" 67 | expect(response.status).to eq(200) 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/integration/web/middleware/sidekiq_stats_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Web::Middleware::SidekiqStats, :api_test do 34 | 35 | context '/sidekiq_stats' do 36 | it 'catches the request, returns 200 and queue stats' do 37 | stats = double(Sidekiq::Stats, processed: 23, failed: 42, enqueued: 666, scheduled_size: 0, retry_size: 123, queues: {"queue1" => 0, "queue2" => 0} ) 38 | history = double(Sidekiq::Stats::History, processed: {"2013-11-08" => 15}, failed: {"2013-11-08" => 19}) 39 | 40 | q1 = double(Sidekiq::Queue, latency: 10) 41 | q2 = double(Sidekiq::Queue, latency: 20) 42 | expect(Sidekiq::Queue).to receive(:new).with("queue1").and_return(q1) 43 | expect(Sidekiq::Queue).to receive(:new).with("queue2").and_return(q2) 44 | allow(Sidekiq::Stats).to receive_messages(new: stats) 45 | 46 | expect(Sidekiq::Stats::History).to receive(:new).with(1).and_return(history) 47 | 48 | response = get '/sidekiq_stats' 49 | expect(response.status).to eq(200) 50 | 51 | JSON.parse(response.body) == { 52 | "latency" => { "queue1" => 10, "queue2" => 20 }, 53 | "today" => { "processed" => 15, "failed" => 19 }, 54 | "processed" => 23, 55 | "failed" => 42, 56 | "enqueued" => 666, 57 | "scheduled_size" => 0, 58 | "retry_size" => 123 59 | } 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/integration/web/users_api_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require "spec_helper" 32 | 33 | describe Backbeat::Web::UsersAPI, :api_test do 34 | 35 | context "GET /" do 36 | it "returns all user names and ids" do 37 | 2.times do |i| 38 | Backbeat::User.create!({ 39 | name: "User #{i}", 40 | activity_endpoint: "http://#{i}.com", 41 | notification_endpoint: "http://#{i}.com" 42 | }) 43 | end 44 | 45 | response = get "/users" 46 | body = JSON.parse(response.body) 47 | 48 | expect(body.size).to eq(2) 49 | expect(body.first['name']).to eq("User 0") 50 | expect(body.first['id']).to eq(Backbeat::User.order(:name).first.id) 51 | expect(body.first.keys).to_not include('auth_token') 52 | end 53 | end 54 | 55 | context "GET /:id" do 56 | it "returns the name and id of a user" do 57 | user = Backbeat::User.create!({ 58 | name: "User 1", 59 | activity_endpoint: "http://i.com", 60 | notification_endpoint: "http://i.com" 61 | }) 62 | 63 | response = get "/users/#{user.id}" 64 | body = JSON.parse(response.body) 65 | 66 | expect(body.keys).to match_array(['name', 'id']) 67 | expect(body['name']).to eq('User 1') 68 | expect(body['id']).to eq(user.id) 69 | end 70 | 71 | it "400s if bad user id provided" do 72 | response = get "/users/1" 73 | expect(response.status).to eq(400) 74 | end 75 | 76 | it "404s if no user found for given uuid" do 77 | response = get "/users/#{SecureRandom.uuid}" 78 | expect(response.status).to eq(404) 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/integration/workflows/link_nodes_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | require 'support/request_helper' 33 | require 'support/sidekiq_helper' 34 | 35 | describe Backbeat, :api_test do 36 | include RequestHelper 37 | 38 | let(:user) { FactoryGirl.create(:user) } 39 | let(:workflow) { FactoryGirl.create(:workflow, user: user) } 40 | let(:node) { FactoryGirl.create(:node, user: user, workflow: workflow, current_client_status: :received, current_server_status: :sent_to_client) } 41 | let(:workflow_to_link) { FactoryGirl.create(:workflow, user: user, subject: {test: :node}) } 42 | 43 | context "linked" do 44 | it "forces a node to wait to complete until its links complete" do 45 | response = post "workflows/#{workflow_to_link.id}/signal/test", { options: { parent_link_id: node.id } } 46 | 47 | signal_node = workflow_to_link.nodes.first 48 | 49 | expect(workflow_to_link.nodes.count).to eq(1) 50 | expect(signal_node.parent_link).to eq(node) 51 | expect(node.child_links).to eq([signal_node]) 52 | 53 | put "activities/#{node.id}/status/completed" 54 | 55 | WebMock.stub_request(:post, "http://backbeat-client:9000/decision") 56 | .with(:body => decision_hash(signal_node, {current_server_status: :sent_to_client, current_client_status: :received})) 57 | .to_return(:status => 200, :body => "", :headers => {}) 58 | 59 | Backbeat::Workers::AsyncWorker.drain 60 | 61 | expect(node.reload.attributes).to include( 62 | "current_client_status" => "complete", 63 | "current_server_status" => "processing_children" 64 | ) 65 | 66 | put "activities/#{signal_node.id}/status/completed" 67 | 68 | SidekiqHelper.soft_drain 69 | 70 | expect(signal_node.reload.attributes).to include( 71 | "current_client_status" => "complete", 72 | "current_server_status" => "complete" 73 | ) 74 | 75 | expect(node.reload.attributes).to include( 76 | "current_client_status" => "complete", 77 | "current_server_status" => "processing_children" 78 | ) 79 | 80 | SidekiqHelper.soft_drain 81 | 82 | expect(node.reload.attributes).to include( 83 | "current_client_status" => "complete", 84 | "current_server_status" => "complete" 85 | ) 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /spec/integration/workflows/pause_workflow_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | require "support/request_helper" 33 | require "support/sidekiq_helper" 34 | 35 | describe Backbeat, :api_test do 36 | include RequestHelper 37 | 38 | let(:user) { FactoryGirl.create(:user) } 39 | let(:workflow) { FactoryGirl.create(:workflow, user: user) } 40 | 41 | context "Pause Workflow" do 42 | it "prevents all nodes from running until resumed" do 43 | response = post "workflows/#{workflow.id}/signal/test", options: { 44 | client_data: { data: '123' }, 45 | client_metadata: { metadata: '456'} 46 | } 47 | signal = JSON.parse(response.body) 48 | node = workflow.children.where(id: signal['id']).first 49 | 50 | expect(node.attributes).to include( 51 | "current_client_status" => "ready", 52 | "current_server_status" => "ready" 53 | ) 54 | 55 | put "workflows/#{workflow.id}/pause" 56 | 57 | Backbeat::Workers::AsyncWorker.drain 58 | 59 | expect(node.reload.attributes).to include( 60 | "current_client_status" => "ready", 61 | "current_server_status" => "paused" 62 | ) 63 | 64 | response = put "workflows/#{workflow.id}/resume" 65 | 66 | expect(response.status).to eq(200) 67 | 68 | WebMock.stub_request(:post, "http://backbeat-client:9000/decision") 69 | .with(:body => decision_hash(node)) 70 | .to_return(:status => 200, :body => "", :headers => {}) 71 | 72 | allow(Backbeat::Client).to receive(:perform) 73 | 74 | SidekiqHelper.soft_drain 75 | 76 | expect(node.reload.attributes).to include( 77 | "current_client_status" => "received", 78 | "current_server_status" => "sent_to_client" 79 | ) 80 | end 81 | end 82 | end 83 | 84 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | ENV['RACK_ENV'] ||= 'test' 32 | 33 | require File.expand_path('../../config/environment', __FILE__) 34 | 35 | $: << File.expand_path('../', __FILE__) 36 | 37 | require 'bundler' 38 | Bundler.setup(:test) 39 | 40 | require 'rspec' 41 | require 'rack/test' 42 | require 'factory_girl' 43 | require 'database_cleaner' 44 | require 'timecop' 45 | require 'webmock' 46 | require 'rspec-sidekiq' 47 | require 'pry' 48 | require 'securerandom' 49 | require 'ap' 50 | 51 | FactoryGirl.find_definitions 52 | 53 | RSpec::Sidekiq.configure do |config| 54 | config.warn_when_jobs_not_processed_by_sidekiq = false 55 | end 56 | 57 | require 'backbeat/web' 58 | 59 | module ApiTest 60 | include Rack::Test::Methods 61 | 62 | def app 63 | Backbeat::Web::App 64 | end 65 | end 66 | 67 | RSpec.configure do |config| 68 | config.before(:each) do 69 | Timecop.freeze(DateTime.now) 70 | end 71 | 72 | config.after(:each) do 73 | Timecop.return 74 | end 75 | 76 | config.include(ApiTest, :api_test) 77 | 78 | config.before(:each, :api_test) do 79 | if defined?(user) 80 | header 'Client-Id', user.id 81 | header "Authorization", "Token token=\"#{user.auth_token}\"" 82 | end 83 | end 84 | 85 | config.before(:suite) do 86 | DatabaseCleaner.strategy = :transaction 87 | DatabaseCleaner.clean_with(:truncation) 88 | end 89 | 90 | config.around(:each) do |example| 91 | DatabaseCleaner.cleaning do 92 | example.run 93 | end 94 | end 95 | 96 | config.color = true 97 | config.formatter = :documentation 98 | config.order = :random 99 | end 100 | -------------------------------------------------------------------------------- /spec/support/capture.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module Capture 32 | def self.with_out_capture(&block) 33 | capture = StringIO.new 34 | out = $stdout 35 | $stdout = capture 36 | block.call 37 | capture.string 38 | ensure 39 | $stdout = out 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/support/request_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module RequestHelper 32 | def client_activity_data(data = {}) 33 | { 34 | type: 'activity', 35 | name: 'name_1', 36 | client_data: { 37 | params: [ 38 | { firstName: "John", lastName: "Smith" }, 39 | 123 40 | ] 41 | }, 42 | metadata: { version: "v2" }, 43 | mode: :blocking, 44 | retry_interval: 6.hours 45 | }.merge(data) 46 | end 47 | module_function :client_activity_data 48 | 49 | def activity_hash(activity_node, attributes = {}) 50 | attributes = Backbeat::Util.camelize(attributes) 51 | { "activity" => Backbeat::NodePresenter.present(activity_node).merge(attributes) }.to_json 52 | end 53 | 54 | def decision_hash(decision_node, attributes = {}) 55 | attributes = Backbeat::Util.camelize(attributes) 56 | { "decision" => Backbeat::NodePresenter.present(decision_node).merge(attributes) }.to_json 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/support/sidekiq_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | module SidekiqHelper 32 | # Acknowledges perform_in time and does not drain jobs that a drained job enqueues 33 | def soft_drain 34 | jobs = Backbeat::Workers::AsyncWorker.jobs 35 | 36 | #only drain current jobs in the queue 37 | job_count = jobs.count 38 | 39 | job_count.times do |i| 40 | job = jobs[i] 41 | if !job["at"] || Time.now.to_f > job["at"] 42 | worker = Backbeat::Workers::AsyncWorker.new 43 | worker.jid = job['jid'] 44 | args = job['args'] 45 | jobs[i] = nil 46 | worker.perform(*args) 47 | end 48 | end 49 | ensure 50 | jobs.compact! 51 | end 52 | module_function :soft_drain 53 | 54 | def show_jobs_in_queue 55 | Backbeat::Workers::AsyncWorker.jobs.each{|job| ap job} 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/unit/instrument_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require "spec_helper" 32 | require "stringio" 33 | 34 | describe Backbeat::Instrument do 35 | let(:log) { StringIO.new } 36 | 37 | before do 38 | Backbeat::Logger.logger = ::Logger.new(log) 39 | end 40 | 41 | it "logs a started message" do 42 | Backbeat::Instrument.instrument("event", 1) do 43 | :done 44 | end 45 | 46 | expect(log.string).to include("started") 47 | end 48 | 49 | it "runs the block" do 50 | x = 1 51 | 52 | Backbeat::Instrument.instrument("event", 1) do 53 | x += 1 54 | end 55 | 56 | expect(x).to eq(2) 57 | end 58 | 59 | it "logs a succeeded message" do 60 | Backbeat::Instrument.instrument("event", 1) do 61 | :done 62 | end 63 | 64 | expect(log.string).to include("succeeded") 65 | end 66 | 67 | it "logs an error message" do 68 | begin 69 | Backbeat::Instrument.instrument("event", 1) do 70 | raise "Error" 71 | end 72 | rescue 73 | end 74 | 75 | expect(log.string).to include("errored") 76 | end 77 | 78 | it "logs a fallback message" do 79 | bad_error = Class.new(StandardError) do 80 | def to_s 81 | raise "Nope" 82 | end 83 | end 84 | 85 | begin 86 | Backbeat::Instrument.instrument("event", 1) do 87 | raise bad_error 88 | end 89 | rescue 90 | end 91 | 92 | expect(log.string).to include("error_logging_error") 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /spec/unit/logging_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Logger do 34 | class TestLogger 35 | extend Backbeat::Logging 36 | end 37 | 38 | it "logs the revision sha" do 39 | allow(Backbeat::Config).to receive(:revision).and_return("fake_sha") 40 | 41 | expect(Backbeat::Logger).to receive(:add) do |level, message| 42 | expect(message[:revision]).to eq("fake_sha") 43 | end 44 | 45 | TestLogger.debug({ message: "message" }) 46 | end 47 | 48 | it "does not log below the configured log severity level" do 49 | logs = StringIO.new 50 | logger = ::Logger.new(logs) 51 | logger.level = ::Logger::WARN 52 | Backbeat::Logger.logger = logger 53 | 54 | Backbeat::Logger.info({ message: 'Hello' }) 55 | 56 | expect(logs.size).to eq(0) 57 | 58 | Backbeat::Logger.fatal({ message: 'Goodbye' }) 59 | 60 | expect(logs.size).to be > 0 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/unit/models/node_detail_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require "spec_helper" 32 | 33 | describe Backbeat::NodeDetail do 34 | 35 | context "retries remaining" do 36 | it "is invalid if less than 0" do 37 | detail = described_class.create(retries_remaining: -1) 38 | expect(detail.errors["retries_remaining"]).to_not be_empty 39 | end 40 | end 41 | 42 | context "retry_interval" do 43 | it "sets the default retry interval to 20 minutes in seconds" do 44 | detail = described_class.new 45 | expect(detail.valid?).to eq(true) 46 | expect(detail.retry_interval).to eq(20 * 60) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/unit/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require "spec_helper" 32 | 33 | describe Backbeat::User do 34 | 35 | context "#generate_auth_token" do 36 | it "creates a random auth token" do 37 | user = Backbeat::User.create({ 38 | name: "Name", 39 | activity_endpoint: "/activity", 40 | notification_endpoint: "/notify" 41 | }) 42 | 43 | expect(user.auth_token).to_not be_nil 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/unit/presenters_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require "spec_helper" 32 | 33 | describe "Presenters" do 34 | 35 | let(:user) { FactoryGirl.create(:user) } 36 | let(:workflow) { FactoryGirl.create(:workflow_with_node, user: user) } 37 | let(:node) { workflow.children.first } 38 | 39 | describe Backbeat::NodePresenter do 40 | it "serializes a node" do 41 | expect(Backbeat::NodePresenter.present(node)).to eq( 42 | { 43 | id: node.id, 44 | mode: node.mode, 45 | name: node.name, 46 | workflowId: node.workflow_id, 47 | parentId: node.parent_id, 48 | userId: node.user_id, 49 | clientData: node.client_data, 50 | metadata: node.client_metadata, 51 | subject: node.subject, 52 | decider: node.decider, 53 | workflowName: node.workflow.name, 54 | currentServerStatus: node.current_server_status, 55 | currentClientStatus: node.current_client_status, 56 | createdAt: node.created_at 57 | } 58 | ) 59 | end 60 | end 61 | 62 | describe Backbeat::NotificationPresenter do 63 | it "serializers a notification" do 64 | expect(Backbeat::NotificationPresenter.new("A message").present(node)).to eq( 65 | { 66 | activity: Backbeat::NodePresenter.present(node), 67 | notification: { 68 | name: node.name, 69 | message: "A message" 70 | }, 71 | error: {} 72 | } 73 | ) 74 | end 75 | end 76 | 77 | context Backbeat::ErrorPresenter do 78 | it "formats the hash for StandardErrors" do 79 | error = StandardError.new('some_error') 80 | expect(Backbeat::ErrorPresenter.present(error)).to eq({ 81 | errorClass: error.class.to_s, 82 | message: error.message 83 | }) 84 | end 85 | 86 | it "formats the hash for strings" do 87 | error = "an error message" 88 | 89 | expect(Backbeat::ErrorPresenter.present(error)).to eq({ message: error }) 90 | end 91 | 92 | it "returns an empty hash for other other class types" do 93 | error = 1 94 | 95 | expect(Backbeat::ErrorPresenter.present(error)).to eq({}) 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /spec/unit/web/helpers/current_user_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require "spec_helper" 32 | 33 | describe Backbeat::Web::CurrentUserHelper do 34 | FakeApi = Struct.new(:env) do 35 | include Backbeat::Web::CurrentUserHelper 36 | end 37 | 38 | context "#current_user" do 39 | let(:api) { FakeApi.new({ "HTTP_CLIENT_ID" => user.id }) } 40 | let(:user) { FactoryGirl.build(:user) } 41 | 42 | context "user exists" do 43 | before do 44 | allow(Backbeat::User).to receive(:find).and_return(user) 45 | end 46 | 47 | it "returns user" do 48 | expect(api.current_user).to eq(user) 49 | end 50 | end 51 | 52 | context "user does not exist" do 53 | before do 54 | allow(Backbeat::User).to receive(:find).and_raise(ActiveRecord::RecordNotFound.new) 55 | end 56 | 57 | it "returns false" do 58 | expect(api.current_user).to eq(false) 59 | end 60 | end 61 | 62 | context "connection error occurs" do 63 | before do 64 | allow(Backbeat::User).to receive(:find).and_raise(StandardError.new("could not connect to db!")) 65 | end 66 | 67 | it "returns false" do 68 | expect(api.current_user).to eq(false) 69 | end 70 | 71 | it "logs error" do 72 | expect(Backbeat::Logger).to receive(:info).with( 73 | message: "Error occurred while finding user", 74 | error: "could not connect to db!", 75 | backtrace: anything 76 | ).and_call_original 77 | 78 | api.current_user 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /spec/unit/workers/log_queues_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Workers::LogQueues do 34 | context "log_count" do 35 | def expect_info(type, count_subject, count) 36 | expect(Backbeat::Logger).to receive(:add) do |_, log| 37 | expect(log[:source]).to eq("Backbeat::Workers::LogQueues") 38 | expect(log[:data][:type]).to eq(type) 39 | expect(log[:data][:subject]).to eq(count_subject) 40 | expect(log[:data][:count]).to eq(count) 41 | end 42 | end 43 | 44 | it "logs info with the correct info" do 45 | allow_any_instance_of(Sidekiq::RetrySet).to receive(:size).and_return(1) 46 | allow_any_instance_of(Sidekiq::ScheduledSet).to receive(:size).and_return(3) 47 | 48 | sidekiq_queues = {"queue_1"=>10, "queue_2"=>0} 49 | allow_any_instance_of(Sidekiq::Stats).to receive(:queues).and_return(sidekiq_queues) 50 | 51 | expect_info(:queue, "retry", 1) 52 | expect_info(:queue, "schedule" ,3) 53 | expect_info(:queue, "queue_1", 10) 54 | expect_info(:queue, "queue_2", 0) 55 | 56 | subject.perform 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/unit/workers/middleware/transaction_id_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require 'spec_helper' 32 | 33 | describe Backbeat::Workers::Middleware::TransactionId do 34 | it "sets the transaction id and then yields" do 35 | yielded = false 36 | expect(Backbeat::Logger.tid).to be_nil 37 | subject.call do 38 | expect(Backbeat::Logger.tid).not_to be_nil 39 | yielded = true 40 | end 41 | expect(Backbeat::Logger.tid).to be_nil 42 | expect(yielded).to eq(true) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /torquebox_init.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Groupon, Inc. 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # Neither the name of GROUPON nor the names of its contributors may be 16 | # used to endorse or promote products derived from this software without 17 | # specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | require File.expand_path('../config/environment', __FILE__) 32 | Bundler.require(:torquebox) 33 | --------------------------------------------------------------------------------