batch_api2 0.3.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +30 -0
- data/changelog.md +74 -0
- data/lib/batch_api.rb +28 -0
- data/lib/batch_api/batch_error.rb +41 -0
- data/lib/batch_api/configuration.rb +36 -0
- data/lib/batch_api/error_wrapper.rb +44 -0
- data/lib/batch_api/internal_middleware.rb +87 -0
- data/lib/batch_api/internal_middleware/decode_json_body.rb +28 -0
- data/lib/batch_api/internal_middleware/response_filter.rb +27 -0
- data/lib/batch_api/operation.rb +2 -0
- data/lib/batch_api/operation/rack.rb +76 -0
- data/lib/batch_api/operation/rails.rb +42 -0
- data/lib/batch_api/processor.rb +113 -0
- data/lib/batch_api/processor/executor.rb +18 -0
- data/lib/batch_api/processor/sequential.rb +29 -0
- data/lib/batch_api/rack_middleware.rb +37 -0
- data/lib/batch_api/response.rb +38 -0
- data/lib/batch_api/utils.rb +17 -0
- data/lib/batch_api/version.rb +3 -0
- data/lib/tasks/batch_api_tasks.rake +4 -0
- data/readme.md +243 -0
- data/spec/dummy/Gemfile +1 -0
- data/spec/dummy/Gemfile.lock +8 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +15 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/javascripts/endpoints.js +2 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/assets/stylesheets/endpoints.css +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/endpoints_controller.rb +36 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/endpoints_helper.rb +2 -0
- data/spec/dummy/app/views/endpoints/get.html.erb +2 -0
- data/spec/dummy/app/views/endpoints/post.html.erb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +32 -0
- data/spec/dummy/config/boot.rb +3 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +64 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/test/functional/endpoints_controller_test.rb +14 -0
- data/spec/dummy/test/unit/helpers/endpoints_helper_test.rb +4 -0
- data/spec/lib/batch_api_spec.rb +20 -0
- data/spec/lib/batch_error_spec.rb +23 -0
- data/spec/lib/configuration_spec.rb +30 -0
- data/spec/lib/error_wrapper_spec.rb +68 -0
- data/spec/lib/internal_middleware/decode_json_body_spec.rb +44 -0
- data/spec/lib/internal_middleware/response_filter_spec.rb +61 -0
- data/spec/lib/internal_middleware_spec.rb +93 -0
- data/spec/lib/operation/rack_spec.rb +246 -0
- data/spec/lib/operation/rails_spec.rb +100 -0
- data/spec/lib/processor/executor_spec.rb +22 -0
- data/spec/lib/processor/sequential_spec.rb +39 -0
- data/spec/lib/processor_spec.rb +136 -0
- data/spec/lib/rack_middleware_spec.rb +103 -0
- data/spec/lib/response_spec.rb +53 -0
- data/spec/rack-integration/rails_spec.rb +10 -0
- data/spec/rack-integration/shared_examples.rb +273 -0
- data/spec/rack-integration/sinatra_integration_spec.rb +19 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/sinatra_app.rb +54 -0
- data/spec/support/sinatra_xhr.rb +13 -0
- metadata +214 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5555904a32bd830b6afdb63a282135a2a33cd995
|
4
|
+
data.tar.gz: 81cd01446df080d2ce53b865eaa8e24ceedeff71
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ebdd0d77a56b9d44b1e3872d95fc017c72fbaa8ec3b4daaee9bfbcdb1a6ea78e88ed98c6989c7a5c57c9446ad6637c1386c8f8487e97e258bf6e37c295dae0ae
|
7
|
+
data.tar.gz: a74c28e3dc240b0c1ed2baa01fa274a86fef87100ca0793d5357135f09b5f6ef40345c9c5da9ba805a2ebe29a63328337a8656636eb8b0e48afc9478b5511a49
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'BatchApi'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
Bundler::GemHelper.install_tasks
|
24
|
+
|
25
|
+
require 'rspec/core/rake_task'
|
26
|
+
RSpec::Core::RakeTask.new do |t|
|
27
|
+
t.rspec_opts = ["--color", '--format doc', '--order rand']
|
28
|
+
end
|
29
|
+
|
30
|
+
task :default => :spec
|
data/changelog.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
v0.3.0
|
2
|
+
|
3
|
+
New features:
|
4
|
+
|
5
|
+
* guard against nil REQUEST_URI (thanks, pbendersky and dlackty!)
|
6
|
+
* don't parse empty bodies as JSON (thanks, trungpham and dlackty!)
|
7
|
+
|
8
|
+
Testing improvements:
|
9
|
+
|
10
|
+
* modernize test infrastructure and test against newer Ruby versions (thanks, dwaller, pmq20 and dlackty!)
|
11
|
+
* update test Rails app version to 4.2
|
12
|
+
* modernize RSpec syntax using transpec
|
13
|
+
|
14
|
+
|
15
|
+
v0.2.2
|
16
|
+
* Update documentation to remove old options and add installation section
|
17
|
+
* Update gems
|
18
|
+
|
19
|
+
v0.2.1
|
20
|
+
* Default the method to GET if no method is specified
|
21
|
+
|
22
|
+
v0.2.0
|
23
|
+
* Refactor app to use internal middlewares for handling operations
|
24
|
+
* Refactor JSON decoding to a middleware
|
25
|
+
* Remove timestamp option
|
26
|
+
|
27
|
+
v0.1.4
|
28
|
+
* Refactor errors into ErrorWrapper/BatchError
|
29
|
+
* Allow specification of custom status codes raised for errors
|
30
|
+
|
31
|
+
v0.1.3
|
32
|
+
* Refactor config to use a struct
|
33
|
+
* Update readme to cover HTTP pipelining
|
34
|
+
|
35
|
+
v0.1.2
|
36
|
+
* Rewrite the readme
|
37
|
+
* Add travis icon
|
38
|
+
|
39
|
+
v0.1.1
|
40
|
+
* Fix dumb error
|
41
|
+
|
42
|
+
v0.1.0
|
43
|
+
* Add direct support for Rails for path params
|
44
|
+
* Fix spec bugs
|
45
|
+
|
46
|
+
v0.0.8
|
47
|
+
* Return the results wrapped in a hash, rather than a raw array
|
48
|
+
* Add process_start timestamp option
|
49
|
+
|
50
|
+
v0.0.7
|
51
|
+
* Return more specific error codes to alert clients to param errors
|
52
|
+
|
53
|
+
v0.0.6
|
54
|
+
* Refactor Rack middleware to be Sinatra-compatible
|
55
|
+
|
56
|
+
v0.0.5
|
57
|
+
* Add setting to decode JSON responses before sending batch results
|
58
|
+
|
59
|
+
v0.0.4
|
60
|
+
* Switch from Rails-based process to a Rack middleware
|
61
|
+
* Improve tests
|
62
|
+
|
63
|
+
v0.0.3
|
64
|
+
* Encapsulate processing into a Processor module
|
65
|
+
* Prepare for parallel processing in the future
|
66
|
+
* Add specific errors
|
67
|
+
* Allow controlling the routing target
|
68
|
+
|
69
|
+
v0.0.2
|
70
|
+
* Add config module
|
71
|
+
* Add options for operation limit, endpoint, and verb
|
72
|
+
|
73
|
+
v0.0.1
|
74
|
+
* Initial build
|
data/lib/batch_api.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'batch_api/configuration'
|
2
|
+
require 'batch_api/version'
|
3
|
+
require 'batch_api/utils'
|
4
|
+
require 'batch_api/processor'
|
5
|
+
|
6
|
+
require 'batch_api/internal_middleware'
|
7
|
+
require 'batch_api/rack_middleware'
|
8
|
+
|
9
|
+
require 'batch_api/error_wrapper'
|
10
|
+
require 'batch_api/batch_error'
|
11
|
+
|
12
|
+
module BatchApi
|
13
|
+
|
14
|
+
# Public: access the main Batch API configuration object.
|
15
|
+
#
|
16
|
+
# Returns a BatchApi::Configuration instance
|
17
|
+
def self.config
|
18
|
+
@config ||= Configuration.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: are we in Rails? This partly exists just so that you
|
22
|
+
# can stub it in the tests.
|
23
|
+
#
|
24
|
+
# Returns true if Rails is a defined constant, false otherwise.
|
25
|
+
def self.rails?
|
26
|
+
defined?(Rails)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module BatchApi
|
2
|
+
module Errors
|
3
|
+
# Public: a module that tags Batch API errors and provides a default
|
4
|
+
# status.
|
5
|
+
module BatchError
|
6
|
+
# Public: the status code for this type of error.
|
7
|
+
# Subclasses can change this as desired.
|
8
|
+
def status_code; 500; end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Public: an error thrown if an invalid option is
|
12
|
+
# specificed.
|
13
|
+
class BadOptionError < ArgumentError
|
14
|
+
include BatchError
|
15
|
+
# Public: the status code for this error.
|
16
|
+
def status_code; 422; end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: an error thrown if too many operations are provided.
|
20
|
+
class OperationLimitExceeded < ArgumentError
|
21
|
+
include BatchError
|
22
|
+
# Public: the status code for this error.
|
23
|
+
def status_code; 422; end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: an error thrown if no operations are provided.
|
27
|
+
class NoOperationsError < ArgumentError
|
28
|
+
include BatchError
|
29
|
+
# Public: the status code for this error.
|
30
|
+
def status_code; 422; end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public: an error thrown if one of the batch operations
|
34
|
+
# is somehow invalid (missing key parameters, etc.).
|
35
|
+
class MalformedOperationError < ArgumentError
|
36
|
+
include BatchError
|
37
|
+
# Public: the status code for this error.
|
38
|
+
def status_code; 422; end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'batch_api/internal_middleware'
|
2
|
+
|
3
|
+
module BatchApi
|
4
|
+
# Public: configuration options.
|
5
|
+
# Currently, you can set:
|
6
|
+
# - endpoint: (URL) through which the Batch API will be exposed (default
|
7
|
+
# "/batch)
|
8
|
+
# - verb: through which it's accessed (default "POST")
|
9
|
+
# - limit: how many requests can be processed in a single request
|
10
|
+
# (default 50)
|
11
|
+
#
|
12
|
+
# There are also two middleware-related options -- check out middleware.rb
|
13
|
+
# for more information.
|
14
|
+
# - global_middleware: any middlewares to use round the entire batch request
|
15
|
+
# (such as authentication, etc.)
|
16
|
+
# - per_op_middleware: any middlewares to run around each individual request
|
17
|
+
# (adding headers, decoding JSON, etc.)
|
18
|
+
CONFIGURATION_OPTIONS = {
|
19
|
+
verb: :post,
|
20
|
+
endpoint: "/batch",
|
21
|
+
limit: 50,
|
22
|
+
batch_middleware: InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE,
|
23
|
+
operation_middleware: InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
|
24
|
+
}
|
25
|
+
|
26
|
+
# Batch API Configuration
|
27
|
+
class Configuration < Struct.new(*CONFIGURATION_OPTIONS.keys)
|
28
|
+
# Public: initialize a new configuration option and apply the defaults.
|
29
|
+
def initialize
|
30
|
+
super
|
31
|
+
CONFIGURATION_OPTIONS.each {|k, v| self[k] = v}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module BatchApi
|
2
|
+
# Public: wrap an error thrown during a batch operation.
|
3
|
+
# This has a body class and a cookies accessor and can
|
4
|
+
# function in place of a regular BatchResponse object.
|
5
|
+
class ErrorWrapper
|
6
|
+
# Public: create a new ErrorWrapper from an error object.
|
7
|
+
def initialize(error)
|
8
|
+
@error = error
|
9
|
+
@status_code = error.status_code if error.respond_to?(:status_code)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Public: the error details as a hash, which can be returned
|
13
|
+
# to clients as JSON.
|
14
|
+
def body
|
15
|
+
message = if self.class.expose_backtrace?
|
16
|
+
{
|
17
|
+
message: @error.message,
|
18
|
+
backtrace: @error.backtrace
|
19
|
+
}
|
20
|
+
else
|
21
|
+
{ message: @error.message }
|
22
|
+
end
|
23
|
+
{ error: message }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: turn the error body into a Rack-compatible body component.
|
27
|
+
#
|
28
|
+
# Returns: an Array with the error body represented as JSON.
|
29
|
+
def render
|
30
|
+
[status_code, RackMiddleware.content_type, [body.to_json]]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public: the status code to return for the given error.
|
34
|
+
def status_code
|
35
|
+
@status_code || 500
|
36
|
+
end
|
37
|
+
|
38
|
+
# Internal: whether the backtrace should be exposed in the response.
|
39
|
+
# Currently Rails-specific, needs to be generalized (to ENV["RACK_ENV"])?
|
40
|
+
def self.expose_backtrace?
|
41
|
+
!Rails.env.production?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'middleware'
|
2
|
+
require 'batch_api/processor/sequential'
|
3
|
+
require 'batch_api/processor/executor'
|
4
|
+
require 'batch_api/internal_middleware/decode_json_body'
|
5
|
+
require 'batch_api/internal_middleware/response_filter'
|
6
|
+
|
7
|
+
module BatchApi
|
8
|
+
# Public: the internal middleware system used to process batch requests.
|
9
|
+
# (Not to be confused with RackMiddleware, which handles incoming Rack
|
10
|
+
# request.) Based on Mitchel Hashimoto's middleware gem.
|
11
|
+
#
|
12
|
+
# There are actually two internal middleware stacks, one run around the
|
13
|
+
# entire request and the other run around each individual operation.
|
14
|
+
#
|
15
|
+
# The batch_middleware stack consists of two parts:
|
16
|
+
# 1) Custom middlewares - these middlewares will be run around the entire
|
17
|
+
# sequence of batch requests. Useful for global processing on a batch
|
18
|
+
# request; for instance, the timestamp middleware adds a timestamp based on
|
19
|
+
# when the original request was started. This should return an array of
|
20
|
+
# BatchApi::Response objects.
|
21
|
+
#
|
22
|
+
# 2) Processor - this automatically-provided middleware will execute all
|
23
|
+
# batch requests either sequentially (currently the only option) or in
|
24
|
+
# parallel (in the future). This will return an array of
|
25
|
+
# BatchApi::Response objects.
|
26
|
+
#
|
27
|
+
# The operation_middleware stack also consists of two parts:
|
28
|
+
# 1) Operation - these middlewares will run once per each operation, giving
|
29
|
+
# you a chance to alter the results or details of an op -- for instance,
|
30
|
+
# decoding the body if it's JSON. This should return an individual
|
31
|
+
# BatchApi::Response object.
|
32
|
+
#
|
33
|
+
# 2) Executor - this automatically-provided middleware actually executes
|
34
|
+
# the individual Rack request, and returns a BatchApi::Response object.
|
35
|
+
#
|
36
|
+
# Symetry, right?
|
37
|
+
#
|
38
|
+
# All middlewares#call will receive the following as an env hash:
|
39
|
+
# {
|
40
|
+
# # for batch middlewares
|
41
|
+
# ops: [], # the total set of operations
|
42
|
+
# # for operation middlewares
|
43
|
+
# op: obj, # the specific operation being executed
|
44
|
+
# # all middlewares get the following:
|
45
|
+
# rack_env: {}, # the Rack environment
|
46
|
+
# rack_app: app # the Rack application
|
47
|
+
# }
|
48
|
+
#
|
49
|
+
# All middlewares should return the result of their individual operation or
|
50
|
+
# the array of operation results, depending on where they are in the chain.
|
51
|
+
# (See above.)
|
52
|
+
module InternalMiddleware
|
53
|
+
# Public: the default internal middlewares to be run around the entire
|
54
|
+
# operation.
|
55
|
+
DEFAULT_BATCH_MIDDLEWARE = Proc.new {}
|
56
|
+
|
57
|
+
# Public: the default internal middlewares to be run around each batch
|
58
|
+
# operation.
|
59
|
+
DEFAULT_OPERATION_MIDDLEWARE = Proc.new do
|
60
|
+
# Decode JSON response bodies, so they're not double-encoded.
|
61
|
+
use InternalMiddleware::ResponseFilter
|
62
|
+
use InternalMiddleware::DecodeJsonBody
|
63
|
+
end
|
64
|
+
|
65
|
+
# Public: the middleware stack to use for processing the batch request as a
|
66
|
+
# whole..
|
67
|
+
def self.batch_stack(processor)
|
68
|
+
Middleware::Builder.new do
|
69
|
+
# evaluate these in the context of the middleware stack
|
70
|
+
self.instance_eval &BatchApi.config.batch_middleware
|
71
|
+
# for now, everything's sequential, but that will change
|
72
|
+
use processor.strategy
|
73
|
+
end
|
74
|
+
end
|
75
|
+
#
|
76
|
+
# Public: the middleware stack to use for processing each batch operation.
|
77
|
+
def self.operation_stack
|
78
|
+
Middleware::Builder.new do
|
79
|
+
# evaluate the operation middleware in the context of the middleware
|
80
|
+
# stack
|
81
|
+
self.instance_eval &BatchApi.config.operation_middleware
|
82
|
+
# and end with actually executing the batch request
|
83
|
+
use Processor::Executor
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module BatchApi
|
2
|
+
module InternalMiddleware
|
3
|
+
# Public: a middleware that decodes the body of any individual batch
|
4
|
+
# operation if it's JSON.
|
5
|
+
class DecodeJsonBody
|
6
|
+
# Public: initialize the middleware.
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@app.call(env).tap do |result|
|
13
|
+
if should_decode?(result)
|
14
|
+
result.body = MultiJson.load(result.body)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def should_decode?(result)
|
22
|
+
result.headers["Content-Type"] =~ /^application\/json/ &&
|
23
|
+
# don't try to decode an empty response
|
24
|
+
result.body.present?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module BatchApi
|
2
|
+
module InternalMiddleware
|
3
|
+
# Public: a batch middleware which surpresses the response from a call. If you
|
4
|
+
# know you don't need a response (for instance, for a POST or PUT), you can
|
5
|
+
# add silent: true (or any truthy value, like 1) to your operation to
|
6
|
+
# surpress all output for successful requests. Failed requests (status !=
|
7
|
+
# 2xx) will still return information.
|
8
|
+
class ResponseFilter
|
9
|
+
# Public: init the middleware.
|
10
|
+
def initialize(app)
|
11
|
+
@app = app
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: execute the call. If env[:op].options[:silent] is true, it'll
|
15
|
+
# remove any output for a successful response.
|
16
|
+
def call(env)
|
17
|
+
@app.call(env).tap do |result|
|
18
|
+
if env[:op].options["silent"] && (200...299).include?(result.status)
|
19
|
+
# we have success and a request for silence
|
20
|
+
# so remove all the content before proceeding
|
21
|
+
result.status = result.body = result.headers = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'batch_api/response'
|
2
|
+
|
3
|
+
module BatchApi
|
4
|
+
# Public: an individual batch operation.
|
5
|
+
module Operation
|
6
|
+
class Rack
|
7
|
+
attr_accessor :method, :url, :params, :headers
|
8
|
+
attr_accessor :env, :app, :result, :options
|
9
|
+
|
10
|
+
# Public: create a new Batch Operation given the specifications for a batch
|
11
|
+
# operation (as defined above) and the request environment for the main
|
12
|
+
# batch request.
|
13
|
+
def initialize(op, base_env, app)
|
14
|
+
@op = op
|
15
|
+
|
16
|
+
@method = op["method"] || "get"
|
17
|
+
@url = op["url"]
|
18
|
+
@params = op["params"] || {}
|
19
|
+
@headers = op["headers"] || {}
|
20
|
+
@options = op
|
21
|
+
|
22
|
+
raise Errors::MalformedOperationError,
|
23
|
+
"BatchAPI operation must include method (received #{@method.inspect}) " +
|
24
|
+
"and url (received #{@url.inspect})" unless @method && @url
|
25
|
+
|
26
|
+
@app = app
|
27
|
+
# deep_dup to avoid unwanted changes across requests
|
28
|
+
@env = BatchApi::Utils.deep_dup(base_env)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Execute a batch request, returning a BatchResponse object. If an error
|
32
|
+
# occurs, it returns the same results as Rails would.
|
33
|
+
def execute
|
34
|
+
process_env
|
35
|
+
begin
|
36
|
+
response = @app.call(@env)
|
37
|
+
rescue => err
|
38
|
+
response = BatchApi::ErrorWrapper.new(err).render
|
39
|
+
end
|
40
|
+
BatchApi::Response.new(response)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Internal: customize the request environment. This is currently done
|
44
|
+
# manually and feels clunky and brittle, but is mostly likely fine, though
|
45
|
+
# there are one or two environment parameters not yet adjusted.
|
46
|
+
def process_env
|
47
|
+
path, qs = @url.split("?")
|
48
|
+
|
49
|
+
# Headers
|
50
|
+
headrs = (@headers || {}).inject({}) do |heads, (k, v)|
|
51
|
+
heads.tap {|h| h["HTTP_" + k.gsub(/\-/, "_").upcase] = v}
|
52
|
+
end
|
53
|
+
# preserve original headers unless explicitly overridden
|
54
|
+
@env.merge!(headrs)
|
55
|
+
|
56
|
+
# method
|
57
|
+
@env["REQUEST_METHOD"] = @method.upcase
|
58
|
+
|
59
|
+
# path and query string
|
60
|
+
if @env["REQUEST_URI"]
|
61
|
+
# not all servers provide REQUEST_URI -- Pow, for instance, doesn't
|
62
|
+
@env["REQUEST_URI"] = @env["REQUEST_URI"].gsub(/#{BatchApi.config.endpoint}.*/, @url)
|
63
|
+
end
|
64
|
+
@env["REQUEST_PATH"] = path
|
65
|
+
@env["ORIGINAL_FULLPATH"] = @env["PATH_INFO"] = @url
|
66
|
+
|
67
|
+
@env["rack.request.query_string"] = qs
|
68
|
+
@env["QUERY_STRING"] = qs
|
69
|
+
|
70
|
+
# parameters
|
71
|
+
@env["rack.request.form_hash"] = @params
|
72
|
+
@env["rack.request.query_hash"] = @method == "get" ? @params : nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|