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
@@ -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