api_hammer 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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