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.
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
@@ -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
- return self.send(:"_#{key}") || default
13
+ value = send(:"_#{key}")
14
+
15
+ return value.nil? ? default : value
12
16
  end
13
17
 
14
- self.send(:"_#{key}=", args.first)
18
+ send(:"_#{key}=", args.first)
15
19
  end
16
20
 
17
21
  define_method(key) do
18
- self.send(:"_#{key}") || default
22
+ self.class.send(key)
19
23
  end
20
24
  end
21
25
  end
22
-
23
26
  end
24
27
  end
@@ -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
@@ -1,66 +1,21 @@
1
- require 'logger'
2
-
3
1
  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
2
+ class Logger
3
+ include Singleton
19
4
 
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
5
+ attr_reader :logger
37
6
 
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
7
+ def initialize
8
+ @logger = Rails.configuration.cangaroo.logger || Rails.logger
9
+ end
59
10
 
60
- @default_tags.merge(additional_tags).map { |k,v| "#{k}=#{v}" }.join(' ')
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
@@ -0,0 +1,13 @@
1
+ require 'active_support/concern'
2
+
3
+ module Cangaroo
4
+ module LoggerHelper
5
+ extend ActiveSupport::Concern
6
+
7
+ def job_tags(tags = {})
8
+ tags.merge!(job: self.class.to_s,
9
+ job_id: job_id,
10
+ connection: self.class.connection.to_s)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module Cangaroo
2
+ module Migration
3
+ def self.[](version)
4
+ if Rails.gem_version >= Gem::Version.new('5.0')
5
+ ActiveRecord::Migration[version]
6
+ else
7
+ ActiveRecord::Migration
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Cangaroo
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.0'.freeze
3
3
  end
@@ -21,23 +21,22 @@ module Cangaroo
21
21
  }
22
22
 
23
23
  if Rails.configuration.cangaroo.basic_auth
24
- request_options.merge!(
25
- basic_auth: {
26
- username: connection.key,
27
- password: connection.token
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
- if req.response.code == '200'
35
- req.parsed_response
36
- elsif req.response.code == '204'
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
- }.merge(payload)
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 '1e4e888ac66f8dd41e00c5a7ac36a32a9950d271'
7
- token '8d49cddb4291562808bfca1bee8a9f7cf947a987'
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
@@ -18,5 +18,7 @@
18
18
  "id":"S53565543",
19
19
  "state":"waiting"
20
20
  }
21
- ]
21
+ ],
22
+ "line_items": [],
23
+ "line-items": []
22
24
  }
@@ -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).to eq('orders' => 2, 'shipments' => 2)
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({ last_poll: Time.now.to_i }) }
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.any_instance.stub(:post).and_return(successful_pull_payload)
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.any_instance.stub(:post).and_return('')
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.any_instance.stub(:post).and_return(
79
- parse_fixture('json_payload_empty.json')
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.any_instance.stub(:post).and_return(parse_fixture('json_payload_ok.json'))
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::HandleRequest).to receive(:call).and_return(double(
93
- success?: false,
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