grape-batch 1.1.1 → 1.1.2

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: e7e676df65253b6b5420d637f37073653ee5a2a1
4
- data.tar.gz: 85cbb4cef22a5825990bf6c7576b47cc42cb6fd9
3
+ metadata.gz: 8bbe03da7123e206a98f1665477f2903ccff61aa
4
+ data.tar.gz: 814d658a2f6300f0b99d966be482bf49adf02f41
5
5
  SHA512:
6
- metadata.gz: b54b0663d8f320b563ad9efe6a23008a52105cde39054ea18ba85197bb14855ff61f365dda35d535d25c37c061008607a8313882b240507449d8e47bd51b645e
7
- data.tar.gz: 351fe44caed2f2a49af357381960e443f69645c45b0ca18d4e06ce87bfd04b003fcd760be7fbcb1d3efb72b9c3e9f56f888f1c2a58238b425da45f913b2379d7
6
+ metadata.gz: 991c0e72406404557650cda1dbd0303fc0f0b85f701b0570ed8019960223fe64b306af997ce6cb21479932a42d54744bb148433708f458d1326ffa638fb94830
7
+ data.tar.gz: 0822eda7abe6aaa04bdcb6ee6797549d4a8f8e745a6cb097abdfe5d81059a3da70f92cb74c9d911d4e81032e1a751758edde913585b8d0d3107ad377abf4bef8
data/grape-batch.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.add_runtime_dependency 'rack', '>= 1.5'
21
21
  spec.add_runtime_dependency 'grape', '>= 0.7.0'
22
22
  spec.add_runtime_dependency 'multi_json', '>= 1.0'
23
+ spec.add_runtime_dependency 'activesupport', '>= 4.1.0'
23
24
 
24
25
  spec.add_development_dependency 'bundler', '~> 1.6'
25
26
  spec.add_development_dependency 'rake', '~> 10.3.2'
@@ -1,12 +1,13 @@
1
1
  module Grape
2
2
  module Batch
3
3
  class Configuration
4
- attr_accessor :path, :limit, :formatter
4
+ attr_accessor :path, :limit, :formatter, :logger
5
5
 
6
6
  def initialize
7
7
  @path = '/batch'
8
8
  @limit = 10
9
9
  @formatter = Grape::Batch::Response
10
+ @logger = nil
10
11
  end
11
12
  end
12
13
 
@@ -0,0 +1,64 @@
1
+ module Grape
2
+ module Batch
3
+ class LogSubscriber < ActiveSupport::LogSubscriber
4
+ def dispatch(event)
5
+ requests = event.payload[:requests]
6
+
7
+ if logger.debug?
8
+ debug_log(requests)
9
+ else
10
+ info_log(requests)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def debug_log(requests)
17
+ logger.info 'grape/batch'
18
+
19
+ requests.map do |params|
20
+ request, response = params
21
+ logger.info " method=#{request['REQUEST_METHOD']} path=#{request['PATH_INFO']}"
22
+
23
+ if request['REQUEST_METHOD'] == 'GET'
24
+ unless request['QUERY_STRING'].empty?
25
+ logger.debug " params: #{request['QUERY_STRING'].to_s}"
26
+ end
27
+ else
28
+ if request['rack.input'].respond_to? :string
29
+ logger.debug " body: #{request['rack.input'].string}"
30
+ end
31
+ end
32
+ logger.debug " response: #{response.to_s}"
33
+ end
34
+ end
35
+
36
+ def info_log(requests)
37
+ messages = []
38
+ requests.each do |params|
39
+ request, response = params
40
+ messages << "method=#{request['REQUEST_METHOD']} path=#{request['PATH_INFO']}"
41
+ end
42
+
43
+ logger.info 'grape/batch ' + messages.join(', ')
44
+ end
45
+
46
+ def logger
47
+ @logger ||= Grape::Batch.configuration.logger || rails_logger || default_logger
48
+ end
49
+
50
+ def default_logger
51
+ logger = Logger.new($stdout)
52
+ logger.level = Logger::INFO
53
+ logger
54
+ end
55
+
56
+ # Get the Rails logger if it's defined.
57
+ def rails_logger
58
+ defined?(::Rails) && ::Rails.respond_to?(:logger) && ::Rails.logger
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ Grape::Batch::LogSubscriber.attach_to(:batch)
@@ -1,5 +1,5 @@
1
1
  module Grape
2
2
  module Batch
3
- VERSION = '1.1.1'
3
+ VERSION = '1.1.2'
4
4
  end
5
5
  end
data/lib/grape/batch.rb CHANGED
@@ -1,8 +1,11 @@
1
+ require 'active_support'
2
+ require 'grape/batch/log_subscriber'
3
+
4
+ require 'grape/batch/version'
1
5
  require 'grape/batch/errors'
2
6
  require 'grape/batch/configuration'
3
7
  require 'grape/batch/parser'
4
8
  require 'grape/batch/response'
5
- require 'grape/batch/version'
6
9
  require 'multi_json'
7
10
 
8
11
  module Grape
@@ -20,7 +23,7 @@ module Grape
20
23
 
21
24
  def batch_call(env)
22
25
  status = 200
23
- headers = {'Content-Type' => 'application/json'}
26
+ headers = { 'Content-Type' => 'application/json' }
24
27
 
25
28
  begin
26
29
  batch_requests = Grape::Batch::Validator::parse(env, Grape::Batch.configuration.limit)
@@ -38,30 +41,44 @@ module Grape
38
41
 
39
42
  def is_batch_request?(env)
40
43
  env['PATH_INFO'].start_with?(Grape::Batch.configuration.path) &&
41
- env['REQUEST_METHOD'] == 'POST' &&
42
- env['CONTENT_TYPE'] == 'application/json'
44
+ env['REQUEST_METHOD'] == 'POST' &&
45
+ env['CONTENT_TYPE'] == 'application/json'
43
46
  end
44
47
 
45
48
  def dispatch(env, batch_requests)
46
- request_env = env.dup
49
+ ActiveSupport::Notifications.instrument 'dispatch.batch' do |event|
50
+ event[:requests] = []
47
51
 
48
- batch_requests.map do |request|
49
- method = request['method']
50
- path = request['path']
51
- body = request['body'].is_a?(Hash) ? request['body'] : {}
52
+ # iterate
53
+ batch_requests.map do |request|
54
+ # init env for Grape resource
55
+ tmp_env = prepare_tmp_env(env.dup, request)
56
+ status, headers, response = @app.call(tmp_env)
52
57
 
53
- request_env['REQUEST_METHOD'] = method
54
- request_env['PATH_INFO'] = path
55
- if method == 'GET'
56
- request_env['rack.input'] = StringIO.new('{}')
57
- request_env['QUERY_STRING'] = URI.encode_www_form(body.to_a)
58
- else
59
- request_env['rack.input'] = StringIO.new(MultiJson.encode(body))
58
+ # format response
59
+ @response_klass::format(status, headers, response).tap do |formatted_response|
60
+ # log call
61
+ event[:requests] << [tmp_env, formatted_response]
62
+ end
60
63
  end
64
+ end
65
+ end
66
+
67
+ def prepare_tmp_env(tmp_env, request)
68
+ method = request['method']
69
+ path = request['path']
70
+ body = request['body'].is_a?(Hash) ? request['body'] : {}
61
71
 
62
- status, headers, response = @app.call(request_env)
72
+ tmp_env.tap do |env|
73
+ env['REQUEST_METHOD'] = method
74
+ env['PATH_INFO'] = path
63
75
 
64
- @response_klass::format(status, headers, response)
76
+ if method == 'GET'
77
+ env['rack.input'] = StringIO.new('{}')
78
+ env['QUERY_STRING'] = URI.encode_www_form(body.to_a)
79
+ else
80
+ env['rack.input'] = StringIO.new(MultiJson.encode(body))
81
+ end
65
82
  end
66
83
  end
67
84
  end
@@ -5,6 +5,10 @@ require 'grape'
5
5
  require 'api'
6
6
 
7
7
  RSpec.describe Grape::Batch::Base do
8
+ before(:all) do
9
+ Grape::Batch.configuration.logger = Logger.new('/dev/null')
10
+ end
11
+
8
12
  before :context do
9
13
  @app = Twitter::API.new
10
14
  end
@@ -31,15 +35,14 @@ RSpec.describe Grape::Batch::Base do
31
35
 
32
36
  describe 'GET /failure' do
33
37
  let(:response) { request.get('/api/v1/failure') }
34
-
35
38
  it { expect(response.status).to eq(503) }
36
- it { expect(response.body).to eq(encode({error: 'Failed as expected'})) }
39
+ it { expect(response.body).to eq(encode({ error: 'Failed as expected' })) }
37
40
  end
38
41
  end
39
42
 
40
43
  describe '/batch' do
41
44
  let(:request_body) { nil }
42
- let(:response) { request.post('/batch', {'CONTENT_TYPE' => 'application/json', input: request_body}) }
45
+ let(:response) { request.post('/batch', { 'CONTENT_TYPE' => 'application/json', input: request_body }) }
43
46
 
44
47
  context 'with invalid body' do
45
48
  it { expect(response.status).to eq(400) }
@@ -74,40 +77,40 @@ RSpec.describe Grape::Batch::Base do
74
77
  end
75
78
 
76
79
  context "when body['requests'] is not an array" do
77
- let(:request_body) { encode({requests: 'request'}) }
80
+ let(:request_body) { encode({ requests: 'request' }) }
78
81
  it { expect(response.body).to eq("'requests' is not well formatted") }
79
82
  end
80
83
 
81
84
  context 'when request limit is exceeded' do
82
- let(:request_body) { encode({requests: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}) }
85
+ let(:request_body) { encode({ requests: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] }) }
83
86
  it { expect(response.body).to eq('Batch requests limit exceeded') }
84
87
  end
85
88
 
86
89
  describe 'method attribute in request object' do
87
90
  context 'method is missing' do
88
- let(:request_body) { encode({requests: [{}]}) }
91
+ let(:request_body) { encode({ requests: [{}] }) }
89
92
  it { expect(response.body).to eq("'method' is missing in one of request objects") }
90
93
  end
91
94
 
92
95
  context 'method is not a String' do
93
- let(:request_body) { encode({requests: [{method: true}]}) }
96
+ let(:request_body) { encode({ requests: [{ method: true }] }) }
94
97
  it { expect(response.body).to eq("'method' is invalid in one of request objects") }
95
98
  end
96
99
 
97
100
  context 'method is invalid' do
98
- let(:request_body) { encode({requests: [{method: 'TRACE'}]}) }
101
+ let(:request_body) { encode({ requests: [{ method: 'TRACE' }] }) }
99
102
  it { expect(response.body).to eq("'method' is invalid in one of request objects") }
100
103
  end
101
104
  end
102
105
 
103
106
  describe 'path attribute in request object' do
104
107
  context 'path is missing' do
105
- let(:request_body) { encode({requests: [{method: 'GET'}]}) }
108
+ let(:request_body) { encode({ requests: [{ method: 'GET' }] }) }
106
109
  it { expect(response.body).to eq("'path' is missing in one of request objects") }
107
110
  end
108
111
 
109
112
  context 'path is not a String' do
110
- let(:request_body) { encode({requests: [{method: 'GET', path: 123}]}) }
113
+ let(:request_body) { encode({ requests: [{ method: 'GET', path: 123 }] }) }
111
114
  it { expect(response.body).to eq("'path' is invalid in one of request objects") }
112
115
  end
113
116
  end
@@ -115,47 +118,47 @@ RSpec.describe Grape::Batch::Base do
115
118
 
116
119
  describe 'GET' do
117
120
  context 'with no parameters' do
118
- let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/hello'}]}) }
121
+ let(:request_body) { encode({ requests: [{ method: 'GET', path: '/api/v1/hello' }] }) }
119
122
  it { expect(response.status).to eq(200) }
120
- it { expect(response.body).to eq(encode([{success: 'world'}])) }
123
+ it { expect(response.body).to eq(encode([{ success: 'world' }])) }
121
124
  end
122
125
 
123
126
  context 'with parameters' do
124
- let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/user/856'}]}) }
127
+ let(:request_body) { encode({ requests: [{ method: 'GET', path: '/api/v1/user/856' }] }) }
125
128
  it { expect(response.status).to eq(200) }
126
- it { expect(response.body).to eq(encode([{success: 'user 856'}])) }
129
+ it { expect(response.body).to eq(encode([{ success: 'user 856' }])) }
127
130
  end
128
131
 
129
132
  context 'with a body' do
130
- let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/status', body: {id: 856}}]}) }
133
+ let(:request_body) { encode({ requests: [{ method: 'GET', path: '/api/v1/status', body: { id: 856 } }] }) }
131
134
  it { expect(response.status).to eq(200) }
132
- it { expect(response.body).to eq(encode([{success: 'status 856'}])) }
135
+ it { expect(response.body).to eq(encode([{ success: 'status 856' }])) }
133
136
  end
134
137
 
135
138
  describe '404 errors' do
136
- let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/unknown'}]}) }
139
+ let(:request_body) { encode({ requests: [{ method: 'GET', path: '/api/v1/unknown' }] }) }
137
140
  it { expect(response.status).to eq(200) }
138
- it { expect(response.body).to eq(encode([{code: 404, error: '/api/v1/unknown not found'}])) }
141
+ it { expect(response.body).to eq(encode([{ code: 404, error: '/api/v1/unknown not found' }])) }
139
142
  end
140
143
  end
141
144
 
142
145
  describe 'POST' do
143
146
  context 'with no parameters' do
144
- let(:request_body) { encode({requests: [{method: 'POST', path: '/api/v1/hello'}]}) }
147
+ let(:request_body) { encode({ requests: [{ method: 'POST', path: '/api/v1/hello' }] }) }
145
148
  it { expect(response.status).to eq(200) }
146
- it { expect(response.body).to eq(encode([{success: 'world'}])) }
149
+ it { expect(response.body).to eq(encode([{ success: 'world' }])) }
147
150
  end
148
151
 
149
152
  context 'with a body' do
150
- let(:request_body) { encode({requests: [{method: 'POST', path: '/api/v1/status', body: {id: 856}}]}) }
153
+ let(:request_body) { encode({ requests: [{ method: 'POST', path: '/api/v1/status', body: { id: 856 } }] }) }
151
154
  it { expect(response.status).to eq(200) }
152
- it { expect(response.body).to eq(encode([{success: 'status 856'}])) }
155
+ it { expect(response.body).to eq(encode([{ success: 'status 856' }])) }
153
156
  end
154
157
  end
155
158
 
156
159
  describe 'POST' do
157
160
  context 'with multiple requests' do
158
- let(:request_body) { encode({requests: [{method: 'POST', path: '/api/v1/hello'}, {method: 'GET', path: '/api/v1/user/856'}]}) }
161
+ let(:request_body) { encode({ requests: [{ method: 'POST', path: '/api/v1/hello' }, { method: 'GET', path: '/api/v1/user/856' }] }) }
159
162
  it { expect(response.status).to eq(200) }
160
163
  it { expect(decode(response.body).size).to eq(2) }
161
164
  end
@@ -173,7 +176,7 @@ RSpec.describe Grape::Batch::Base do
173
176
 
174
177
  describe '.configure' do
175
178
  before do
176
- allow( Grape::Batch).to receive(:configuration) do
179
+ allow(Grape::Batch).to receive(:configuration) do
177
180
  config = Grape::Batch::Configuration.new
178
181
  config.path = '/custom_path'
179
182
  config.limit = 15
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-batch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lionel Oto
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-12-31 00:00:00.000000000 Z
13
+ date: 2015-02-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -54,6 +54,20 @@ dependencies:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
56
  version: '1.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: activesupport
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 4.1.0
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 4.1.0
57
71
  - !ruby/object:Gem::Dependency
58
72
  name: bundler
59
73
  requirement: !ruby/object:Gem::Requirement
@@ -132,6 +146,7 @@ files:
132
146
  - lib/grape/batch.rb
133
147
  - lib/grape/batch/configuration.rb
134
148
  - lib/grape/batch/errors.rb
149
+ - lib/grape/batch/log_subscriber.rb
135
150
  - lib/grape/batch/parser.rb
136
151
  - lib/grape/batch/response.rb
137
152
  - lib/grape/batch/version.rb