cangaroo 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/Rakefile +13 -11
- data/app/controllers/cangaroo/endpoint_controller.rb +24 -4
- data/app/interactors/cangaroo/count_json_object.rb +3 -0
- data/app/interactors/cangaroo/run_polls.rb +12 -0
- data/app/interactors/cangaroo/validate_json_schema.rb +9 -3
- data/app/jobs/cangaroo/job.rb +9 -27
- data/app/jobs/cangaroo/poll_job.rb +67 -0
- data/app/models/cangaroo/connection.rb +10 -2
- data/app/models/cangaroo/poll_timestamp.rb +17 -0
- data/db/migrate/20151030140821_add_parameters_to_cangaroo_connection.rb +1 -1
- data/db/migrate/20160317020230_create_cangaroo_poll_timestamps.rb +12 -0
- data/lib/cangaroo.rb +2 -0
- data/lib/cangaroo/class_configuration.rb +24 -0
- data/lib/cangaroo/engine.rb +2 -0
- data/lib/cangaroo/logger.rb +66 -0
- data/lib/cangaroo/version.rb +1 -1
- data/lib/cangaroo/webhook/client.rb +21 -3
- data/lib/tasks/cangaroo_tasks.rake +7 -4
- data/spec/controllers/cangaroo/endpoint_controller_spec.rb +75 -35
- data/spec/fixtures/json_payload_connection_response.json +1 -0
- data/spec/fixtures/json_payload_empty.json +3 -0
- data/spec/interactors/cangaroo/perform_jobs_spec.rb +28 -13
- data/spec/interactors/cangaroo/run_polls_spec.rb +18 -0
- data/spec/interactors/cangaroo/validate_json_schema_spec.rb +8 -0
- data/spec/jobs/cangaroo/job_spec.rb +12 -1
- data/spec/jobs/cangaroo/poll_job_spec.rb +107 -0
- data/spec/lib/cangaroo/webhook/client_spec.rb +38 -0
- data/spec/rails_helper.rb +18 -44
- data/spec/support/database_cleaner.rb +14 -0
- data/spec/support/factory_girl.rb +5 -0
- data/spec/support/rails_app.rb +29 -0
- data/spec/support/shoulda_matchers.rb +8 -0
- data/spec/support/webmock.rb +8 -0
- metadata +71 -87
- data/spec/dummy/README.rdoc +0 -28
- data/spec/dummy/Rakefile +0 -6
- data/spec/dummy/app/assets/javascripts/application.js +0 -13
- data/spec/dummy/app/assets/stylesheets/application.css +0 -15
- data/spec/dummy/app/controllers/application_controller.rb +0 -5
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/bin/bundle +0 -3
- data/spec/dummy/bin/rails +0 -4
- data/spec/dummy/bin/rake +0 -4
- data/spec/dummy/bin/setup +0 -29
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/config/application.rb +0 -31
- data/spec/dummy/config/boot.rb +0 -5
- data/spec/dummy/config/database.yml +0 -11
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -41
- data/spec/dummy/config/environments/production.rb +0 -79
- data/spec/dummy/config/environments/test.rb +0 -42
- data/spec/dummy/config/initializers/assets.rb +0 -11
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/spec/dummy/config/initializers/inflections.rb +0 -16
- data/spec/dummy/config/initializers/mime_types.rb +0 -4
- data/spec/dummy/config/initializers/session_store.rb +0 -3
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -9
- data/spec/dummy/config/locales/en.yml +0 -23
- data/spec/dummy/config/routes.rb +0 -4
- data/spec/dummy/config/secrets.yml +0 -22
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +0 -29
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/cangaroo.log +0 -0
- data/spec/dummy/log/development.log +0 -4024
- data/spec/dummy/log/test.log +0 -166964
- data/spec/dummy/public/404.html +0 -67
- data/spec/dummy/public/422.html +0 -67
- data/spec/dummy/public/500.html +0 -66
- data/spec/dummy/public/favicon.ico +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56de926de5b5e5d494a4a1f132e854a171191543
|
4
|
+
data.tar.gz: 94b41bce37f5b18794eabb1fa5a9cf623bb0087e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e927e1bf09605b62d8690aeed1c14599949fc5c3d7b5611626a23d67eb6006b0ba33a091e1dbbc23398f322119b0889b4e8fc170bb41b39977e011caa3e79d07
|
7
|
+
data.tar.gz: 2f24253cc3a8a1f39f07ed74624963e59034c11da0d75d492367ee2642469622d6c8993a3dc1f150eca0df429875b48958444d36ee786f96b0ff7e82c02ec40a
|
data/MIT-LICENSE
CHANGED
data/Rakefile
CHANGED
@@ -5,6 +5,14 @@ rescue LoadError
|
|
5
5
|
end
|
6
6
|
|
7
7
|
require 'rdoc/task'
|
8
|
+
require 'bundler/gem_tasks'
|
9
|
+
require 'appraisal'
|
10
|
+
require 'rspec/core'
|
11
|
+
require 'rspec/core/rake_task'
|
12
|
+
|
13
|
+
Bundler::GemHelper.install_tasks
|
14
|
+
|
15
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f }
|
8
16
|
|
9
17
|
RDoc::Task.new(:rdoc) do |rdoc|
|
10
18
|
rdoc.rdoc_dir = 'rdoc'
|
@@ -14,19 +22,13 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
14
22
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
23
|
end
|
16
24
|
|
17
|
-
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
18
|
-
load 'rails/tasks/engine.rake'
|
19
|
-
|
20
25
|
load 'rails/tasks/statistics.rake'
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f }
|
27
|
+
desc 'Run Cangaroo specs.'
|
28
|
+
RSpec::Core::RakeTask.new(:spec)
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
desc 'Run all specs in spec directory (excluding plugin specs)'
|
30
|
-
RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
|
30
|
+
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
31
|
+
task :default => :appraisal
|
32
|
+
end
|
31
33
|
|
32
34
|
task default: :spec
|
@@ -18,8 +18,10 @@ module Cangaroo
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def handle_error
|
22
|
-
|
21
|
+
def handle_error(exception)
|
22
|
+
if Rails.env.development?
|
23
|
+
raise(exception)
|
24
|
+
else
|
23
25
|
render json: { error: 'Something went wrong!' }, status: 500
|
24
26
|
end
|
25
27
|
end
|
@@ -39,11 +41,29 @@ module Cangaroo
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def key
|
42
|
-
|
44
|
+
if Rails.configuration.cangaroo.basic_auth
|
45
|
+
if !ActionController::HttpAuthentication::Basic.has_basic_credentials?(request)
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
user, pass = ActionController::HttpAuthentication::Basic::user_name_and_password(request)
|
50
|
+
user
|
51
|
+
else
|
52
|
+
request.headers['X-Hub-Store']
|
53
|
+
end
|
43
54
|
end
|
44
55
|
|
45
56
|
def token
|
46
|
-
|
57
|
+
if Rails.configuration.cangaroo.basic_auth
|
58
|
+
if !ActionController::HttpAuthentication::Basic.has_basic_credentials?(request)
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
|
62
|
+
user, pass = ActionController::HttpAuthentication::Basic::user_name_and_password(request)
|
63
|
+
pass
|
64
|
+
else
|
65
|
+
request.headers['X-Hub-Access-Token']
|
66
|
+
end
|
47
67
|
end
|
48
68
|
end
|
49
69
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Cangaroo
|
2
2
|
class CountJsonObject
|
3
|
+
include Cangaroo::Log
|
3
4
|
include Interactor
|
4
5
|
|
5
6
|
before :prepare_context
|
@@ -9,6 +10,8 @@ module Cangaroo
|
|
9
10
|
o[k] = v.size
|
10
11
|
o
|
11
12
|
end
|
13
|
+
|
14
|
+
log.info 'total consumed payloads', count: context.object_count
|
12
15
|
end
|
13
16
|
|
14
17
|
private
|
@@ -3,13 +3,13 @@ module Cangaroo
|
|
3
3
|
include Interactor
|
4
4
|
|
5
5
|
SCHEMA = {
|
6
|
+
'id': 'Cangaroo Object',
|
6
7
|
'type': 'object',
|
7
8
|
'minProperties': 1,
|
8
9
|
'additionalProperties': false,
|
9
10
|
'patternProperties': {
|
10
11
|
'^[a-z]*$': {
|
11
12
|
'type': 'array',
|
12
|
-
'minItems': 1,
|
13
13
|
'items': {
|
14
14
|
'type': 'object',
|
15
15
|
'required': ['id'],
|
@@ -26,8 +26,13 @@ module Cangaroo
|
|
26
26
|
before :prepare_context
|
27
27
|
|
28
28
|
def call
|
29
|
-
JSON::Validator.fully_validate(SCHEMA, context.json_body)
|
30
|
-
|
29
|
+
validation_response = JSON::Validator.fully_validate(SCHEMA, context.json_body)
|
30
|
+
|
31
|
+
if validation_response.empty?
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
|
35
|
+
context.fail!(message: validation_response.join(', '), error_code: 500)
|
31
36
|
end
|
32
37
|
|
33
38
|
private
|
@@ -35,6 +40,7 @@ module Cangaroo
|
|
35
40
|
def prepare_context
|
36
41
|
context.request_id = context.json_body.delete('request_id')
|
37
42
|
context.summary = context.json_body.delete('summary')
|
43
|
+
context.parameters = context.json_body.delete('parameters')
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
data/app/jobs/cangaroo/job.rb
CHANGED
@@ -1,21 +1,12 @@
|
|
1
1
|
module Cangaroo
|
2
2
|
class Job < ActiveJob::Base
|
3
|
-
|
4
|
-
|
5
|
-
class_attribute :connection_name, :webhook_path, :webhook_parameters
|
6
|
-
class << self
|
7
|
-
def connection(name)
|
8
|
-
self.connection_name = name
|
9
|
-
end
|
3
|
+
include Cangaroo::ClassConfiguration
|
10
4
|
|
11
|
-
|
12
|
-
self.webhook_path = path
|
13
|
-
end
|
5
|
+
queue_as :cangaroo
|
14
6
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
7
|
+
class_configuration :connection
|
8
|
+
class_configuration :path, ''
|
9
|
+
class_configuration :parameters, {}
|
19
10
|
|
20
11
|
def perform(*)
|
21
12
|
restart_flow(connection_request)
|
@@ -37,6 +28,9 @@ module Cangaroo
|
|
37
28
|
end
|
38
29
|
|
39
30
|
def restart_flow(response)
|
31
|
+
# if no json was returned, the response should be discarded
|
32
|
+
return if response.blank?
|
33
|
+
|
40
34
|
PerformFlow.call(
|
41
35
|
source_connection: destination_connection,
|
42
36
|
json_body: response.to_json,
|
@@ -57,19 +51,7 @@ module Cangaroo
|
|
57
51
|
end
|
58
52
|
|
59
53
|
def destination_connection
|
60
|
-
@connection ||= Cangaroo::Connection.find_by!(name:
|
61
|
-
end
|
62
|
-
|
63
|
-
def connection_name
|
64
|
-
self.class.connection_name
|
65
|
-
end
|
66
|
-
|
67
|
-
def path
|
68
|
-
self.class.webhook_path || ''
|
69
|
-
end
|
70
|
-
|
71
|
-
def parameters
|
72
|
-
self.class.webhook_parameters || {}
|
54
|
+
@connection ||= Cangaroo::Connection.find_by!(name: connection)
|
73
55
|
end
|
74
56
|
end
|
75
57
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Cangaroo
|
2
|
+
class PollJob < ActiveJob::Base
|
3
|
+
include Cangaroo::Log
|
4
|
+
include Cangaroo::ClassConfiguration
|
5
|
+
|
6
|
+
queue_as :cangaroo
|
7
|
+
|
8
|
+
class_configuration :connection
|
9
|
+
class_configuration :frequency, 1.day
|
10
|
+
class_configuration :path, ''
|
11
|
+
class_configuration :parameters, {}
|
12
|
+
|
13
|
+
def perform(*)
|
14
|
+
log.set_context(self)
|
15
|
+
|
16
|
+
last_poll = Time.at(arguments.first.fetch(:last_poll)).to_datetime
|
17
|
+
current_time = DateTime.now
|
18
|
+
|
19
|
+
if !perform?(current_time)
|
20
|
+
log.info 'skipping poll'
|
21
|
+
return
|
22
|
+
end
|
23
|
+
|
24
|
+
log.info 'initiating poll', last_poll: last_poll.to_i
|
25
|
+
|
26
|
+
response = Cangaroo::Webhook::Client.new(destination_connection, path)
|
27
|
+
.post({ last_poll: last_poll_timestamp.to_i }, @job_id, parameters)
|
28
|
+
|
29
|
+
log.info 'processing poll results'
|
30
|
+
|
31
|
+
command = HandleRequest.call(
|
32
|
+
key: destination_connection.key,
|
33
|
+
token: destination_connection.token,
|
34
|
+
json_body: response.to_json,
|
35
|
+
jobs: Rails.configuration.cangaroo.jobs
|
36
|
+
)
|
37
|
+
|
38
|
+
if !command.success?
|
39
|
+
fail Cangaroo::Webhook::Error, command.message
|
40
|
+
end
|
41
|
+
|
42
|
+
log.info 'updating last poll', last_poll: current_time
|
43
|
+
|
44
|
+
last_job_poll = Cangaroo::PollTimestamp.for_class(self.class)
|
45
|
+
last_job_poll.value = current_time
|
46
|
+
last_job_poll.save!
|
47
|
+
|
48
|
+
log.reset_context!
|
49
|
+
end
|
50
|
+
|
51
|
+
def perform?(execution_time)
|
52
|
+
last_poll_timestamp.nil? ||
|
53
|
+
execution_time.to_i - last_poll_timestamp.to_i > self.class.frequency
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def last_poll_timestamp
|
59
|
+
@last_poll_timestamp ||= Cangaroo::PollTimestamp.for_class(self.class).value
|
60
|
+
end
|
61
|
+
|
62
|
+
def destination_connection
|
63
|
+
@connection ||= Cangaroo::Connection.find_by!(name: connection)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -2,11 +2,19 @@ module Cangaroo
|
|
2
2
|
class Connection < ActiveRecord::Base
|
3
3
|
serialize :parameters
|
4
4
|
|
5
|
-
validates :name, :url, :
|
6
|
-
validates :
|
5
|
+
validates :name, :url, :token, presence: true, uniqueness: true
|
6
|
+
validates :key, presence: true, uniqueness: true, if: -> { !Rails.configuration.cangaroo.basic_auth }
|
7
|
+
|
8
|
+
after_initialize :set_default_parameters
|
7
9
|
|
8
10
|
def self.authenticate(key, token)
|
9
11
|
where(key: key, token: token).first
|
10
12
|
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def set_default_parameters
|
17
|
+
self.parameters ||= {}
|
18
|
+
end
|
11
19
|
end
|
12
20
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Cangaroo
|
2
|
+
class PollTimestamp < ActiveRecord::Base
|
3
|
+
serialize :value
|
4
|
+
|
5
|
+
belongs_to :connection
|
6
|
+
|
7
|
+
validates_uniqueness_of :job, scope: :connection
|
8
|
+
|
9
|
+
def self.for_class(klass)
|
10
|
+
self.where(
|
11
|
+
job: klass.to_s,
|
12
|
+
connection: Cangaroo::Connection.find_by!(name: klass.connection)
|
13
|
+
).first_or_initialize
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateCangarooPollTimestamps < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :cangaroo_poll_timestamps do |t|
|
4
|
+
t.string :job
|
5
|
+
t.references :connection
|
6
|
+
t.text :value
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :cangaroo_poll_timestamps, [:job], :name => 'index_cangaroo_poll_timestamps_on_job'
|
11
|
+
end
|
12
|
+
end
|
data/lib/cangaroo.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Cangaroo
|
2
|
+
module ClassConfiguration
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def class_configuration(key, default = nil)
|
7
|
+
class_attribute :"_#{key}"
|
8
|
+
|
9
|
+
define_singleton_method(key) do |*args|
|
10
|
+
if args.empty?
|
11
|
+
return self.send(:"_#{key}") || default
|
12
|
+
end
|
13
|
+
|
14
|
+
self.send(:"_#{key}=", args.first)
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method(key) do
|
18
|
+
self.send(:"_#{key}") || default
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/cangaroo/engine.rb
CHANGED
@@ -12,6 +12,8 @@ module Cangaroo
|
|
12
12
|
config.before_configuration do
|
13
13
|
Rails.configuration.cangaroo = ActiveSupport::OrderedOptions.new
|
14
14
|
Rails.configuration.cangaroo.jobs = []
|
15
|
+
Rails.configuration.cangaroo.poll_jobs = []
|
16
|
+
Rails.configuration.cangaroo.basic_auth = false
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Cangaroo
|
4
|
+
module Log
|
5
|
+
|
6
|
+
def log
|
7
|
+
Cangaroo::Log::Writer.instance
|
8
|
+
end
|
9
|
+
|
10
|
+
class Writer
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
attr_reader :default_tags
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@l = Logger.new(STDOUT)
|
17
|
+
@default_tags = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset_context!
|
21
|
+
@default_tags = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_context(job)
|
25
|
+
reset_context!
|
26
|
+
|
27
|
+
@default_tags.merge!({
|
28
|
+
job: job.class.to_s,
|
29
|
+
job_id: job.job_id,
|
30
|
+
connection: job.class.connection
|
31
|
+
})
|
32
|
+
end
|
33
|
+
|
34
|
+
def error(msg, opts={})
|
35
|
+
@l.error("#{msg}: #{stringify_tags(opts)}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def info(msg, opts={})
|
39
|
+
@l.info("#{msg}: #{stringify_tags(opts)}")
|
40
|
+
end
|
41
|
+
|
42
|
+
def debug(msg, opts={})
|
43
|
+
@l.debug("#{msg}: #{stringify_tags(opts)}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def warn(msg, opts={})
|
47
|
+
@l.warn("#{msg}: #{stringify_tags(opts)}")
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def stringify_tags(additional_tags)
|
53
|
+
additional_tags = additional_tags.dup
|
54
|
+
|
55
|
+
if translation = additional_tags.delete(:translation)
|
56
|
+
additional_tags[:translation_id] = translation.id
|
57
|
+
# TODO extract other important info from the translation
|
58
|
+
end
|
59
|
+
|
60
|
+
@default_tags.merge(additional_tags).map { |k,v| "#{k}=#{v}" }.join(' ')
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|