batch_api 0.1.1 → 0.2.0

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