cangaroo 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|