api_hammer 0.7.1 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2efd3cea2d6c262bff720e983354d944f0de16da
4
- data.tar.gz: 9611db21a814571891d2d17ed017c82d2048b52e
3
+ metadata.gz: ac61ce80519fdf3583b64584b95987a4c19c1f70
4
+ data.tar.gz: 3b9b20ab3729e8c9a473747f54652da7cc947284
5
5
  SHA512:
6
- metadata.gz: 8b0dbb63040d700c2c137e24c4d84eb4804d2a234a68f3b02fdc001a78ff9364cb04975466e571bac801436be222174cd7fb7def879766c1af15afadddc1a093
7
- data.tar.gz: 01d61498c3db5d7ef844b6c4c8bd4524a7f98c5d2b8bd738b3a8d5ac42e952c11eab9e85c48d794e9e9d1c8fdbdbff5f2317b78d95b8f4e3af954b35729228e9
6
+ metadata.gz: e3cac57cc7ac0e03057b8b5bb5b2c10cc96af35e1df16ff635476ce332bd3855f5a7c13aab42051e29543b7f734615984f99bbbe0d7327bed55eeee7aefb56e3
7
+ data.tar.gz: ed7d082a200f2b6c4cab977afb1c32cb02dc997b65c0f5369197cac176f53dd1f13c6adc3ab408b94fd47f5948a8dc18eae1e88eb21f1854d67c7b6a3088d624
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # v0.8.0
2
+ - log request and response bodies - not just IDs from them - if they aren't too big
3
+
1
4
  # v0.7.1
2
5
  - logstash filters for sidekiq, activesupport tags, and of course ApiHammer's request logging
3
6
  - use i18n for errors and add error_message to response
@@ -15,6 +15,8 @@ module ApiHammer
15
15
  class RequestLogger < Rack::CommonLogger
16
16
  include Term::ANSIColor
17
17
 
18
+ LARGE_BODY_SIZE = 4096
19
+
18
20
  def call(env)
19
21
  began_at = Time.now
20
22
 
@@ -97,31 +99,29 @@ module ApiHammer
97
99
  'activesupport_tagged_logging_tags' => log_tags,
98
100
  }.merge(env['request_logger.info'] || {}).merge(Thread.current['request_logger.info'] || {}).reject { |k,v| v.nil? },
99
101
  }
100
- ids_from_body = proc do |body_string, content_type|
101
- media_type = ::Rack::Request.new({'CONTENT_TYPE' => content_type}).media_type
102
- body_object = begin
103
- if media_type == 'application/json'
104
- JSON.parse(body_string) rescue nil
105
- elsif media_type == 'application/x-www-form-urlencoded'
106
- CGI.parse(body_string).map { |k, vs| {k => vs.last} }.inject({}, &:update)
102
+ response_body_string = response_body.to_enum.to_a.join('')
103
+ body_info = [['request', request_body, request.content_type], ['response', response_body_string, response.content_type]]
104
+ body_info.map do |(role, body, content_type)|
105
+ if (400..599).include?(status.to_i) || body.size < LARGE_BODY_SIZE
106
+ # log bodies if they are not large, or if there was an error (either client or server)
107
+ data[role]['body'] = body
108
+ else
109
+ # otherwise, log id and uuid fields
110
+ media_type = ::Rack::Request.new({'CONTENT_TYPE' => content_type}).media_type
111
+ body_object = begin
112
+ if media_type == 'application/json'
113
+ JSON.parse(body) rescue nil
114
+ elsif media_type == 'application/x-www-form-urlencoded'
115
+ CGI.parse(body).map { |k, vs| {k => vs.last} }.inject({}, &:update)
116
+ end
107
117
  end
118
+ if body_object.is_a?(Hash)
119
+ sep = /(?:\b|\W|_)/
120
+ body_ids = body_object.reject { |key, value| !(key =~ /#{sep}([ug]u)?id#{sep}/ && value.is_a?(String)) }
121
+ end
122
+
123
+ data[role]['body_ids'] = body_ids if body_ids && body_ids.any?
108
124
  end
109
- if body_object.is_a?(Hash)
110
- sep = /(?:\b|\W|_)/
111
- body_object.reject { |key, value| !(key =~ /#{sep}([ug]u)?id#{sep}/ && value.is_a?(String)) }
112
- end
113
- end
114
- response_body_string = response_body.to_enum.to_a.join('')
115
- if (400..599).include?(status.to_i)
116
- # only log bodies if there was an error (either client or server)
117
- data['request']['body'] = request_body
118
- data['response']['body'] = response_body_string
119
- else
120
- # otherwise, log id and uuid fields
121
- request_body_ids = ids_from_body.call(request_body, request.content_type)
122
- data['request']['body_ids'] = request_body_ids if request_body_ids && request_body_ids.any?
123
- response_body_ids = ids_from_body.call(response_body_string, response.content_type)
124
- data['response']['body_ids'] = response_body_ids if response_body_ids && response_body_ids.any?
125
125
  end
126
126
  Thread.current['request_logger.info'] = nil
127
127
  json_data = JSON.dump(data)
@@ -1,3 +1,3 @@
1
1
  module ApiHammer
2
- VERSION = "0.7.1"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -25,43 +25,43 @@ describe 'ApiHammer::Rails#check_required_params' do
25
25
  it 'is missing id' do
26
26
  c = controller_with_params(:person => {:name => 'hammer', :height => '3'}, :lucky_numbers => ['2'])
27
27
  err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
28
- assert_equal({'errors' => {'id' => ['is required but was not provided']}}, err.body)
28
+ assert_equal({'error_message' => 'id is required but was not provided', 'errors' => {'id' => ['id is required but was not provided']}}, err.body)
29
29
  end
30
30
 
31
31
  it 'is missing person' do
32
32
  c = controller_with_params(:id => '99', :lucky_numbers => ['2'])
33
33
  err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
34
- assert_equal({'errors' => {'person' => ['is required but was not provided']}}, err.body)
34
+ assert_equal({'error_message' => 'person is required but was not provided', 'errors' => {'person' => ['person is required but was not provided']}}, err.body)
35
35
  end
36
36
 
37
37
  it 'is has the wrong type for person' do
38
38
  c = controller_with_params(:id => '99', :person => ['hammer', '3'], :lucky_numbers => ['2'])
39
39
  err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
40
- assert_equal({'errors' => {'person' => ['must be a Hash']}}, err.body)
40
+ assert_equal({'error_message' => 'person must be a Hash', 'errors' => {'person' => ['person must be a Hash']}}, err.body)
41
41
  end
42
42
 
43
43
  it 'is missing person#name' do
44
44
  c = controller_with_params(:id => '99', :person => {:height => '3'}, :lucky_numbers => ['2'])
45
45
  err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
46
- assert_equal({'errors' => {'person#name' => ['is required but was not provided']}}, err.body)
46
+ assert_equal({'error_message' => 'person#name is required but was not provided', 'errors' => {'person#name' => ['person#name is required but was not provided']}}, err.body)
47
47
  end
48
48
 
49
49
  it 'is missing lucky_numbers' do
50
50
  c = controller_with_params(:id => '99', :person => {:name => 'hammer', :height => '3'})
51
51
  err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
52
- assert_equal({'errors' => {'lucky_numbers' => ['is required but was not provided']}}, err.body)
52
+ assert_equal({'error_message' => 'lucky_numbers is required but was not provided', 'errors' => {'lucky_numbers' => ['lucky_numbers is required but was not provided']}}, err.body)
53
53
  end
54
54
 
55
55
  it 'has the wrong type for lucky_numbers' do
56
56
  c = controller_with_params(:id => '99', :person => {:name => 'hammer', :height => '3'}, :lucky_numbers => '2')
57
57
  err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
58
- assert_equal({'errors' => {'lucky_numbers' => ['must be a Array']}}, err.body)
58
+ assert_equal({'error_message' => 'lucky_numbers must be a Array', 'errors' => {'lucky_numbers' => ['lucky_numbers must be a Array']}}, err.body)
59
59
  end
60
60
 
61
61
  it 'has multiple problems' do
62
62
  c = controller_with_params({})
63
63
  err = assert_raises(ApiHammer::Halt) { c.check_required_params(checks) }
64
- assert_equal({'errors' => {'id' => ['is required but was not provided'], 'person' => ['is required but was not provided'], 'lucky_numbers' => ['is required but was not provided']}}, err.body)
64
+ assert_equal({'error_message' => 'id is required but was not provided. person is required but was not provided. lucky_numbers is required but was not provided.', 'errors' => {'id' => ['id is required but was not provided'], 'person' => ['person is required but was not provided'], 'lucky_numbers' => ['lucky_numbers is required but was not provided']}}, err.body)
65
65
  end
66
66
  end
67
67
  end
data/test/halt_test.rb CHANGED
@@ -50,7 +50,7 @@ describe 'ApiHammer::Rails#halt' do
50
50
  end
51
51
  end
52
52
  haltex = assert_raises(ApiHammer::Halt) { FakeController.new.find_or_halt(model, {:id => 'anid'}) }
53
- assert_equal({'errors' => {'record' => ['Unknown record! id: anid']}}, haltex.body)
53
+ assert_equal({'error_message' => 'Unknown record! id: anid', 'errors' => {'record' => ['Unknown record! id: anid']}}, haltex.body)
54
54
  assert_equal(404, haltex.render_options[:status])
55
55
  end
56
56
  end
@@ -22,23 +22,30 @@ describe ApiHammer::RequestLogger do
22
22
  end
23
23
 
24
24
  it 'logs id and uuid (json)' do
25
- body = %q({"uuid": "theuuid", "foo_uuid": "thefoouuid", "id": "theid", "id_for_x": "theidforx", "bar.id": "thebarid", "baz-guid": "bazzz"})
25
+ body = %Q({"uuid": "theuuid", "foo_uuid": "thefoouuid", "id": "theid", "id_for_x": "theidforx", "bar.id": "thebarid", "baz-guid": "bazzz", "bigthing": "#{' ' * 4096}"})
26
26
  app = ApiHammer::RequestLogger.new(proc { |env| [200, {"Content-Type" => 'application/json; charset=UTF8'}, [body]] }, logger)
27
27
  app.call(Rack::MockRequest.env_for('/')).last.close
28
28
  assert_match(%q("body_ids":{"uuid":"theuuid","foo_uuid":"thefoouuid","id":"theid","id_for_x":"theidforx","bar.id":"thebarid","baz-guid":"bazzz"}), logio.string)
29
29
  end
30
30
 
31
31
  it 'logs id and uuid (form encoded)' do
32
- body = %q(uuid=theuuid&foo_uuid=thefoouuid&id=theid&id_for_x=theidforx&bar.id=thebarid&baz-guid=bazzz)
32
+ body = %Q(uuid=theuuid&foo_uuid=thefoouuid&id=theid&id_for_x=theidforx&bar.id=thebarid&baz-guid=bazzz&bigthing=#{' ' * 4096})
33
33
  app = ApiHammer::RequestLogger.new(proc { |env| [200, {"Content-Type" => 'application/x-www-form-urlencoded; charset=UTF8'}, [body]] }, logger)
34
34
  app.call(Rack::MockRequest.env_for('/')).last.close
35
35
  assert_match(%q("body_ids":{"uuid":"theuuid","foo_uuid":"thefoouuid","id":"theid","id_for_x":"theidforx","bar.id":"thebarid","baz-guid":"bazzz"}), logio.string)
36
36
  end
37
37
 
38
- it 'logs request and response body on error' do
39
- app = ApiHammer::RequestLogger.new(proc { |env| [400, {}, ['the_response_body']] }, logger)
38
+ it 'logs not-too-big request response bodies' do
39
+ app = ApiHammer::RequestLogger.new(proc { |env| [200, {}, ['the_response_body']] }, logger)
40
40
  app.call(Rack::MockRequest.env_for('/', :input => 'the_request_body')).last.close
41
- assert_match(/"request":\{.*"body":"the_request_body"/, logio.string)
42
- assert_match(/"response":\{.*"body":"the_response_body"/, logio.string)
41
+ assert_match(/"request":\{.*"body":"the_request_body/, logio.string)
42
+ assert_match(/"response":\{.*"body":"the_response_body/, logio.string)
43
+ end
44
+
45
+ it 'logs request and response body on error (even if big)' do
46
+ app = ApiHammer::RequestLogger.new(proc { |env| [400, {}, ["the_response_body #{' ' * 4096}"]] }, logger)
47
+ app.call(Rack::MockRequest.env_for('/', :input => "the_request_body #{' ' * 4096}")).last.close
48
+ assert_match(/"request":\{.*"body":"the_request_body/, logio.string)
49
+ assert_match(/"response":\{.*"body":"the_response_body/, logio.string)
43
50
  end
44
51
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_hammer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-07 00:00:00.000000000 Z
11
+ date: 2014-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack