cangaroo 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/app/controllers/cangaroo/endpoint_controller.rb +22 -28
  4. data/app/interactors/cangaroo/count_json_object.rb +2 -12
  5. data/app/interactors/cangaroo/perform_jobs.rb +2 -6
  6. data/app/interactors/cangaroo/run_polls.rb +0 -1
  7. data/app/interactors/cangaroo/validate_json_schema.rb +3 -8
  8. data/app/jobs/cangaroo/base_job.rb +35 -0
  9. data/app/jobs/cangaroo/job.rb +2 -36
  10. data/app/jobs/cangaroo/poll_job.rb +20 -44
  11. data/app/jobs/cangaroo/push_job.rb +15 -0
  12. data/app/models/cangaroo/connection.rb +3 -3
  13. data/app/models/cangaroo/poll_timestamp.rb +1 -2
  14. data/db/migrate/20151028172151_create_cangaroo_connections.rb +1 -1
  15. data/db/migrate/20151030140821_add_parameters_to_cangaroo_connection.rb +1 -1
  16. data/db/migrate/20160317020230_create_cangaroo_poll_timestamps.rb +2 -2
  17. data/lib/cangaroo.rb +9 -0
  18. data/lib/cangaroo/class_configuration.rb +7 -4
  19. data/lib/cangaroo/engine.rb +2 -0
  20. data/lib/cangaroo/logger.rb +13 -58
  21. data/lib/cangaroo/logger_helper.rb +13 -0
  22. data/lib/cangaroo/migration.rb +11 -0
  23. data/lib/cangaroo/version.rb +1 -1
  24. data/lib/cangaroo/webhook/client.rb +27 -15
  25. data/spec/factories/cangaroo_connections.rb +26 -2
  26. data/spec/features/push_flow_spec.rb +79 -0
  27. data/spec/fixtures/json_payload_ok.json +3 -1
  28. data/spec/interactors/cangaroo/count_json_object_spec.rb +4 -2
  29. data/spec/interactors/cangaroo/perform_jobs_spec.rb +2 -3
  30. data/spec/jobs/cangaroo/poll_job_spec.rb +15 -17
  31. data/spec/jobs/cangaroo/{job_spec.rb → push_job_spec.rb} +25 -17
  32. data/spec/lib/cangaroo/logger_helper_spec.rb +21 -0
  33. data/spec/lib/cangaroo/logger_spec.rb +83 -0
  34. data/spec/lib/cangaroo/webhook/client_spec.rb +39 -24
  35. data/spec/rails_helper.rb +4 -2
  36. data/spec/{controllers/cangaroo/endpoint_controller_spec.rb → requests/cangaroo/endpoint_spec.rb} +21 -30
  37. data/spec/support/jobs/confirm_order_mail_job.rb +10 -0
  38. data/spec/support/jobs/erp_job.rb +10 -0
  39. data/spec/support/jobs/fake_push_job.rb +5 -0
  40. data/spec/support/jobs/shipped_order_mail_job.rb +10 -0
  41. data/spec/support/jobs/store_job.rb +10 -0
  42. data/spec/support/jobs/warehouse_job.rb +10 -0
  43. data/spec/support/rails_app.rb +1 -0
  44. data/spec/support/spec_helpers.rb +23 -0
  45. metadata +53 -32
  46. data/app/controllers/cangaroo/application_controller.rb +0 -4
@@ -1,21 +1,15 @@
1
1
  require 'rails_helper'
2
2
 
3
3
  module Cangaroo
4
- RSpec.describe Job, type: :job do
5
- class FakeJob < Cangaroo::Job
6
- connection :store
7
- path '/webhook_path'
8
- parameters(email: 'info@nebulab.it')
9
- end
10
-
11
- let(:job_class) { FakeJob }
4
+ RSpec.describe PushJob, type: :job do
5
+ let(:job_class) { FakePushJob }
12
6
  let(:destination_connection) { create(:cangaroo_connection) }
13
7
  let(:type) { 'orders' }
14
8
  let(:payload) { { id: 'O123' } }
15
9
  let(:connection_response) { parse_fixture('json_payload_connection_response.json') }
16
10
 
17
11
  let(:options) do
18
- { connection: destination_connection,
12
+ { source_connection: destination_connection,
19
13
  type: type,
20
14
  payload: payload }
21
15
  end
@@ -24,15 +18,17 @@ module Cangaroo
24
18
  Cangaroo::Webhook::Client.new(destination_connection, '/webhook_path')
25
19
  end
26
20
 
21
+ let(:fake_command) { double('fake perform flow command', success?: true) }
22
+
23
+ let(:job) { job_class.new(options) }
24
+
27
25
  before do
28
26
  allow(client).to receive(:post).and_return(connection_response)
29
27
  allow(Cangaroo::Webhook::Client).to receive(:new).and_return(client)
30
- allow(Cangaroo::PerformFlow).to receive(:call)
28
+ allow(Cangaroo::PerformFlow).to receive(:call).and_return(fake_command)
31
29
  end
32
30
 
33
31
  describe '#perform' do
34
- let(:job) { job_class.new(options) }
35
-
36
32
  it 'instantiates a Cangaroo::Webhook::Client' do
37
33
  expect(Cangaroo::Webhook::Client).to receive(:new)
38
34
  .with(destination_connection, '/webhook_path')
@@ -41,25 +37,34 @@ module Cangaroo
41
37
  end
42
38
 
43
39
  it 'calls post on client' do
44
- job.perform
40
+ job.perform_now
45
41
  expect(client).to have_received(:post)
46
42
  .with(job.transform, job.job_id, email: 'info@nebulab.it')
47
43
  end
48
44
 
49
45
  it 'restart the flow' do
50
- job.perform
46
+ job.class.process_response(true)
47
+ job.perform_now
51
48
  expect(Cangaroo::PerformFlow).to have_received(:call)
52
49
  .once
53
50
  .with(source_connection: destination_connection,
54
- json_body: connection_response.to_json,
51
+ json_body: connection_response,
55
52
  jobs: Rails.configuration.cangaroo.jobs)
56
53
  end
57
54
 
55
+ it 'should not restart the flow when disabled' do
56
+ job.class.process_response(false)
57
+
58
+ job.perform_now
59
+
60
+ expect(Cangaroo::PerformFlow).to_not have_received(:call)
61
+ end
62
+
58
63
  context 'endpoint provides a empty response' do
59
64
  it 'should not restart the flow' do
60
65
  allow(client).to receive(:post).and_return('')
61
66
 
62
- job.perform
67
+ job.perform_now
63
68
 
64
69
  expect(Cangaroo::PerformFlow).to_not have_received(:call)
65
70
  end
@@ -71,7 +76,10 @@ module Cangaroo
71
76
  end
72
77
 
73
78
  describe '#transform' do
74
- it { expect(job_class.new(options).transform).to eq('order' => payload) }
79
+ it 'return a single element hash with singularized type as key and payload as value' do
80
+ job.perform_now
81
+ expect(job.transform).to eq('order' => payload)
82
+ end
75
83
  end
76
84
  end
77
85
  end
@@ -0,0 +1,21 @@
1
+ require 'rails_helper'
2
+
3
+ describe Cangaroo::LoggerHelper do
4
+ describe '#job_tags' do
5
+ let(:job_class) { FakePushJob }
6
+ let(:type) { 'orders' }
7
+ let(:payload) { { id: 'O123' } }
8
+ let(:options) do
9
+ { connection: destination_connection,
10
+ type: type,
11
+ payload: payload }
12
+ end
13
+ let(:job) { job_class.new(options) }
14
+ let!(:destination_connection) { create(:cangaroo_connection) }
15
+ let(:extra_tags) { { tag1: 1, tag2: 2 } }
16
+
17
+ it 'merges given tags with job class name, job_id and connection name' do
18
+ expect(job.job_tags(extra_tags)).to eq(extra_tags.merge(job: 'FakePushJob', job_id: job.job_id, connection: destination_connection.name))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,83 @@
1
+ require 'rails_helper'
2
+
3
+ class CustomLogger
4
+ def log(_message); end
5
+
6
+ def unknown(_message, _opts = {}); end
7
+
8
+ def debug(_message, _opts = {}); end
9
+
10
+ def info(_message, _opts = {}); end
11
+
12
+ def warn(_message, _opts = {}); end
13
+
14
+ def error(_message, _opts = {}); end
15
+ end
16
+
17
+ describe Cangaroo::Logger do
18
+ let(:cangaroo_logger) { Cangaroo::Logger.send(:new) }
19
+ before { Rails.configuration.cangaroo.logger = logger }
20
+
21
+ describe '#logger' do
22
+ subject { cangaroo_logger.logger }
23
+
24
+ context 'when Rails.configuration.cangaroo.logger is set' do
25
+ let(:logger) { CustomLogger.new }
26
+
27
+ it { is_expected.to eq(logger) }
28
+ end
29
+
30
+ context 'when Rails.configuration.cangaroo.logger is not set' do
31
+ let(:logger) { nil }
32
+
33
+ it { is_expected.to eq(Rails.logger) }
34
+ end
35
+ end
36
+
37
+ context 'log methods' do
38
+ let(:message) { 'message' }
39
+ let(:opts) { { opt1: 1, opt2: 2 } }
40
+
41
+ describe '#log' do
42
+ after { cangaroo_logger.log(message, opts) }
43
+
44
+ context 'when logger can receive given parameters' do
45
+ let(:logger) { nil }
46
+
47
+ it 'calls the logger log method with the given params' do
48
+ expect(Rails.logger).to receive(:log).with(message, opts)
49
+ end
50
+ end
51
+
52
+ context 'when logger can not receive given parameters' do
53
+ let(:logger) { CustomLogger.new }
54
+
55
+ it 'calls the logger log method with an array of message and params' do
56
+ expect(logger).to receive(:log).with([message, opts])
57
+ end
58
+ end
59
+ end
60
+
61
+ %i(unknown debug info warn error).each do |log_method|
62
+ describe "##{log_method}" do
63
+ after { cangaroo_logger.send(log_method, message, opts) }
64
+
65
+ context 'when logger can receive given parameters' do
66
+ let(:logger) { CustomLogger.new }
67
+
68
+ it "calls the logger #{log_method} method with the given params" do
69
+ expect(logger).to receive(log_method).with(message, opts)
70
+ end
71
+ end
72
+
73
+ context 'when logger can not receive given parameters' do
74
+ let(:logger) { nil }
75
+
76
+ it "calls the logger #{log_method} method with an array of message and params" do
77
+ expect(Rails.logger).to receive(log_method).with([message, opts])
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -11,11 +11,12 @@ module Cangaroo
11
11
 
12
12
  let(:request_id) { '123456' }
13
13
  let(:parameters) { { email: 'info@nebulab.it' } }
14
- let(:payload) { { order: { id: 'R12345', state: 'completed' } } }
14
+ let(:payload) do
15
+ { order: { id: 'R12345', state: 'completed' } }
16
+ end
15
17
 
16
18
  let(:response) do
17
- { 'request_id': '52f367367575e449c3000001',
18
- 'summary': 'Successfully updated order for R12345' }
19
+ { 'request_id' => '52f367367575e449c3000001', 'summary' => 'Successfully updated order for R12345' }
19
20
  end
20
21
 
21
22
  before do
@@ -28,13 +29,12 @@ module Cangaroo
28
29
  client.post(payload, request_id, parameters)
29
30
  expect(WebMock).to have_requested(:post,
30
31
  'http://www.store.com/api_path')
31
- .with(
32
- headers: { 'X_HUB_TOKEN': connection.token },
33
- body: {
34
- request_id: request_id,
35
- parameters: connection.parameters.deep_merge(parameters),
36
- order: { id: 'R12345', state: 'completed' }
37
- }.to_json)
32
+ .with(headers: { 'X_HUB_TOKEN' => connection.token },
33
+ body: {
34
+ request_id: request_id,
35
+ parameters: connection.parameters.deep_merge(parameters),
36
+ order: { id: 'R12345', state: 'completed' }
37
+ }.to_json)
38
38
  end
39
39
 
40
40
  context 'when basic auth is enabled' do
@@ -42,16 +42,38 @@ module Cangaroo
42
42
 
43
43
  it 'sends key and token as basic auth username and password' do
44
44
  expect(client.class).to receive(:post)
45
- .with(anything, hash_including(basic_auth: {
46
- username: connection.key,
47
- password: connection.token }
48
- ))
45
+ .with(anything, hash_including(basic_auth: { username: connection.key, password: connection.token } ))
49
46
  .and_return(double(response: double(code: '200'), parsed_response: response))
50
47
 
51
48
  client.post(payload, request_id, parameters)
52
49
  end
53
50
  end
54
51
 
52
+ describe 'Rails.configuration.cangaroo.client_timeout' do
53
+
54
+ context 'when set' do
55
+ before { Rails.configuration.cangaroo.client_timeout = 120 }
56
+ it 'sets the timeout config on the client' do
57
+ expect(client.class).to receive(:post)
58
+ .with(anything, hash_including(timeout: 120 ))
59
+ .and_return(double(response: double(code: '200'), parsed_response: response))
60
+
61
+ client.post(payload, request_id, parameters)
62
+ end
63
+ end
64
+
65
+ context 'when not set' do
66
+ before { Rails.configuration.cangaroo.client_timeout = nil }
67
+ it 'does not set the timeout config on the client' do
68
+ allow(client.class).to receive(:post)
69
+ .with(anything, hash_excluding(:timeout))
70
+ .and_return(double(response: double(code: '200'), parsed_response: response))
71
+
72
+ client.post(payload, request_id, parameters)
73
+ end
74
+ end
75
+ end
76
+
55
77
  context 'when response code is 200 (success)' do
56
78
  it 'returns the parsed response' do
57
79
  expect(client.post(payload, request_id, parameters))
@@ -69,16 +91,11 @@ module Cangaroo
69
91
 
70
92
  context 'when response code is not 200 (success)' do
71
93
  let(:failure_response) do
72
- {
73
- 'request_id': '52f367367575e449c3000001',
74
- 'summary': 'Cannot update order. Order R12345 not found in storefront.'
75
- }
94
+ { 'request_id' => '52f367367575e449c3000001', 'summary' => 'Cannot update order. Order R12345 not found in storefront.' }
76
95
  end
77
96
 
78
97
  before do
79
- stub_request(:post, /^#{url}.*/).to_return(
80
- body: failure_response.to_json,
81
- status: 500)
98
+ stub_request(:post, /^#{url}.*/).to_return(body: failure_response.to_json, status: 500)
82
99
  end
83
100
 
84
101
  it 'raises Cangaroo::Webhook::Error' do
@@ -91,9 +108,7 @@ module Cangaroo
91
108
  let(:failure_response) { 'i am not json' }
92
109
 
93
110
  before do
94
- stub_request(:post, /^#{url}.*/).to_return(
95
- body: failure_response,
96
- status: 500)
111
+ stub_request(:post, /^#{url}.*/).to_return(body: failure_response, status: 500)
97
112
  end
98
113
 
99
114
  it 'raises Cangaroo::Webhook::Error' do
@@ -1,11 +1,10 @@
1
1
  require 'simplecov'
2
- require 'codeclimate-test-reporter'
3
2
  require 'pry-byebug'
4
3
 
5
4
  SimpleCov.start 'rails' do
6
5
  add_group 'Commands', 'app/commands'
6
+ add_filter 'lib/cangaroo/version'
7
7
  end
8
- CodeClimate::TestReporter.start
9
8
 
10
9
  # This file is copied to spec/ when you run 'rails generate rspec:install'
11
10
  ENV['RAILS_ENV'] ||= 'test'
@@ -31,6 +30,8 @@ require 'rspec/rails'
31
30
  spec_helpers
32
31
  ).each { |path| require File.expand_path("../support/#{path}.rb", __FILE__) }
33
32
 
33
+ Dir[File.dirname(__FILE__) + '/support/jobs/*.rb'].each { |file| require file }
34
+
34
35
  # Checks for pending migrations before tests are run.
35
36
  # If you are not using ActiveRecord, you can remove this line.
36
37
  ActiveRecord::Migration.maintain_test_schema!
@@ -41,6 +42,7 @@ RSpec.configure do |config|
41
42
  Rails.configuration.cangaroo.basic_auth = false
42
43
  Rails.configuration.cangaroo.jobs = []
43
44
  Rails.configuration.cangaroo.poll_job = []
45
+ Rails.configuration.cangaroo.logger = nil
44
46
  end
45
47
 
46
48
  # The different available types are documented in the features, such as in
@@ -1,51 +1,45 @@
1
1
  require 'rails_helper'
2
2
 
3
3
  module Cangaroo
4
- RSpec.describe EndpointController, type: :controller do
5
- routes { Cangaroo::Engine.routes }
4
+ RSpec.describe :Endpoint, type: :request do
6
5
  let(:connection) { create :cangaroo_connection }
7
- let(:request_payload) { JSON.parse(load_fixture('json_payload_ok.json')) }
8
-
9
- before do
10
- request.headers['Content-Type'] = 'application/json'
11
- end
6
+ let(:request_payload) { load_fixture('json_payload_ok.json') }
7
+ let(:headers) {
8
+ { 'Content-Type' => 'application/json',
9
+ 'X-Hub-Store' => connection.key,
10
+ 'X-Hub-Access-Token' => connection.token }
11
+ }
12
12
 
13
13
  context 'when wombat authentication is enabled' do
14
- before do
15
- request.headers['X-Hub-Store'] = connection.key
16
- request.headers['X-Hub-Access-Token'] = connection.token
17
- end
18
-
19
14
  describe '#create' do
20
15
  before do
21
- post :create, request_payload
16
+ post endpoint_index_path, params: request_payload, headers: headers
22
17
  end
23
18
 
24
19
  it 'accepts only application/json requests' do
25
20
  expect(response.status).to eq(202)
26
21
 
27
- request.headers['Content-Type'] = 'text/html'
28
- post :create
22
+ headers['Content-Type'] = 'text/html'
23
+ post endpoint_index_path, params: {}, headers: headers
29
24
  expect(response.status).to eq(406)
30
25
  end
31
26
 
32
27
  context 'when success' do
33
- let(:auth_headers) {}
34
-
35
28
  it 'responds with 200' do
36
29
  expect(response.status).to eq(202)
37
30
  end
38
31
 
39
32
  it 'responds with the number of objects received in payload' do
40
33
  res = JSON.parse(response.body)
41
- expect(res).to eq('orders' => 2, 'shipments' => 2)
34
+ expect(res).to eq('orders' => 2, 'shipments' => 2,
35
+ 'line_items' => 0, 'line-items' => 0)
42
36
  end
43
37
  end
44
38
 
45
39
  context 'when error' do
46
40
  before do
47
- request.headers['X-Hub-Access-Token'] = 'wrongtoken'
48
- post :create, request_payload
41
+ headers['X-Hub-Access-Token'] = 'wrongtoken'
42
+ post endpoint_index_path, params: request_payload, headers: headers
49
43
  end
50
44
 
51
45
  it 'responds with the command error code' do
@@ -59,8 +53,8 @@ module Cangaroo
59
53
 
60
54
  context 'when an exception was raised' do
61
55
  before do
62
- HandleRequest.stub(:call).and_raise('An error')
63
- post :create, request_payload
56
+ allow(HandleRequest).to receive(:call).and_raise('An error')
57
+ post endpoint_index_path, params: request_payload, headers: headers
64
58
  end
65
59
 
66
60
  it 'responds with 500' do
@@ -81,10 +75,9 @@ module Cangaroo
81
75
 
82
76
  describe '#create' do
83
77
  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)
78
+ headers['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(connection.key, connection.token)
86
79
 
87
- post :create, request_payload
80
+ post endpoint_index_path, params: request_payload, headers: headers
88
81
 
89
82
  expect(response.status).to eq(202)
90
83
  end
@@ -92,21 +85,19 @@ module Cangaroo
92
85
  it 'successfully authenticates against a connection token' do
93
86
  connection.update(key: '')
94
87
 
95
- request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic
96
- .encode_credentials('', connection.token)
88
+ headers['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('', connection.token)
97
89
 
98
- post :create, request_payload
90
+ post endpoint_index_path, params: request_payload, headers: headers
99
91
 
100
92
  expect(response.status).to eq(202)
101
93
  end
102
94
 
103
95
  it 'fails to authenticate when basic auth is not provided' do
104
- post :create, request_payload
96
+ post endpoint_index_path, params: request_payload, headers: headers
105
97
 
106
98
  expect(response.status).to eq(401)
107
99
  end
108
100
  end
109
101
  end
110
-
111
102
  end
112
103
  end