cangaroo 1.2.0 → 1.3.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/Rakefile +1 -1
- data/app/controllers/cangaroo/endpoint_controller.rb +22 -28
- data/app/interactors/cangaroo/count_json_object.rb +2 -12
- data/app/interactors/cangaroo/perform_jobs.rb +2 -6
- data/app/interactors/cangaroo/run_polls.rb +0 -1
- data/app/interactors/cangaroo/validate_json_schema.rb +3 -8
- data/app/jobs/cangaroo/base_job.rb +35 -0
- data/app/jobs/cangaroo/job.rb +2 -36
- data/app/jobs/cangaroo/poll_job.rb +20 -44
- data/app/jobs/cangaroo/push_job.rb +15 -0
- data/app/models/cangaroo/connection.rb +3 -3
- data/app/models/cangaroo/poll_timestamp.rb +1 -2
- data/db/migrate/20151028172151_create_cangaroo_connections.rb +1 -1
- data/db/migrate/20151030140821_add_parameters_to_cangaroo_connection.rb +1 -1
- data/db/migrate/20160317020230_create_cangaroo_poll_timestamps.rb +2 -2
- data/lib/cangaroo.rb +9 -0
- data/lib/cangaroo/class_configuration.rb +7 -4
- data/lib/cangaroo/engine.rb +2 -0
- data/lib/cangaroo/logger.rb +13 -58
- data/lib/cangaroo/logger_helper.rb +13 -0
- data/lib/cangaroo/migration.rb +11 -0
- data/lib/cangaroo/version.rb +1 -1
- data/lib/cangaroo/webhook/client.rb +27 -15
- data/spec/factories/cangaroo_connections.rb +26 -2
- data/spec/features/push_flow_spec.rb +79 -0
- data/spec/fixtures/json_payload_ok.json +3 -1
- data/spec/interactors/cangaroo/count_json_object_spec.rb +4 -2
- data/spec/interactors/cangaroo/perform_jobs_spec.rb +2 -3
- data/spec/jobs/cangaroo/poll_job_spec.rb +15 -17
- data/spec/jobs/cangaroo/{job_spec.rb → push_job_spec.rb} +25 -17
- data/spec/lib/cangaroo/logger_helper_spec.rb +21 -0
- data/spec/lib/cangaroo/logger_spec.rb +83 -0
- data/spec/lib/cangaroo/webhook/client_spec.rb +39 -24
- data/spec/rails_helper.rb +4 -2
- data/spec/{controllers/cangaroo/endpoint_controller_spec.rb → requests/cangaroo/endpoint_spec.rb} +21 -30
- data/spec/support/jobs/confirm_order_mail_job.rb +10 -0
- data/spec/support/jobs/erp_job.rb +10 -0
- data/spec/support/jobs/fake_push_job.rb +5 -0
- data/spec/support/jobs/shipped_order_mail_job.rb +10 -0
- data/spec/support/jobs/store_job.rb +10 -0
- data/spec/support/jobs/warehouse_job.rb +10 -0
- data/spec/support/rails_app.rb +1 -0
- data/spec/support/spec_helpers.rb +23 -0
- metadata +53 -32
- data/app/controllers/cangaroo/application_controller.rb +0 -4
data/lib/cangaroo.rb
CHANGED
@@ -4,9 +4,18 @@ require 'json-schema'
|
|
4
4
|
require 'httparty'
|
5
5
|
|
6
6
|
require 'cangaroo/logger'
|
7
|
+
require 'cangaroo/logger_helper'
|
7
8
|
require 'cangaroo/webhook/error'
|
8
9
|
require 'cangaroo/webhook/client'
|
9
10
|
require 'cangaroo/class_configuration'
|
11
|
+
require 'cangaroo/migration'
|
10
12
|
|
11
13
|
module Cangaroo
|
14
|
+
class << self
|
15
|
+
attr_writer :logger
|
16
|
+
|
17
|
+
def logger
|
18
|
+
@logger ||= Cangaroo::Logger.instance
|
19
|
+
end
|
20
|
+
end
|
12
21
|
end
|
@@ -2,23 +2,26 @@ module Cangaroo
|
|
2
2
|
module ClassConfiguration
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
# NOTE you must use the `_key` class attribute to nil out a class_cofiguration
|
6
|
+
|
5
7
|
module ClassMethods
|
6
8
|
def class_configuration(key, default = nil)
|
7
9
|
class_attribute :"_#{key}"
|
8
10
|
|
9
11
|
define_singleton_method(key) do |*args|
|
10
12
|
if args.empty?
|
11
|
-
|
13
|
+
value = send(:"_#{key}")
|
14
|
+
|
15
|
+
return value.nil? ? default : value
|
12
16
|
end
|
13
17
|
|
14
|
-
|
18
|
+
send(:"_#{key}=", args.first)
|
15
19
|
end
|
16
20
|
|
17
21
|
define_method(key) do
|
18
|
-
self.send(
|
22
|
+
self.class.send(key)
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
22
|
-
|
23
26
|
end
|
24
27
|
end
|
data/lib/cangaroo/engine.rb
CHANGED
@@ -14,6 +14,8 @@ module Cangaroo
|
|
14
14
|
Rails.configuration.cangaroo.jobs = []
|
15
15
|
Rails.configuration.cangaroo.poll_jobs = []
|
16
16
|
Rails.configuration.cangaroo.basic_auth = false
|
17
|
+
Rails.configuration.cangaroo.logger = nil
|
18
|
+
Rails.configuration.cangaroo.client_timeout = nil
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
data/lib/cangaroo/logger.rb
CHANGED
@@ -1,66 +1,21 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
1
|
module Cangaroo
|
4
|
-
|
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
|
2
|
+
class Logger
|
3
|
+
include Singleton
|
19
4
|
|
20
|
-
|
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
|
5
|
+
attr_reader :logger
|
37
6
|
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
7
|
+
def initialize
|
8
|
+
@logger = Rails.configuration.cangaroo.logger || Rails.logger
|
9
|
+
end
|
59
10
|
|
60
|
-
|
11
|
+
%i(log unknown debug info warn error).each do |log_method|
|
12
|
+
define_method log_method do |*params|
|
13
|
+
begin
|
14
|
+
@logger.send(log_method, *params)
|
15
|
+
rescue ArgumentError
|
16
|
+
@logger.send(log_method, params)
|
61
17
|
end
|
62
|
-
|
18
|
+
end
|
63
19
|
end
|
64
|
-
|
65
20
|
end
|
66
21
|
end
|
data/lib/cangaroo/version.rb
CHANGED
@@ -21,23 +21,22 @@ module Cangaroo
|
|
21
21
|
}
|
22
22
|
|
23
23
|
if Rails.configuration.cangaroo.basic_auth
|
24
|
-
request_options
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
request_options[:basic_auth] = {
|
25
|
+
username: connection.key,
|
26
|
+
password: connection.token
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
if timeout = Rails.configuration.cangaroo.client_timeout
|
31
|
+
request_options[:timeout] = timeout
|
30
32
|
end
|
31
33
|
|
32
34
|
req = self.class.post(url, request_options)
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
else
|
39
|
-
fail Cangaroo::Webhook::Error, (req.parsed_response['summary'] rescue req.response)
|
40
|
-
end
|
36
|
+
sanitized_response = sanitize_response(req)
|
37
|
+
|
38
|
+
fail Cangaroo::Webhook::Error, sanitized_response unless %w(200 201 202 204).include?(req.response.code)
|
39
|
+
sanitized_response
|
41
40
|
end
|
42
41
|
|
43
42
|
private
|
@@ -58,8 +57,21 @@ module Cangaroo
|
|
58
57
|
|
59
58
|
def body(payload, request_id, parameters)
|
60
59
|
{ request_id: request_id,
|
61
|
-
parameters: connection.parameters.deep_merge(parameters)
|
62
|
-
|
60
|
+
parameters: connection.parameters.deep_merge(parameters) }.merge(payload)
|
61
|
+
end
|
62
|
+
|
63
|
+
def sanitize_response(request)
|
64
|
+
if %w(200 201 202).include?(request.response.code)
|
65
|
+
request.parsed_response
|
66
|
+
elsif request.response.code == '204'
|
67
|
+
''
|
68
|
+
else
|
69
|
+
begin
|
70
|
+
(request.parsed_response['summary'] || request.response.body)
|
71
|
+
rescue
|
72
|
+
request.response.body
|
73
|
+
end
|
74
|
+
end
|
63
75
|
end
|
64
76
|
end
|
65
77
|
end
|
@@ -1,9 +1,33 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
1
3
|
FactoryGirl.define do
|
2
4
|
factory :cangaroo_connection, class: 'Cangaroo::Connection' do
|
3
5
|
name :store
|
4
6
|
url 'www.store.com'
|
5
7
|
parameters { { first: 'first', second: 'second' } }
|
6
|
-
key
|
7
|
-
token
|
8
|
+
key { SecureRandom.hex }
|
9
|
+
token { SecureRandom.hex }
|
10
|
+
|
11
|
+
factory :store do
|
12
|
+
parameters nil
|
13
|
+
end
|
14
|
+
|
15
|
+
factory :erp do
|
16
|
+
parameters nil
|
17
|
+
name :erp
|
18
|
+
url 'www.erp.com'
|
19
|
+
end
|
20
|
+
|
21
|
+
factory :mail do
|
22
|
+
parameters nil
|
23
|
+
name :mail
|
24
|
+
url 'www.mail.com'
|
25
|
+
end
|
26
|
+
|
27
|
+
factory :warehouse do
|
28
|
+
parameters nil
|
29
|
+
name :warehouse
|
30
|
+
url 'www.warehouse.com'
|
31
|
+
end
|
8
32
|
end
|
9
33
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
RSpec.describe 'Push flow' do
|
5
|
+
let(:store_payload) { load_fixture('json_payload_ok.json') }
|
6
|
+
let(:payed_order) { JSON.parse(store_payload)['orders'].last }
|
7
|
+
let(:store) { Cangaroo::Connection.find_by(name: 'store') }
|
8
|
+
let(:headers) {
|
9
|
+
{ 'Content-Type' => 'application/json',
|
10
|
+
'X-Hub-Store' => store.key,
|
11
|
+
'X-Hub-Access-Token' => store.token }
|
12
|
+
}
|
13
|
+
|
14
|
+
let!(:erp_api) {
|
15
|
+
stub_api(job: Cangaroo::ErpJob,
|
16
|
+
request_body: { order: { id: payed_order['id'], state: payed_order['state'] } },
|
17
|
+
response_body: {
|
18
|
+
'summary' => "Successfully updated order for #{payed_order['id']}",
|
19
|
+
'orders' => [{ id: payed_order['id'], state: 'confirmed' }]
|
20
|
+
})
|
21
|
+
}
|
22
|
+
|
23
|
+
let!(:update_order_store_api) {
|
24
|
+
stub_api(job: Cangaroo::StoreJob, request_body: { order: { id: payed_order['id'], state: anything } })
|
25
|
+
}
|
26
|
+
|
27
|
+
let!(:confirm_mail_api) {
|
28
|
+
stub_api(job: Cangaroo::ConfirmOrderMailJob, request_body: { order: { id: payed_order['id'], state: 'confirmed' } })
|
29
|
+
}
|
30
|
+
|
31
|
+
let!(:warehouse_api) {
|
32
|
+
stub_api(job: Cangaroo::WarehouseJob,
|
33
|
+
request_body: { order: { id: payed_order['id'], state: 'confirmed' } },
|
34
|
+
response_body: {
|
35
|
+
'summary' => "Successfully shipped order #{payed_order['id']}",
|
36
|
+
'orders' => [{ id: payed_order['id'], state: 'shipped' }]
|
37
|
+
})
|
38
|
+
}
|
39
|
+
|
40
|
+
let!(:shipped_mail_api) {
|
41
|
+
stub_api(job: Cangaroo::ShippedOrderMailJob, request_body: { order: { id: payed_order['id'], state: 'shipped' } })
|
42
|
+
}
|
43
|
+
|
44
|
+
before do
|
45
|
+
Rails.configuration.cangaroo.jobs = [Cangaroo::ErpJob,
|
46
|
+
Cangaroo::StoreJob,
|
47
|
+
Cangaroo::ConfirmOrderMailJob,
|
48
|
+
Cangaroo::WarehouseJob,
|
49
|
+
Cangaroo::ShippedOrderMailJob]
|
50
|
+
|
51
|
+
post endpoint_index_path, params: store_payload, headers: headers
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'when new order in state cart coming from store' do
|
55
|
+
it 'calls the erp api that responds with order in confirmed state' do
|
56
|
+
expect(erp_api).to have_been_requested
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'then' do
|
60
|
+
it 'calls mail api to send confirmation email to user' do
|
61
|
+
expect(confirm_mail_api).to have_been_requested
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'calls warehouse to send the package and responds with order in shipped state' do
|
65
|
+
expect(warehouse_api).to have_been_requested
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'then' do
|
69
|
+
it 'calls mail api to send shipped email to user' do
|
70
|
+
expect(shipped_mail_api).to have_been_requested
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'calls the store api to update the order state twice' do
|
76
|
+
expect(update_order_store_api).to have_been_requested.twice
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -3,11 +3,13 @@ require 'rails_helper'
|
|
3
3
|
describe Cangaroo::CountJsonObject do
|
4
4
|
subject(:context) { Cangaroo::CountJsonObject.call(json_body: json_body) }
|
5
5
|
|
6
|
-
let(:json_body) { load_fixture('json_payload_ok.json') }
|
6
|
+
let(:json_body) { JSON.parse(load_fixture('json_payload_ok.json')) }
|
7
7
|
|
8
8
|
describe '.call' do
|
9
9
|
it 'provides the object_count' do
|
10
|
-
expect(context.object_count)
|
10
|
+
expect(context.object_count)
|
11
|
+
.to eq('orders' => 2, 'shipments' => 2,
|
12
|
+
'line_items' => 0, 'line-items' => 0)
|
11
13
|
end
|
12
14
|
|
13
15
|
it 'succeeds' do
|
@@ -22,7 +22,7 @@ describe Cangaroo::PerformJobs do
|
|
22
22
|
let(:job_b) { double('job_b', perform?: false, enqueue: nil) }
|
23
23
|
|
24
24
|
context 'payload with objects' do
|
25
|
-
let(:json_body) { load_fixture('json_payload_ok.json') }
|
25
|
+
let(:json_body) { JSON.parse(load_fixture('json_payload_ok.json')) }
|
26
26
|
|
27
27
|
it 'instantiates jobs' do
|
28
28
|
context
|
@@ -42,7 +42,7 @@ describe Cangaroo::PerformJobs do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
context 'payload with no objects' do
|
45
|
-
let(:json_body) { load_fixture('json_payload_empty.json') }
|
45
|
+
let(:json_body) { JSON.parse(load_fixture('json_payload_empty.json')) }
|
46
46
|
|
47
47
|
it 'succeeds' do
|
48
48
|
context
|
@@ -52,6 +52,5 @@ describe Cangaroo::PerformJobs do
|
|
52
52
|
expect(job_b).to_not have_received(:enqueue)
|
53
53
|
end
|
54
54
|
end
|
55
|
-
|
56
55
|
end
|
57
56
|
end
|
@@ -8,7 +8,7 @@ RSpec.describe Cangaroo::PollJob, type: :job do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
let(:job_class) { FakePollJob }
|
11
|
-
let(:job) { job_class.new(
|
11
|
+
let(:job) { job_class.new(last_poll: Time.now.to_i) }
|
12
12
|
let(:successful_pull_payload) { parse_fixture('json_payload_ok.json') }
|
13
13
|
|
14
14
|
before do
|
@@ -46,9 +46,7 @@ RSpec.describe Cangaroo::PollJob, type: :job do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
describe '#perform' do
|
49
|
-
|
50
49
|
context 'pull is successful' do
|
51
|
-
|
52
50
|
before do
|
53
51
|
allow(Cangaroo::HandleRequest).to receive(:call).
|
54
52
|
and_return(double(success?: true))
|
@@ -58,7 +56,9 @@ RSpec.describe Cangaroo::PollJob, type: :job do
|
|
58
56
|
end
|
59
57
|
|
60
58
|
it 'updates the poll timestamp' do
|
61
|
-
Cangaroo::Webhook::Client
|
59
|
+
allow_any_instance_of(Cangaroo::Webhook::Client)
|
60
|
+
.to receive(:post)
|
61
|
+
.and_return(successful_pull_payload)
|
62
62
|
|
63
63
|
job.perform
|
64
64
|
|
@@ -69,30 +69,30 @@ RSpec.describe Cangaroo::PollJob, type: :job do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'handles a empty response' do
|
72
|
-
Cangaroo::Webhook::Client
|
72
|
+
allow_any_instance_of(Cangaroo::Webhook::Client)
|
73
|
+
.to receive(:post)
|
74
|
+
.and_return('')
|
73
75
|
|
74
76
|
job.perform
|
75
77
|
end
|
76
78
|
|
77
79
|
it 'handles a response with a empty array' do
|
78
|
-
Cangaroo::Webhook::Client
|
79
|
-
|
80
|
-
|
80
|
+
allow_any_instance_of(Cangaroo::Webhook::Client)
|
81
|
+
.to receive(:post)
|
82
|
+
.and_return(parse_fixture('json_payload_empty.json'))
|
81
83
|
|
82
84
|
job.perform
|
83
85
|
end
|
84
|
-
|
85
86
|
end
|
86
87
|
|
87
88
|
context 'pull fails' do
|
88
|
-
|
89
89
|
before do
|
90
|
-
Cangaroo::Webhook::Client
|
90
|
+
allow_any_instance_of(Cangaroo::Webhook::Client)
|
91
|
+
.to receive(:post)
|
92
|
+
.and_return(parse_fixture('json_payload_ok.json'))
|
91
93
|
|
92
|
-
allow(Cangaroo::
|
93
|
-
|
94
|
-
message: 'bad failure'
|
95
|
-
))
|
94
|
+
allow(Cangaroo::PerformFlow).to receive(:call).and_return(double(success?: false,
|
95
|
+
message: 'bad failure'))
|
96
96
|
end
|
97
97
|
|
98
98
|
it 'does not update timestamp if pull fails' do
|
@@ -100,8 +100,6 @@ RSpec.describe Cangaroo::PollJob, type: :job do
|
|
100
100
|
|
101
101
|
expect(Cangaroo::PollTimestamp.for_class(FakePollJob).id).to be_nil
|
102
102
|
end
|
103
|
-
|
104
103
|
end
|
105
104
|
end
|
106
|
-
|
107
105
|
end
|