batch_api 0.1.1 → 0.2.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.
Files changed (81) hide show
  1. data/changelog.md +17 -0
  2. data/lib/batch_api.rb +6 -1
  3. data/lib/batch_api/batch_error.rb +41 -0
  4. data/lib/batch_api/configuration.rb +31 -21
  5. data/lib/batch_api/error_wrapper.rb +44 -0
  6. data/lib/batch_api/internal_middleware.rb +87 -0
  7. data/lib/batch_api/internal_middleware/decode_json_body.rb +24 -0
  8. data/lib/batch_api/internal_middleware/response_filter.rb +27 -0
  9. data/lib/batch_api/operation/rack.rb +4 -5
  10. data/lib/batch_api/processor.rb +22 -20
  11. data/lib/batch_api/processor/executor.rb +18 -0
  12. data/lib/batch_api/processor/sequential.rb +29 -0
  13. data/lib/batch_api/{middleware.rb → rack_middleware.rb} +2 -2
  14. data/lib/batch_api/response.rb +10 -8
  15. data/lib/batch_api/version.rb +1 -1
  16. data/readme.md +179 -106
  17. data/spec/dummy/README.rdoc +261 -0
  18. data/spec/dummy/Rakefile +15 -0
  19. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  20. data/spec/dummy/app/assets/javascripts/endpoints.js +2 -0
  21. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  22. data/spec/dummy/app/assets/stylesheets/endpoints.css +4 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  24. data/spec/dummy/app/controllers/endpoints_controller.rb +36 -0
  25. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  26. data/spec/dummy/app/helpers/endpoints_helper.rb +2 -0
  27. data/spec/dummy/app/views/endpoints/get.html.erb +2 -0
  28. data/spec/dummy/app/views/endpoints/post.html.erb +2 -0
  29. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  30. data/spec/dummy/config.ru +4 -0
  31. data/spec/dummy/config/application.rb +63 -0
  32. data/spec/dummy/config/boot.rb +10 -0
  33. data/spec/dummy/config/database.yml +25 -0
  34. data/spec/dummy/config/environment.rb +5 -0
  35. data/spec/dummy/config/environments/development.rb +37 -0
  36. data/spec/dummy/config/environments/production.rb +67 -0
  37. data/spec/dummy/config/environments/test.rb +37 -0
  38. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  39. data/spec/dummy/config/initializers/inflections.rb +15 -0
  40. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  41. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  42. data/spec/dummy/config/initializers/session_store.rb +8 -0
  43. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  44. data/spec/dummy/config/locales/en.yml +5 -0
  45. data/spec/dummy/config/routes.rb +64 -0
  46. data/spec/dummy/db/development.sqlite3 +0 -0
  47. data/spec/dummy/db/test.sqlite3 +0 -0
  48. data/spec/dummy/log/development.log +1742 -0
  49. data/spec/dummy/log/test.log +48237 -0
  50. data/spec/dummy/public/404.html +26 -0
  51. data/spec/dummy/public/422.html +26 -0
  52. data/spec/dummy/public/500.html +25 -0
  53. data/spec/dummy/public/favicon.ico +0 -0
  54. data/spec/dummy/script/rails +6 -0
  55. data/spec/dummy/test/functional/endpoints_controller_test.rb +14 -0
  56. data/spec/dummy/test/unit/helpers/endpoints_helper_test.rb +4 -0
  57. data/spec/integration/rails_spec.rb +10 -0
  58. data/spec/integration/shared_examples.rb +256 -0
  59. data/spec/integration/sinatra_integration_spec.rb +14 -0
  60. data/spec/lib/batch_api_spec.rb +20 -0
  61. data/spec/lib/batch_error_spec.rb +23 -0
  62. data/spec/lib/configuration_spec.rb +30 -0
  63. data/spec/lib/error_wrapper_spec.rb +68 -0
  64. data/spec/lib/internal_middleware/decode_json_body_spec.rb +37 -0
  65. data/spec/lib/internal_middleware/response_filter_spec.rb +61 -0
  66. data/spec/lib/internal_middleware_spec.rb +91 -0
  67. data/spec/lib/operation/rack_spec.rb +243 -0
  68. data/spec/lib/operation/rails_spec.rb +100 -0
  69. data/spec/lib/processor/executor_spec.rb +22 -0
  70. data/spec/lib/processor/sequential_spec.rb +39 -0
  71. data/spec/lib/processor_spec.rb +134 -0
  72. data/spec/lib/rack_middleware_spec.rb +103 -0
  73. data/spec/lib/response_spec.rb +53 -0
  74. data/spec/spec_helper.rb +28 -0
  75. data/spec/support/sinatra_app.rb +54 -0
  76. metadata +148 -12
  77. data/lib/batch_api/error.rb +0 -3
  78. data/lib/batch_api/errors/base.rb +0 -45
  79. data/lib/batch_api/errors/operation.rb +0 -7
  80. data/lib/batch_api/errors/request.rb +0 -26
  81. data/lib/batch_api/processor/strategies/sequential.rb +0 -18
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class EndpointsControllerTest < ActionController::TestCase
4
+ test "should get get" do
5
+ get :get
6
+ assert_response :success
7
+ end
8
+
9
+ test "should get post" do
10
+ get :post
11
+ assert_response :success
12
+ end
13
+
14
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class EndpointsHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require_relative './shared_examples'
3
+
4
+ describe "Rails integration specs" do
5
+ before :each do
6
+ BatchApi.stub(:rails?).and_return(true)
7
+ end
8
+
9
+ it_should_behave_like "integrating with a server"
10
+ end
@@ -0,0 +1,256 @@
1
+ shared_examples_for "integrating with a server" do
2
+ def headerize(hash)
3
+ Hash[hash.map do |k, v|
4
+ ["HTTP_#{k.to_s.upcase}", v.to_s]
5
+ end]
6
+ end
7
+
8
+ before :all do
9
+ BatchApi.config.endpoint = "/batch"
10
+ BatchApi.config.verb = :post
11
+ end
12
+
13
+ before :each do
14
+ BatchApi::ErrorWrapper.stub(:expose_backtrace?).and_return(false)
15
+ end
16
+
17
+ # these are defined in the dummy app's endpoints controller
18
+ let(:get_headers) { {"foo" => "bar"} }
19
+ let(:get_params) { {"other" => "value" } }
20
+
21
+ let(:get_request) { {
22
+ url: "/endpoint",
23
+ method: "get",
24
+ headers: get_headers,
25
+ params: get_params
26
+ } }
27
+
28
+ let(:get_result) { {
29
+ status: 422,
30
+ body: {
31
+ "result" => "GET OK",
32
+ "params" => get_params.merge(
33
+ BatchApi.rails? ? {
34
+ "controller" => "endpoints",
35
+ "action" => "get"
36
+ } : {}
37
+ )
38
+ },
39
+ headers: { "GET" => "hello" }
40
+ } }
41
+
42
+ # these are defined in the dummy app's endpoints controller
43
+ let(:post_headers) { {"foo" => "bar"} }
44
+ let(:post_params) { {"other" => "value"} }
45
+
46
+ let(:post_request) { {
47
+ url: "/endpoint",
48
+ method: "post",
49
+ headers: post_headers,
50
+ params: post_params
51
+ } }
52
+
53
+ let(:post_result) { {
54
+ status: 203,
55
+ body: {
56
+ "result" => "POST OK",
57
+ "params" => post_params.merge(
58
+ BatchApi.rails? ? {
59
+ "controller" => "endpoints",
60
+ "action" => "post"
61
+ } : {}
62
+ )
63
+ },
64
+ headers: { "POST" => "guten tag" }
65
+ } }
66
+
67
+ let(:error_request) { {
68
+ url: "/endpoint/error",
69
+ method: "get"
70
+ } }
71
+
72
+ let(:error_response) { {
73
+ status: 500,
74
+ body: { "error" => { "message" => "StandardError" } }
75
+ } }
76
+
77
+ let(:missing_request) { {
78
+ url: "/dont/work",
79
+ method: "delete"
80
+ } }
81
+
82
+ let(:missing_response) { {
83
+ status: 404,
84
+ body: {}
85
+ } }
86
+
87
+ let(:parameter) {
88
+ (rand * 10000).to_i
89
+ }
90
+
91
+ let(:parameter_request) { {
92
+ url: "/endpoint/capture/#{parameter}",
93
+ method: "get"
94
+ } }
95
+
96
+ let(:parameter_result) { {
97
+ body: {
98
+ "result" => parameter.to_s
99
+ }
100
+ } }
101
+
102
+ let(:silent_request) { {
103
+ url: "/endpoint",
104
+ method: "post",
105
+ silent: true
106
+ } }
107
+
108
+ let(:failed_silent_request) {
109
+ error_request.merge(silent: true)
110
+ }
111
+
112
+ let(:failed_silent_result) {
113
+ error_response
114
+ }
115
+
116
+ before :each do
117
+ @t = Time.now
118
+ xhr :post, "/batch", {
119
+ ops: [
120
+ get_request,
121
+ post_request,
122
+ error_request,
123
+ missing_request,
124
+ parameter_request,
125
+ silent_request,
126
+ failed_silent_request
127
+ ],
128
+ sequential: true
129
+ }.to_json, "CONTENT_TYPE" => "application/json"
130
+ end
131
+
132
+ it "returns a 200" do
133
+ response.status.should == 200
134
+ end
135
+
136
+ it "includes results" do
137
+ JSON.parse(response.body)["results"].should be_a(Array)
138
+ end
139
+
140
+ context "for a get request" do
141
+ describe "the response" do
142
+ before :each do
143
+ @result = JSON.parse(response.body)["results"][0]
144
+ end
145
+
146
+ it "returns the body as objects" do
147
+ @result = JSON.parse(response.body)["results"][0]
148
+ @result["body"].should == get_result[:body]
149
+ end
150
+
151
+ it "returns the expected status" do
152
+ @result["status"].should == get_result[:status]
153
+ end
154
+
155
+ it "returns the expected headers" do
156
+ @result["headers"].should include(get_result[:headers])
157
+ end
158
+
159
+ it "verifies that the right headers were received" do
160
+ @result["headers"]["REQUEST_HEADERS"].should include(
161
+ headerize(get_headers)
162
+ )
163
+ end
164
+ end
165
+ end
166
+
167
+ context "for a request with parameters" do
168
+ describe "the response" do
169
+ before :each do
170
+ @result = JSON.parse(response.body)["results"][4]
171
+ end
172
+
173
+ it "properly parses the URL segment as a paramer" do
174
+ @result["body"].should == parameter_result[:body]
175
+ end
176
+ end
177
+ end
178
+
179
+ context "for a post request" do
180
+ describe "the response" do
181
+ before :each do
182
+ @result = JSON.parse(response.body)["results"][1]
183
+ end
184
+
185
+ it "returns the body raw if decode_json_responses = false" do
186
+ # BatchApi.config.decode_bodies = false
187
+ xhr :post, "/batch", {ops: [post_request], sequential: true}.to_json,
188
+ "CONTENT_TYPE" => "application/json"
189
+ @result = JSON.parse(response.body)["results"][0]
190
+ @result["body"].should == JSON.parse(post_result[:body].to_json)
191
+ end
192
+
193
+ it "returns the body as objects if decode_json_responses = true" do
194
+ @result["body"].should == post_result[:body]
195
+ end
196
+
197
+ it "returns the expected status" do
198
+ @result["status"].should == post_result[:status]
199
+ end
200
+
201
+ it "returns the expected headers" do
202
+ @result["headers"].should include(post_result[:headers])
203
+ end
204
+
205
+ it "verifies that the right headers were received" do
206
+ @result["headers"]["REQUEST_HEADERS"].should include(headerize(post_headers))
207
+ end
208
+ end
209
+ end
210
+
211
+ context "for a request that returns an error" do
212
+ before :each do
213
+ @result = JSON.parse(response.body)["results"][2]
214
+ end
215
+
216
+ it "returns the right status" do
217
+ @result["status"].should == error_response[:status]
218
+ end
219
+
220
+ it "returns the right error information" do
221
+ # we don't care about the backtrace,
222
+ # the main thing is that the messsage arrives
223
+ @result["body"]["error"].should include(error_response[:body]["error"])
224
+ end
225
+ end
226
+
227
+ context "for a request that returns error" do
228
+ before :each do
229
+ @result = JSON.parse(response.body)["results"][3]
230
+ end
231
+
232
+ it "returns the right status" do
233
+ @result["status"].should == 404
234
+ end
235
+ end
236
+
237
+ context "for a silent request" do
238
+ before :each do
239
+ @result = JSON.parse(response.body)["results"][5]
240
+ end
241
+
242
+ it "returns nothing" do
243
+ @result.should == {}
244
+ end
245
+ end
246
+
247
+ context "for a silent request that causes an error" do
248
+ before :each do
249
+ @result = JSON.parse(response.body)["results"][6]
250
+ end
251
+
252
+ it "returns a regular result" do
253
+ @result.keys.should_not be_empty
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'support/sinatra_app'
3
+ require 'rack/test'
4
+ require_relative './shared_examples'
5
+
6
+ describe "Sinatra integration" do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ SinatraApp
11
+ end
12
+
13
+ it_should_behave_like "integrating with a server"
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'batch_api'
3
+
4
+ describe BatchApi do
5
+ describe ".config" do
6
+ it "has a reader for config" do
7
+ BatchApi.config.should_not be_nil
8
+ end
9
+
10
+ it "provides a default config" do
11
+ BatchApi.config.should be_a(BatchApi::Configuration)
12
+ end
13
+ end
14
+
15
+ describe ".rails?" do
16
+ it "returns a value we can't test based on whether Rails is defined" do
17
+ BatchApi.rails?.should_not be_nil
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe BatchApi::Errors::BatchError do
4
+
5
+ [
6
+ BatchApi::Errors::OperationLimitExceeded,
7
+ BatchApi::Errors::BadOptionError,
8
+ BatchApi::Errors::NoOperationsError,
9
+ BatchApi::Errors::MalformedOperationError
10
+ ].each do |klass|
11
+ it "provides a #{klass} error based on ArgumentError" do
12
+ klass.superclass.should == ArgumentError
13
+ end
14
+
15
+ it "is is also a BatchError" do
16
+ klass.new.should be_a(BatchApi::Errors::BatchError)
17
+ end
18
+
19
+ it "has a status code of 422" do
20
+ klass.new.status_code.should == 422
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module BatchApi
4
+ describe Configuration do
5
+ let(:config) { Configuration.new }
6
+
7
+ describe "options" do
8
+ {
9
+ verb: :post,
10
+ endpoint: "/batch",
11
+ limit: 50,
12
+ batch_middleware: InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE,
13
+ operation_middleware: InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
14
+ }.each_pair do |option, default|
15
+ opt, defa = option, default
16
+ describe "##{opt}" do
17
+ it "has an accessor for #{opt}" do
18
+ stubby = stub
19
+ config.send("#{opt}=", stubby)
20
+ config.send(opt).should == stubby
21
+ end
22
+
23
+ it "defaults #{opt} to #{defa.inspect}" do
24
+ config.send(opt).should == defa
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end