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
data/lib/cangaroo/version.rb
CHANGED
@@ -14,11 +14,29 @@ module Cangaroo
|
|
14
14
|
|
15
15
|
def post(payload, request_id, parameters)
|
16
16
|
request_body = body(payload, request_id, parameters).to_json
|
17
|
-
|
17
|
+
|
18
|
+
request_options = {
|
19
|
+
headers: headers,
|
20
|
+
body: request_body
|
21
|
+
}
|
22
|
+
|
23
|
+
if Rails.configuration.cangaroo.basic_auth
|
24
|
+
request_options.merge!(
|
25
|
+
basic_auth: {
|
26
|
+
username: connection.key,
|
27
|
+
password: connection.token
|
28
|
+
}
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
req = self.class.post(url, request_options)
|
33
|
+
|
18
34
|
if req.response.code == '200'
|
19
35
|
req.parsed_response
|
36
|
+
elsif req.response.code == '204'
|
37
|
+
''
|
20
38
|
else
|
21
|
-
fail Cangaroo::Webhook::Error, req.parsed_response['summary']
|
39
|
+
fail Cangaroo::Webhook::Error, (req.parsed_response['summary'] rescue req.response)
|
22
40
|
end
|
23
41
|
end
|
24
42
|
|
@@ -32,7 +50,7 @@ module Cangaroo
|
|
32
50
|
|
33
51
|
def headers
|
34
52
|
{
|
35
|
-
'X_HUB_TOKEN' => connection.token,
|
53
|
+
'X_HUB_TOKEN' => connection.token || '',
|
36
54
|
'Content-Type' => 'application/json',
|
37
55
|
'Accept' => 'application/json'
|
38
56
|
}
|
@@ -8,65 +8,105 @@ module Cangaroo
|
|
8
8
|
|
9
9
|
before do
|
10
10
|
request.headers['Content-Type'] = 'application/json'
|
11
|
-
request.headers['X-Hub-Store'] = connection.key
|
12
|
-
request.headers['X-Hub-Access-Token'] = connection.token
|
13
11
|
end
|
14
12
|
|
15
|
-
|
13
|
+
context 'when wombat authentication is enabled' do
|
16
14
|
before do
|
17
|
-
|
15
|
+
request.headers['X-Hub-Store'] = connection.key
|
16
|
+
request.headers['X-Hub-Access-Token'] = connection.token
|
18
17
|
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
post :create
|
25
|
-
expect(response.status).to eq(406)
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'when success' do
|
29
|
-
let(:auth_headers) {}
|
19
|
+
describe '#create' do
|
20
|
+
before do
|
21
|
+
post :create, request_payload
|
22
|
+
end
|
30
23
|
|
31
|
-
it '
|
24
|
+
it 'accepts only application/json requests' do
|
32
25
|
expect(response.status).to eq(202)
|
33
|
-
end
|
34
26
|
|
35
|
-
|
36
|
-
|
37
|
-
expect(
|
27
|
+
request.headers['Content-Type'] = 'text/html'
|
28
|
+
post :create
|
29
|
+
expect(response.status).to eq(406)
|
38
30
|
end
|
39
|
-
end
|
40
31
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
32
|
+
context 'when success' do
|
33
|
+
let(:auth_headers) {}
|
34
|
+
|
35
|
+
it 'responds with 200' do
|
36
|
+
expect(response.status).to eq(202)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'responds with the number of objects received in payload' do
|
40
|
+
res = JSON.parse(response.body)
|
41
|
+
expect(res).to eq('orders' => 2, 'shipments' => 2)
|
42
|
+
end
|
45
43
|
end
|
46
44
|
|
47
|
-
|
48
|
-
|
45
|
+
context 'when error' do
|
46
|
+
before do
|
47
|
+
request.headers['X-Hub-Access-Token'] = 'wrongtoken'
|
48
|
+
post :create, request_payload
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'responds with the command error code' do
|
52
|
+
expect(response.status).to eq(401)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'responds with error messages in the body' do
|
56
|
+
expect(JSON.parse(response.body)['error']).to be_present
|
57
|
+
end
|
49
58
|
end
|
50
59
|
|
51
|
-
|
52
|
-
|
60
|
+
context 'when an exception was raised' do
|
61
|
+
before do
|
62
|
+
HandleRequest.stub(:call).and_raise('An error')
|
63
|
+
post :create, request_payload
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'responds with 500' do
|
67
|
+
expect(response.status).to eq(500)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'responds with error messages in the body' do
|
71
|
+
expect(JSON.parse(response.body)['error']).to eq 'Something went wrong!'
|
72
|
+
end
|
53
73
|
end
|
54
74
|
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when basic auth is enabled' do
|
78
|
+
before do
|
79
|
+
Rails.configuration.cangaroo.basic_auth = true
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#create' do
|
83
|
+
it 'successfully authorized against a connection key and token' do
|
84
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic
|
85
|
+
.encode_credentials(connection.key, connection.token)
|
55
86
|
|
56
|
-
context 'when an exception was raised' do
|
57
|
-
before do
|
58
|
-
HandleRequest.stub(:call).and_raise('An error')
|
59
87
|
post :create, request_payload
|
88
|
+
|
89
|
+
expect(response.status).to eq(202)
|
60
90
|
end
|
61
91
|
|
62
|
-
it '
|
63
|
-
|
92
|
+
it 'successfully authenticates against a connection token' do
|
93
|
+
connection.update(key: '')
|
94
|
+
|
95
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic
|
96
|
+
.encode_credentials('', connection.token)
|
97
|
+
|
98
|
+
post :create, request_payload
|
99
|
+
|
100
|
+
expect(response.status).to eq(202)
|
64
101
|
end
|
65
102
|
|
66
|
-
it '
|
67
|
-
|
103
|
+
it 'fails to authenticate when basic auth is not provided' do
|
104
|
+
post :create, request_payload
|
105
|
+
|
106
|
+
expect(response.status).to eq(401)
|
68
107
|
end
|
69
108
|
end
|
70
109
|
end
|
110
|
+
|
71
111
|
end
|
72
112
|
end
|
@@ -18,25 +18,40 @@ describe Cangaroo::PerformJobs do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
describe '.call' do
|
21
|
-
let(:json_body) { load_fixture('json_payload_ok.json') }
|
22
|
-
|
23
21
|
let(:job_a) { double('job_a', perform?: true, enqueue: nil) }
|
24
22
|
let(:job_b) { double('job_b', perform?: false, enqueue: nil) }
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
context 'payload with objects' do
|
25
|
+
let(:json_body) { load_fixture('json_payload_ok.json') }
|
26
|
+
|
27
|
+
it 'instantiates jobs' do
|
28
|
+
context
|
29
|
+
expect(JobA).to have_received(:new).exactly(4).times
|
30
|
+
expect(JobB).to have_received(:new).exactly(4).times
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'enqueues only cangaroo jobs that can perform' do
|
34
|
+
context
|
35
|
+
expect(job_a).to have_received(:enqueue).exactly(4).times
|
36
|
+
expect(job_b).to_not have_received(:enqueue)
|
37
|
+
end
|
31
38
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
expect(job_b).to_not have_received(:enqueue)
|
39
|
+
it 'succeeds' do
|
40
|
+
expect(context).to be_a_success
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
|
-
|
39
|
-
|
44
|
+
context 'payload with no objects' do
|
45
|
+
let(:json_body) { load_fixture('json_payload_empty.json') }
|
46
|
+
|
47
|
+
it 'succeeds' do
|
48
|
+
context
|
49
|
+
|
50
|
+
expect(context).to be_a_success
|
51
|
+
expect(job_a).to_not have_received(:enqueue)
|
52
|
+
expect(job_b).to_not have_received(:enqueue)
|
53
|
+
end
|
40
54
|
end
|
55
|
+
|
41
56
|
end
|
42
57
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
class PollJobA < Cangaroo::PollJob; end
|
4
|
+
class PollJobB < Cangaroo::PollJob; end
|
5
|
+
|
6
|
+
describe Cangaroo::RunPolls do
|
7
|
+
let(:connection) { create(:cangaroo_connection) }
|
8
|
+
|
9
|
+
# let(:job_a) { double('job_a', enqueue: nil) }
|
10
|
+
# let(:job_b) { double('job_b', enqueue: nil) }
|
11
|
+
|
12
|
+
it 'enques all polling jobs' do
|
13
|
+
expect_any_instance_of(PollJobA).to receive(:enqueue).once
|
14
|
+
expect_any_instance_of(PollJobB).to receive(:enqueue).once
|
15
|
+
|
16
|
+
Cangaroo::RunPolls.call(jobs: [PollJobA, PollJobB])
|
17
|
+
end
|
18
|
+
end
|
@@ -12,6 +12,14 @@ describe Cangaroo::ValidateJsonSchema do
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
context 'when json is well formatted and empty' do
|
16
|
+
let(:json_body) { parse_fixture('json_payload_empty.json') }
|
17
|
+
|
18
|
+
it 'succeeds' do
|
19
|
+
expect(context).to be_a_success
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
context 'when json is well formatted and come from a connection' do
|
16
24
|
let(:json_body) { parse_fixture('json_payload_connection_response.json') }
|
17
25
|
|
@@ -25,7 +25,7 @@ module Cangaroo
|
|
25
25
|
end
|
26
26
|
|
27
27
|
before do
|
28
|
-
client.
|
28
|
+
allow(client).to receive(:post).and_return(connection_response)
|
29
29
|
allow(Cangaroo::Webhook::Client).to receive(:new).and_return(client)
|
30
30
|
allow(Cangaroo::PerformFlow).to receive(:call)
|
31
31
|
end
|
@@ -49,10 +49,21 @@ module Cangaroo
|
|
49
49
|
it 'restart the flow' do
|
50
50
|
job.perform
|
51
51
|
expect(Cangaroo::PerformFlow).to have_received(:call)
|
52
|
+
.once
|
52
53
|
.with(source_connection: destination_connection,
|
53
54
|
json_body: connection_response.to_json,
|
54
55
|
jobs: Rails.configuration.cangaroo.jobs)
|
55
56
|
end
|
57
|
+
|
58
|
+
context 'endpoint provides a empty response' do
|
59
|
+
it 'should not restart the flow' do
|
60
|
+
allow(client).to receive(:post).and_return('')
|
61
|
+
|
62
|
+
job.perform
|
63
|
+
|
64
|
+
expect(Cangaroo::PerformFlow).to_not have_received(:call)
|
65
|
+
end
|
66
|
+
end
|
56
67
|
end
|
57
68
|
|
58
69
|
describe '#perform?' do
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Cangaroo::PollJob, type: :job do
|
4
|
+
class FakePollJob < Cangaroo::PollJob
|
5
|
+
connection :store
|
6
|
+
path '/webhook_path'
|
7
|
+
parameters(email: 'info@nebulab.it')
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:job_class) { FakePollJob }
|
11
|
+
let(:job) { job_class.new({ last_poll: Time.now.to_i }) }
|
12
|
+
let(:successful_pull_payload) { parse_fixture('json_payload_ok.json') }
|
13
|
+
|
14
|
+
before do
|
15
|
+
create(:cangaroo_connection)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#perform?' do
|
19
|
+
before do
|
20
|
+
allow_any_instance_of(Cangaroo::Webhook::Client).to receive(:post)
|
21
|
+
.and_return(successful_pull_payload)
|
22
|
+
|
23
|
+
allow(Cangaroo::HandleRequest).to receive(:call).
|
24
|
+
and_return(double(success?: true))
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'runs a poll if one has never been run' do
|
28
|
+
expect(FakePollJob.new.perform?(DateTime.now)).to eq(true)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'runs a poll if the poll frequency delta is reached' do
|
32
|
+
last_poll = Cangaroo::PollTimestamp.for_class(FakePollJob)
|
33
|
+
last_poll.value = DateTime.now - FakePollJob.frequency - 1.second
|
34
|
+
last_poll.save
|
35
|
+
|
36
|
+
expect(FakePollJob.new.perform?(DateTime.now)).to eq(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not run a poll if the time passed is less than the frequency' do
|
40
|
+
last_poll = Cangaroo::PollTimestamp.for_class(FakePollJob)
|
41
|
+
last_poll.value = DateTime.now - FakePollJob.frequency + 1.second
|
42
|
+
last_poll.save
|
43
|
+
|
44
|
+
expect(FakePollJob.new.perform?(DateTime.now)).to eq(false)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#perform' do
|
49
|
+
|
50
|
+
context 'pull is successful' do
|
51
|
+
|
52
|
+
before do
|
53
|
+
allow(Cangaroo::HandleRequest).to receive(:call).
|
54
|
+
and_return(double(success?: true))
|
55
|
+
|
56
|
+
allow_any_instance_of(Cangaroo::PollJob).to receive(:perform?)
|
57
|
+
.and_return(true)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'updates the poll timestamp' do
|
61
|
+
Cangaroo::Webhook::Client.any_instance.stub(:post).and_return(successful_pull_payload)
|
62
|
+
|
63
|
+
job.perform
|
64
|
+
|
65
|
+
last_poll_timestamp = Cangaroo::PollTimestamp.for_class(FakePollJob)
|
66
|
+
expect(last_poll_timestamp.job).to eq(job.class.to_s)
|
67
|
+
expect(last_poll_timestamp.connection.name).to eq(job.class.connection.to_s)
|
68
|
+
expect(last_poll_timestamp.value).to be <= DateTime.now
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'handles a empty response' do
|
72
|
+
Cangaroo::Webhook::Client.any_instance.stub(:post).and_return('')
|
73
|
+
|
74
|
+
job.perform
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'handles a response with a empty array' do
|
78
|
+
Cangaroo::Webhook::Client.any_instance.stub(:post).and_return(
|
79
|
+
parse_fixture('json_payload_empty.json')
|
80
|
+
)
|
81
|
+
|
82
|
+
job.perform
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'pull fails' do
|
88
|
+
|
89
|
+
before do
|
90
|
+
Cangaroo::Webhook::Client.any_instance.stub(:post).and_return(parse_fixture('json_payload_ok.json'))
|
91
|
+
|
92
|
+
allow(Cangaroo::HandleRequest).to receive(:call).and_return(double(
|
93
|
+
success?: false,
|
94
|
+
message: 'bad failure'
|
95
|
+
))
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'does not update timestamp if pull fails' do
|
99
|
+
expect { job.perform }.to raise_error(Cangaroo::Webhook::Error)
|
100
|
+
|
101
|
+
expect(Cangaroo::PollTimestamp.for_class(FakePollJob).id).to be_nil
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -37,6 +37,21 @@ module Cangaroo
|
|
37
37
|
}.to_json)
|
38
38
|
end
|
39
39
|
|
40
|
+
context 'when basic auth is enabled' do
|
41
|
+
before { Rails.configuration.cangaroo.basic_auth = true }
|
42
|
+
|
43
|
+
it 'sends key and token as basic auth username and password' do
|
44
|
+
expect(client.class).to receive(:post)
|
45
|
+
.with(anything, hash_including(basic_auth: {
|
46
|
+
username: connection.key,
|
47
|
+
password: connection.token }
|
48
|
+
))
|
49
|
+
.and_return(double(response: double(code: '200'), parsed_response: response))
|
50
|
+
|
51
|
+
client.post(payload, request_id, parameters)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
40
55
|
context 'when response code is 200 (success)' do
|
41
56
|
it 'returns the parsed response' do
|
42
57
|
expect(client.post(payload, request_id, parameters))
|
@@ -44,6 +59,14 @@ module Cangaroo
|
|
44
59
|
end
|
45
60
|
end
|
46
61
|
|
62
|
+
context 'when response code is 204 (no content)' do
|
63
|
+
it 'returns an empty string' do
|
64
|
+
stub_request(:post, /^#{url}.*/).to_return(status: 204, body: '')
|
65
|
+
|
66
|
+
expect(client.post(payload, request_id, parameters)).to eq('')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
47
70
|
context 'when response code is not 200 (success)' do
|
48
71
|
let(:failure_response) do
|
49
72
|
{
|
@@ -63,6 +86,21 @@ module Cangaroo
|
|
63
86
|
.to raise_error(Cangaroo::Webhook::Error)
|
64
87
|
end
|
65
88
|
end
|
89
|
+
|
90
|
+
context 'when response code is not 200 and response is not json' do
|
91
|
+
let(:failure_response) { 'i am not json' }
|
92
|
+
|
93
|
+
before do
|
94
|
+
stub_request(:post, /^#{url}.*/).to_return(
|
95
|
+
body: failure_response,
|
96
|
+
status: 500)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'raises Cangaroo::Webhook::Error' do
|
100
|
+
expect { client.post(payload, request_id, parameters) }
|
101
|
+
.to raise_error(Cangaroo::Webhook::Error)
|
102
|
+
end
|
103
|
+
end
|
66
104
|
end
|
67
105
|
end
|
68
106
|
end
|