orchestrate 0.6.3 → 0.7.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 +4 -4
- data/README.md +64 -27
- data/lib/orchestrate.rb +6 -1
- data/lib/orchestrate/api.rb +1 -0
- data/lib/orchestrate/api/errors.rb +3 -1
- data/lib/orchestrate/api/helpers.rb +1 -0
- data/lib/orchestrate/api/response.rb +43 -10
- data/lib/orchestrate/application.rb +55 -0
- data/lib/orchestrate/client.rb +5 -24
- data/lib/orchestrate/collection.rb +310 -0
- data/lib/orchestrate/key_value.rb +232 -0
- data/lib/orchestrate/version.rb +2 -1
- data/orchestrate.gemspec +1 -1
- data/test/orchestrate/api/{collections_test.rb → collections_api_test.rb} +1 -1
- data/test/orchestrate/api/exceptions_test.rb +9 -55
- data/test/orchestrate/api/{key_value_test.rb → key_value_api_test.rb} +1 -1
- data/test/orchestrate/application_test.rb +44 -0
- data/test/orchestrate/client_test.rb +19 -17
- data/test/orchestrate/collection_enumeration_test.rb +116 -0
- data/test/orchestrate/collection_kv_accessors_test.rb +145 -0
- data/test/orchestrate/collection_test.rb +63 -0
- data/test/orchestrate/key_value_persistence_test.rb +161 -0
- data/test/orchestrate/key_value_test.rb +116 -0
- data/test/test_helper.rb +134 -8
- metadata +24 -15
@@ -0,0 +1,63 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class CollectionTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def test_instantiates_with_app_and_name
|
6
|
+
app, stubs = make_application
|
7
|
+
users = Orchestrate::Collection.new(app, :users)
|
8
|
+
assert_equal app, users.app
|
9
|
+
assert_equal 'users', users.name
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_instantiates_with_client_and_name
|
13
|
+
client, stubs = make_client_and_artifacts
|
14
|
+
stubs.head("/v0") { [200, response_headers, ''] }
|
15
|
+
users = Orchestrate::Collection.new(client, :users)
|
16
|
+
assert_equal client, users.app.client
|
17
|
+
assert_equal 'users', users.name
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_destroy
|
21
|
+
app, stubs = make_application
|
22
|
+
users = Orchestrate::Collection.new(app, :users)
|
23
|
+
stubs.delete("/v0/users") do |env|
|
24
|
+
assert_equal "true", env.params["force"]
|
25
|
+
[204, response_headers, '']
|
26
|
+
end
|
27
|
+
assert true, users.destroy!
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_equality
|
31
|
+
app, stubs = make_application
|
32
|
+
app2, stubs = make_application
|
33
|
+
items1 = app[:items]
|
34
|
+
items2 = app[:items]
|
35
|
+
other = app[:other]
|
36
|
+
assert_equal items1, items2
|
37
|
+
refute_equal items1, other
|
38
|
+
refute_equal items1, OpenStruct.new({name: 'items'})
|
39
|
+
refute_equal items1, 3
|
40
|
+
refute_equal items1, app2[:items]
|
41
|
+
|
42
|
+
assert items1.eql?(items2)
|
43
|
+
assert items2.eql?(items1)
|
44
|
+
refute items1.eql?(other)
|
45
|
+
refute other.eql?(items1)
|
46
|
+
refute items1.eql?(app2[:items])
|
47
|
+
|
48
|
+
# equal? is for object identity only
|
49
|
+
refute items1.equal?(items2)
|
50
|
+
refute other.equal?(items1)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_sorting
|
54
|
+
app, stubs = make_application
|
55
|
+
app2, stubs = make_application
|
56
|
+
assert_equal(-1, app[:users] <=> app[:items])
|
57
|
+
assert_equal 0, app[:items] <=> app[:items]
|
58
|
+
assert_equal 1, app[:items] <=> app[:users]
|
59
|
+
assert_nil 2 <=> app[:items]
|
60
|
+
assert_nil app2[:items] <=> app[:items]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class KeyValuePersistenceTest < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@app, @stubs = make_application
|
6
|
+
@items = @app[:items]
|
7
|
+
@kv = make_kv_item(@items, @stubs, :loaded => Time.now - 60)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_save_performs_put_if_match_and_returns_true_on_success
|
11
|
+
@kv[:foo] = "bar"
|
12
|
+
new_ref = nil
|
13
|
+
@stubs.put("/v0/items/#{@kv.key}") do |env|
|
14
|
+
assert_equal @kv.value, JSON.parse(env.body)
|
15
|
+
assert_header 'If-Match', "\"#{@kv.ref}\"", env
|
16
|
+
new_ref = make_ref
|
17
|
+
[ 201, response_headers({'Etag' => new_ref, "Location" => "/v0/items/#{@kv.key}/refs/#{new_ref}"}), '' ]
|
18
|
+
end
|
19
|
+
assert_equal true, @kv.save
|
20
|
+
assert_equal new_ref, @kv.ref
|
21
|
+
assert_in_delta Time.now.to_f, @kv.last_request_time.to_f, 1.1
|
22
|
+
assert_equal "bar", @kv[:foo]
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_save_performs_put_if_match_and_returns_false_on_version_mismatch
|
26
|
+
@stubs.put("/v0/items/#{@kv.key}") do |env|
|
27
|
+
assert_header 'If-Match', "\"#{@kv.ref}\"", env
|
28
|
+
error_response(:version_mismatch)
|
29
|
+
end
|
30
|
+
assert_equal false, @kv.save
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_save_performs_put_if_match_and_returns_true_on_indexing_conflict
|
34
|
+
ref = @kv.ref
|
35
|
+
@stubs.put("/v0/items/#{@kv.key}") do |env|
|
36
|
+
ref = make_ref
|
37
|
+
assert_header 'If-Match', "\"#{@kv.ref}\"", env
|
38
|
+
error_response(:indexing_conflict, {
|
39
|
+
headers: {"Location" => "/v0/items/#{@kv.key}/refs/#{ref}"},
|
40
|
+
conflicts_uri: "/v0/items/#{@kv.key}/refs/#{ref}/conflicts"
|
41
|
+
})
|
42
|
+
end
|
43
|
+
assert_equal true, @kv.save
|
44
|
+
assert_equal ref, @kv.ref
|
45
|
+
assert_in_delta Time.now.to_f, @kv.last_request_time.to_f, 1.1
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_save_returns_false_on_etc_errors
|
49
|
+
@stubs.put("/v0/items/#{@kv.key}") { error_response(:bad_request) }
|
50
|
+
assert_equal false, @kv.save
|
51
|
+
|
52
|
+
@stubs.put("/v0/items/#{@kv.key}") { error_response(:service_error) }
|
53
|
+
assert_equal false, @kv.save
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_save_bang_performs_put_if_match_doesnt_raise_on_success
|
57
|
+
@kv[:foo] = "bar"
|
58
|
+
ref = @kv.ref
|
59
|
+
@stubs.put("/v0/items/#{@kv.key}") do |env|
|
60
|
+
assert_equal @kv.value, JSON.parse(env.body)
|
61
|
+
assert_header 'If-Match', %|"#{ref}"|, env
|
62
|
+
ref = make_ref
|
63
|
+
[201, response_headers({'Etag'=>%|"#{ref}"|, 'Loation'=>"/v0/items/#{@kv.key}/refs/#{ref}"}), '']
|
64
|
+
end
|
65
|
+
@kv.save!
|
66
|
+
assert_equal ref, @kv.ref
|
67
|
+
assert_in_delta Time.now.to_f, @kv.last_request_time.to_f, 1.1
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_save_bang_performs_put_if_match_raises_on_version_mismatch
|
71
|
+
@stubs.put("/v0/items/#{@kv.key}") do |env|
|
72
|
+
assert_header "If-Match", %|"#{@kv.ref}"|, env
|
73
|
+
error_response(:version_mismatch)
|
74
|
+
end
|
75
|
+
assert_raises Orchestrate::API::VersionMismatch do
|
76
|
+
@kv.save!
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_save_bang_performs_put_if_match_doesnt_raise_on_indexing_conflict
|
81
|
+
ref = @kv.ref
|
82
|
+
@stubs.put("/v0/items/#{@kv.key}") do |env|
|
83
|
+
ref = make_ref
|
84
|
+
assert_header "If-Match", %|"#{@kv.ref}"|, env
|
85
|
+
error_response(:indexing_conflict, {
|
86
|
+
headers: {"Location" => "/v0/items/#{@kv.key}/refs/#{ref}"},
|
87
|
+
conflicts_uri: "/v0/items/#{@kv.key}/refs/#{ref}/conflicts"
|
88
|
+
})
|
89
|
+
end
|
90
|
+
@kv.save!
|
91
|
+
assert_equal ref, @kv.ref
|
92
|
+
assert_in_delta Time.now.to_f, @kv.last_request_time.to_f, 1.1
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_save_bang_performs_put_if_match_raises_on_request_error
|
96
|
+
@stubs.put("/v0/items/#{@kv.key}") do |env|
|
97
|
+
assert_header "If-Match", %|"#{@kv.ref}"|, env
|
98
|
+
error_response(:bad_request)
|
99
|
+
end
|
100
|
+
assert_raises Orchestrate::API::BadRequest do
|
101
|
+
@kv.save!
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_save_bang_performs_put_if_match_raises_on_service_error
|
106
|
+
@stubs.put("/v0/items/#{@kv.key}") { error_response(:service_error) }
|
107
|
+
assert_raises Orchestrate::API::ServiceError do
|
108
|
+
@kv.save!
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_destroy_performs_delete_if_match_and_returns_true_on_success
|
113
|
+
@stubs.delete("/v0/items/#{@kv.key}") do |env|
|
114
|
+
assert_header 'If-Match', "\"#{@kv.ref}\"", env
|
115
|
+
[204, response_headers, '']
|
116
|
+
end
|
117
|
+
assert_equal true, @kv.destroy
|
118
|
+
assert_nil @kv.ref
|
119
|
+
assert_in_delta Time.now.to_f, @kv.last_request_time.to_f, 1.1
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_destroy_performs_delete_if_match_and_returns_false_on_error
|
123
|
+
@stubs.delete("/v0/items/#{@kv.key}") { error_response(:version_mismatch) }
|
124
|
+
assert_equal false, @kv.destroy
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_destroy_bang_performs_delete_raises_on_error
|
128
|
+
@stubs.delete("/v0/items/#{@kv.key}") { error_response(:version_mismatch) }
|
129
|
+
assert_raises Orchestrate::API::VersionMismatch do
|
130
|
+
@kv.destroy!
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_purge_performs_purge_if_match_and_returns_true_on_success
|
135
|
+
@stubs.delete("/v0/items/#{@kv.key}") do |env|
|
136
|
+
assert_header 'If-Match', %|"#{@kv.ref}"|, env
|
137
|
+
assert_equal "true", env.params['purge']
|
138
|
+
[ 204, response_headers, '' ]
|
139
|
+
end
|
140
|
+
assert_equal true, @kv.purge
|
141
|
+
assert_nil @kv.ref
|
142
|
+
assert_in_delta Time.now.to_f, @kv.last_request_time.to_f, 1.1
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_purge_performs_purge_if_match_and_returns_false_on_error
|
146
|
+
@stubs.delete("/v0/items/#{@kv.key}") { error_response(:version_mismatch) }
|
147
|
+
assert_equal false, @kv.purge
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_purge_bang_performs_purge_if_match_and_raises_on_error
|
151
|
+
@stubs.delete("/v0/items/#{@kv.key}") do |env|
|
152
|
+
assert_header 'If-Match', %|"#{@kv.ref}"|, env
|
153
|
+
assert_equal "true", env.params['purge']
|
154
|
+
error_response(:version_mismatch)
|
155
|
+
end
|
156
|
+
assert_raises Orchestrate::API::VersionMismatch do
|
157
|
+
@kv.purge!
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class KeyValueTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def test_load_with_collection_and_key
|
6
|
+
app, stubs = make_application
|
7
|
+
items = app[:items]
|
8
|
+
ref = "123456"
|
9
|
+
body = {"hello" => "world"}
|
10
|
+
kv = make_kv_item(items, stubs, { key: "hello", ref: ref, body: body })
|
11
|
+
assert_equal "hello", kv.key
|
12
|
+
assert_equal ref, kv.ref
|
13
|
+
assert_equal "items/hello", kv.id
|
14
|
+
assert_equal body, kv.value
|
15
|
+
assert kv.loaded?
|
16
|
+
assert_in_delta Time.now.to_f, kv.last_request_time.to_f, 1.1
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_value_accessors
|
20
|
+
app, stubs = make_application
|
21
|
+
items = app[:items]
|
22
|
+
body = {"hello" => "world", "one" => "two"}
|
23
|
+
kv = make_kv_item(items, stubs, {body: body})
|
24
|
+
assert_equal body["hello"], kv["hello"]
|
25
|
+
assert_equal body["hello"], kv[:hello]
|
26
|
+
assert_equal body["one"], kv["one"]
|
27
|
+
assert_equal body["one"], kv[:one]
|
28
|
+
|
29
|
+
kv[:one] = 1
|
30
|
+
assert_equal 1, kv["one"]
|
31
|
+
kv[:two] = "Two"
|
32
|
+
assert_equal "Two", kv[:two]
|
33
|
+
kv["three"] = "Tres"
|
34
|
+
assert_equal "Tres", kv[:three]
|
35
|
+
kv["four"] = "IV"
|
36
|
+
assert_equal "IV", kv["four"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_instantiates_from_parallel_request
|
40
|
+
app, stubs = make_application({parallel: true})
|
41
|
+
items = app[:items]
|
42
|
+
body = {"hello" => "foo"}
|
43
|
+
ref = make_ref
|
44
|
+
stubs.get("/v0/items/foo") { [200, response_headers(ref_headers(:items, :foo, ref)), body.to_json] }
|
45
|
+
stubs.put("/v0/items/bar") { [201, response_headers(ref_headers(:items, :bar, make_ref)), ''] }
|
46
|
+
results = app.in_parallel do |r|
|
47
|
+
r[:foo] = items[:foo]
|
48
|
+
r[:bar] = items.create(:bar, {})
|
49
|
+
end
|
50
|
+
assert_equal 'foo', results[:foo].key
|
51
|
+
assert_equal ref, results[:foo].ref
|
52
|
+
assert_equal body, results[:foo].value
|
53
|
+
assert_equal 'bar', results[:bar].key
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_instantiates_without_loading
|
57
|
+
app, stubs = make_application
|
58
|
+
items = app[:items]
|
59
|
+
kv = Orchestrate::KeyValue.new(items, 'keyname')
|
60
|
+
assert_equal 'keyname', kv.key
|
61
|
+
assert_equal items, kv.collection
|
62
|
+
assert ! kv.loaded?
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_instantiates_from_collection_and_listing
|
66
|
+
app, stubs = make_application
|
67
|
+
listing = make_kv_listing('items', {key: "foo"})
|
68
|
+
kv = Orchestrate::KeyValue.new(app[:items], listing, Time.now)
|
69
|
+
assert_equal 'items', kv.collection_name
|
70
|
+
assert_equal "foo", kv.key
|
71
|
+
assert_equal listing['path']['ref'], kv.ref
|
72
|
+
assert_equal listing['value'], kv.value
|
73
|
+
assert_in_delta Time.at(listing['reftime'] / 1000), kv.reftime, 1.1
|
74
|
+
assert_in_delta Time.now.to_f, kv.last_request_time.to_f, 1.1
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_equality
|
78
|
+
app, stubs = make_application
|
79
|
+
app2, stubs = make_application
|
80
|
+
items = app[:items]
|
81
|
+
|
82
|
+
foo = Orchestrate::KeyValue.new(items, :foo)
|
83
|
+
|
84
|
+
assert_equal foo, Orchestrate::KeyValue.new(items, :foo)
|
85
|
+
refute_equal foo, Orchestrate::KeyValue.new(items, :bar)
|
86
|
+
refute_equal foo, Orchestrate::KeyValue.new(app[:users], :foo)
|
87
|
+
refute_equal foo, Orchestrate::KeyValue.new(app2[:items], :foo)
|
88
|
+
refute_equal foo, :foo
|
89
|
+
|
90
|
+
assert foo.eql?(Orchestrate::KeyValue.new(items, :foo))
|
91
|
+
refute foo.eql?(Orchestrate::KeyValue.new(items, :bar))
|
92
|
+
refute foo.eql?(Orchestrate::KeyValue.new(app[:users], :foo))
|
93
|
+
refute foo.eql?(Orchestrate::KeyValue.new(app2[:items], :foo))
|
94
|
+
refute foo.eql?(:foo)
|
95
|
+
|
96
|
+
# equal? is for Object equality
|
97
|
+
assert foo.equal?(foo)
|
98
|
+
refute foo.equal?(Orchestrate::KeyValue.new(items, :foo))
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_sorting
|
102
|
+
app, stubs = make_application
|
103
|
+
app2, stubs = make_application
|
104
|
+
|
105
|
+
foo = Orchestrate::KeyValue.new(app[:items], :foo)
|
106
|
+
|
107
|
+
assert_equal(-1, foo <=> Orchestrate::KeyValue.new(app[:items], :bar))
|
108
|
+
assert_equal 0, foo <=> Orchestrate::KeyValue.new(app[:items], :foo)
|
109
|
+
assert_equal 1, foo <=> Orchestrate::KeyValue.new(app[:items], :zoo)
|
110
|
+
assert_nil foo <=> Orchestrate::KeyValue.new(app[:users], :foo)
|
111
|
+
assert_nil foo <=> Orchestrate::KeyValue.new(app2[:items], :foo)
|
112
|
+
assert_nil foo <=> :foo
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
data/test/test_helper.rb
CHANGED
@@ -7,6 +7,37 @@ require "securerandom"
|
|
7
7
|
require "time"
|
8
8
|
require "logger"
|
9
9
|
|
10
|
+
class ParallelTest < Faraday::Adapter::Test
|
11
|
+
self.supports_parallel = true
|
12
|
+
extend Faraday::Adapter::Parallelism
|
13
|
+
|
14
|
+
class Manager
|
15
|
+
def initialize
|
16
|
+
@queue = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def queue(env)
|
20
|
+
@queue.push(env)
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
@queue.each {|env| env[:response].finish(env) unless env[:response].finished? }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.setup_parallel_manager(options={})
|
29
|
+
@mgr ||= Manager.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
super(env)
|
34
|
+
env[:parallel_manager].queue(env) if env[:parallel_manager]
|
35
|
+
env[:response]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Faraday::Adapter.register_middleware :parallel_test => :ParallelTest
|
40
|
+
|
10
41
|
# Test Helpers ---------------------------------------------------------------
|
11
42
|
|
12
43
|
def output_message(name, msg = nil)
|
@@ -15,17 +46,59 @@ end
|
|
15
46
|
|
16
47
|
# TODO this is a bit messy for now at least but there's a bunch of
|
17
48
|
# intermediate state we'd have to deal with in a bunch of other places
|
18
|
-
def make_client_and_artifacts
|
49
|
+
def make_client_and_artifacts(parallel=false)
|
19
50
|
api_key = SecureRandom.hex(24)
|
20
51
|
basic_auth = "Basic #{Base64.encode64("#{api_key}:").gsub(/\n/,'')}"
|
21
52
|
stubs = Faraday::Adapter::Test::Stubs.new
|
22
53
|
client = Orchestrate::Client.new(api_key) do |f|
|
23
|
-
|
54
|
+
if parallel
|
55
|
+
f.adapter :parallel_test, stubs
|
56
|
+
else
|
57
|
+
f.adapter :test, stubs
|
58
|
+
end
|
24
59
|
f.response :logger, Logger.new(File.join(File.dirname(__FILE__), "test.log"))
|
25
60
|
end
|
26
61
|
[client, stubs, basic_auth]
|
27
62
|
end
|
28
63
|
|
64
|
+
def ref_headers(coll, key, ref)
|
65
|
+
{'Etag' => %|"#{ref}"|, 'Location' => "/v0/#{coll}/#{key}/refs/#{ref}"}
|
66
|
+
end
|
67
|
+
|
68
|
+
def make_application(opts={})
|
69
|
+
client, stubs = make_client_and_artifacts(opts[:parallel])
|
70
|
+
stubs.head("/v0") { [200, response_headers, ''] }
|
71
|
+
app = Orchestrate::Application.new(client)
|
72
|
+
[app, stubs]
|
73
|
+
end
|
74
|
+
|
75
|
+
def make_ref
|
76
|
+
SecureRandom.hex(16)
|
77
|
+
end
|
78
|
+
|
79
|
+
def make_kv_item(collection, stubs, opts={})
|
80
|
+
key = opts[:key] || 'hello'
|
81
|
+
ref = opts[:ref] || "12345"
|
82
|
+
body = opts[:body] || {"hello" => "world"}
|
83
|
+
res_headers = response_headers({
|
84
|
+
'Etag' => "\"#{ref}\"",
|
85
|
+
'Content-Location' => "/v0/#{collection.name}/#{key}/refs/#{ref}"
|
86
|
+
})
|
87
|
+
stubs.get("/v0/items/#{key}") { [200, res_headers, body.to_json] }
|
88
|
+
kv = Orchestrate::KeyValue.load(collection, key)
|
89
|
+
kv.instance_variable_set(:@last_request_time, opts[:loaded]) if opts[:loaded]
|
90
|
+
kv
|
91
|
+
end
|
92
|
+
|
93
|
+
def make_kv_listing(collection, opts={})
|
94
|
+
key = opts[:key] || "item-#{rand(1_000_000)}"
|
95
|
+
ref = opts[:ref] || make_ref
|
96
|
+
reftime = opts[:reftime] || Time.now.to_f - (rand(24) * 3600_000)
|
97
|
+
body = opts[:body] || {"key" => key}
|
98
|
+
{ "path" => { "collection" => collection, "key" => key, "ref" => ref },
|
99
|
+
"reftime" => reftime, "value" => body }
|
100
|
+
end
|
101
|
+
|
29
102
|
def capture_warnings
|
30
103
|
old, $stderr = $stderr, StringIO.new
|
31
104
|
begin
|
@@ -50,12 +123,65 @@ def chunked_encoding_header
|
|
50
123
|
end
|
51
124
|
|
52
125
|
def response_not_found(items)
|
53
|
-
{ "message" => "The requested items could not be found.",
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
}.to_json
|
126
|
+
{ "message" => "The requested items could not be found.",
|
127
|
+
"details" => {
|
128
|
+
"items" => [ items ]
|
129
|
+
},
|
130
|
+
"code" => "items_not_found"
|
131
|
+
}.to_json
|
132
|
+
end
|
133
|
+
|
134
|
+
def error_response(error, etc={})
|
135
|
+
headers = response_headers(etc.fetch(:headers, {}))
|
136
|
+
case error
|
137
|
+
when :bad_request
|
138
|
+
[400, headers, {message: "The API request is malformed.", code: "api_bad_request"}.to_json ]
|
139
|
+
when :search_query_malformed
|
140
|
+
[ 400, headers, {
|
141
|
+
message: "The search query provided is invalid.",
|
142
|
+
code: "search_query_malformed"
|
143
|
+
}.to_json ]
|
144
|
+
when :invalid_search_param
|
145
|
+
[ 400, headers, {
|
146
|
+
message: "A provided search query param is invalid.",
|
147
|
+
details: { query: "Query is empty." },
|
148
|
+
code: "search_param_invalid"
|
149
|
+
}.to_json ]
|
150
|
+
when :malformed_ref
|
151
|
+
[ 400, headers, {
|
152
|
+
message: "The provided Item Ref is malformed.",
|
153
|
+
details: { ref: "blerg" },
|
154
|
+
code: "item_ref_malformed"
|
155
|
+
}.to_json ]
|
156
|
+
when :unauthorized
|
157
|
+
[ 401, headers, {
|
158
|
+
"message" => "Valid credentials are required.",
|
159
|
+
"code" => "security_unauthorized"
|
160
|
+
}.to_json ]
|
161
|
+
when :indexing_conflict
|
162
|
+
[409, headers, {
|
163
|
+
message: "The item has been stored but conflicts were detected when indexing. Conflicting fields have not been indexed.",
|
164
|
+
details: {
|
165
|
+
conflicts: { name: { type: "string", expected: "long" } },
|
166
|
+
conflicts_uri: etc[:conflicts_uri]
|
167
|
+
},
|
168
|
+
code: "indexing_conflict"
|
169
|
+
}.to_json ]
|
170
|
+
when :version_mismatch
|
171
|
+
[412, headers, {
|
172
|
+
message: "The version of the item does not match.",
|
173
|
+
code: "item_version_mismatch"
|
174
|
+
}.to_json]
|
175
|
+
when :already_present
|
176
|
+
[ 412, headers, {
|
177
|
+
message: "The item is already present.",
|
178
|
+
code: "item_already_present"
|
179
|
+
}.to_json ]
|
180
|
+
when :service_error
|
181
|
+
headers.delete("Content-Type")
|
182
|
+
[ 500, headers, '' ]
|
183
|
+
else raise ArgumentError.new("unknown error #{error}")
|
184
|
+
end
|
59
185
|
end
|
60
186
|
|
61
187
|
# Assertion Helpers
|