grape-batch 1.1.1 → 1.1.2

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