vayacondios-server 0.0.8 → 0.0.9
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.
- data/app/http_shim.rb +57 -23
- data/lib/vayacondios-server.rb +4 -1
- data/lib/vayacondios/server/errors/not_found.rb +6 -0
- data/lib/vayacondios/server/handlers/config_handler.rb +2 -9
- data/lib/vayacondios/server/handlers/event_handler.rb +2 -2
- data/lib/vayacondios/server/handlers/itemset_handler.rb +61 -0
- data/lib/vayacondios/server/model/itemset_document.rb +113 -0
- data/lib/vayacondios/version.rb +1 -1
- data/spec/server/config_spec.rb +16 -24
- data/spec/server/event_spec.rb +12 -14
- data/spec/server/itemset_spec.rb +294 -0
- metadata +8 -3
data/app/http_shim.rb
CHANGED
@@ -1,42 +1,65 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'vayacondios-server'
|
4
|
-
require '
|
4
|
+
require 'multi_json'
|
5
5
|
|
6
6
|
class HttpShim < Goliath::API
|
7
|
-
use Goliath::Rack::Heartbeat # respond to /status with 200, OK (monitoring, etc)
|
8
7
|
use Goliath::Rack::Tracer, 'X-Tracer' # log trace statistics
|
9
|
-
use Goliath::Rack::Params # parse & merge query and body parameters
|
10
|
-
|
11
8
|
use Goliath::Rack::DefaultMimeType # cleanup accepted media types
|
12
|
-
use Goliath::Rack::
|
9
|
+
use Goliath::Rack::Formatters::JSON # JSON output formatter
|
10
|
+
use Goliath::Rack::Render # auto-negotiate response format
|
11
|
+
use Goliath::Rack::Heartbeat # respond to /status with 200, OK (monitoring, etc)
|
13
12
|
|
14
13
|
def response(env)
|
15
14
|
# Validate path_params
|
16
15
|
path_params = parse_path(env[Goliath::Request::REQUEST_PATH])
|
17
|
-
return [400, {}, {error: "Bad Request"}] if !path_params.present?
|
18
|
-
|
16
|
+
return [400, {}, MultiJson.dump({error: "Bad Request"})] if !path_params.present? || method_name(env).nil?
|
17
|
+
|
18
|
+
# TODO refactor a middlware
|
19
|
+
# Decode the document body
|
20
|
+
body = nil
|
21
|
+
begin
|
22
|
+
if env['rack.input']
|
23
|
+
body = env['rack.input'].read
|
24
|
+
body = MultiJson.decode(body) if !body.blank?
|
25
|
+
env['rack.input'].rewind
|
26
|
+
end
|
27
|
+
rescue MultiJson::DecodeError => ex
|
28
|
+
return [400, {}, MultiJson.dump({error: "Bad Request"})]
|
29
|
+
end
|
19
30
|
# Look up handler using inflection
|
20
31
|
klass = ('vayacondios/' + path_params[:type] + '_handler').camelize.constantize
|
21
32
|
|
22
33
|
begin
|
23
|
-
|
24
|
-
|
25
|
-
|
34
|
+
case method_name(env)
|
35
|
+
|
36
|
+
when :show
|
26
37
|
record = klass.find(mongo, path_params)
|
38
|
+
[200, {}, MultiJson.dump(record.body)]
|
39
|
+
|
40
|
+
when :update
|
41
|
+
record = klass.new(mongo).update(body, path_params)
|
42
|
+
[200, {}, nil]
|
43
|
+
|
44
|
+
when :patch
|
45
|
+
record = klass.new(mongo).patch(body, path_params)
|
46
|
+
[200, {}, nil]
|
47
|
+
|
48
|
+
when :delete
|
49
|
+
record = klass.find(mongo, path_params).destroy(body)
|
50
|
+
[200, {}, MultiJson.dump(record.body)]
|
51
|
+
|
52
|
+
when :create
|
53
|
+
return [405, ({'Allow' => "GET PUT PATCH DELETE"}), nil]
|
27
54
|
end
|
55
|
+
rescue Vayacondios::Error::NotFound => ex
|
56
|
+
return [404, {}, MultiJson.dump({error: "Not Found"})]
|
28
57
|
rescue Vayacondios::Error::BadRequest => ex
|
29
|
-
return [400, {}, {error: "Bad Request"}]
|
58
|
+
return [400, {}, MultiJson.dump({error: "Bad Request"})]
|
30
59
|
rescue Exception => ex
|
31
60
|
puts ex
|
32
61
|
ex.backtrace.each{|l| puts l}
|
33
62
|
end
|
34
|
-
|
35
|
-
if !record.present?
|
36
|
-
[404, {}, {error: "Not Found"}]
|
37
|
-
end
|
38
|
-
|
39
|
-
[200, {}, record]
|
40
63
|
end
|
41
64
|
|
42
65
|
private
|
@@ -44,7 +67,7 @@ class HttpShim < Goliath::API
|
|
44
67
|
# Determine the organization, type of action (config or event), the topic,
|
45
68
|
# id, and format for the request.
|
46
69
|
def parse_path path
|
47
|
-
path_regex = /^\/v1\/(?<organization>[a-z]\w+)\/(?<type>config|event)(\/(?<topic>\w+)(\/(?<id>(\w+\/?)+))?)?(\/|\.(?<format>json))?$/i
|
70
|
+
path_regex = /^\/v1\/(?<organization>[a-z]\w+)\/(?<type>config|event|itemset)(\/(?<topic>\w+)(\/(?<id>(\w+\/?)+))?)?(\/|\.(?<format>json))?$/i
|
48
71
|
if (match = path_regex.match(path))
|
49
72
|
{}.tap do |segments|
|
50
73
|
match.names.each do |segment|
|
@@ -54,11 +77,22 @@ class HttpShim < Goliath::API
|
|
54
77
|
end
|
55
78
|
end
|
56
79
|
|
57
|
-
def method_name
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
80
|
+
def method_name env
|
81
|
+
case env['REQUEST_METHOD'].upcase
|
82
|
+
when "GET"
|
83
|
+
:show
|
84
|
+
when "PUT"
|
85
|
+
if env['HTTP_X_METHOD'] && env['HTTP_X_METHOD'].upcase == 'PATCH'
|
86
|
+
:patch
|
87
|
+
else
|
88
|
+
:update
|
89
|
+
end
|
90
|
+
when "POST"
|
91
|
+
:create
|
92
|
+
when "PATCH"
|
93
|
+
:patch
|
94
|
+
when "DELETE"
|
95
|
+
:delete
|
62
96
|
end
|
63
97
|
end
|
64
98
|
end
|
data/lib/vayacondios-server.rb
CHANGED
@@ -12,9 +12,12 @@ require 'gorillib/string/inflections'
|
|
12
12
|
require 'multi_json'
|
13
13
|
|
14
14
|
require 'vayacondios/server/errors/bad_request'
|
15
|
+
require 'vayacondios/server/errors/not_found'
|
15
16
|
|
16
17
|
require 'vayacondios/server/model/config_document'
|
17
18
|
require 'vayacondios/server/model/event_document'
|
19
|
+
require 'vayacondios/server/model/itemset_document'
|
18
20
|
|
19
21
|
require 'vayacondios/server/handlers/config_handler'
|
20
|
-
require 'vayacondios/server/handlers/event_handler'
|
22
|
+
require 'vayacondios/server/handlers/event_handler'
|
23
|
+
require 'vayacondios/server/handlers/itemset_handler'
|
@@ -21,19 +21,12 @@ class Vayacondios
|
|
21
21
|
else
|
22
22
|
existing_document = ConfigDocument.create(@mongo, document, options)
|
23
23
|
end
|
24
|
-
|
25
|
-
{
|
26
|
-
topic: existing_document.topic,
|
27
|
-
id: existing_document.id,
|
28
|
-
cargo: existing_document.body,
|
29
|
-
status: :success
|
30
|
-
}
|
31
24
|
end
|
32
25
|
|
33
26
|
def self.find(mongodb, options)
|
34
27
|
existing_document = ConfigDocument.find(mongodb, options)
|
35
|
-
|
36
|
-
existing_document
|
28
|
+
raise Error::NotFound.new unless existing_document
|
29
|
+
existing_document
|
37
30
|
end
|
38
31
|
end
|
39
32
|
end
|
@@ -26,8 +26,8 @@ class Vayacondios
|
|
26
26
|
|
27
27
|
def self.find(mongodb, options)
|
28
28
|
existing_document = EventDocument.find(mongodb, options)
|
29
|
-
|
30
|
-
existing_document
|
29
|
+
raise Error::NotFound.new unless existing_document
|
30
|
+
existing_document
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Vayacondios::ItemsetHandler
|
2
|
+
#
|
3
|
+
# This handler will accept requests to handle arrays for an organization.
|
4
|
+
# These arrays can only contain numbers and strings.
|
5
|
+
# GET requests are idempotent
|
6
|
+
# POST requests are forbidden
|
7
|
+
# PUT requests will clobber an existing array
|
8
|
+
# DELETE requests require an array of objects to remove
|
9
|
+
|
10
|
+
class Vayacondios
|
11
|
+
|
12
|
+
class ItemsetHandler
|
13
|
+
|
14
|
+
def initialize(mongodb)
|
15
|
+
@mongo = mongodb
|
16
|
+
end
|
17
|
+
|
18
|
+
def update(document, options={})
|
19
|
+
validate_options options
|
20
|
+
|
21
|
+
existing_document = ItemsetDocument.find(@mongo, options)
|
22
|
+
if existing_document
|
23
|
+
existing_document.update(document)
|
24
|
+
else
|
25
|
+
existing_document = ItemsetDocument.create(@mongo, document, options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def patch(document, options={})
|
30
|
+
validate_options options
|
31
|
+
|
32
|
+
existing_document = ItemsetDocument.find(@mongo, options)
|
33
|
+
if existing_document
|
34
|
+
existing_document.patch(document)
|
35
|
+
else
|
36
|
+
existing_document = ItemsetDocument.create(@mongo, document, options)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy(document, options={})
|
41
|
+
validate_options options
|
42
|
+
|
43
|
+
existing_document = ItemsetDocument.find(@mongo, options)
|
44
|
+
puts "destroy existing"
|
45
|
+
existing_document.destroy(document)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.find(mongodb, options)
|
49
|
+
existing_document = ItemsetDocument.find(mongodb, options)
|
50
|
+
raise Error::NotFound.new unless existing_document
|
51
|
+
existing_document
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def validate_options(options)
|
57
|
+
raise Error::BadRequest.new unless options[:topic] && options[:id]
|
58
|
+
raise Error::BadRequest.new if /\W/ =~ options[:id]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'vayacondios/server/model/document'
|
2
|
+
|
3
|
+
# The configuration model
|
4
|
+
#
|
5
|
+
# Configuration documents are key-value pairs, represented in JSON. A document
|
6
|
+
# consists of a primary key called the topic (_id in mongodb). It belongs to a
|
7
|
+
# collection named "#{organization_name}.config"
|
8
|
+
#
|
9
|
+
# Note: mongodb is passed in beacuse Goliath makes Thread lookups will not
|
10
|
+
# work while Goliath is in a streaming context.
|
11
|
+
|
12
|
+
class Vayacondios::ItemsetDocument < Vayacondios::Document
|
13
|
+
attr_reader :organization, :topic, :body, :id
|
14
|
+
|
15
|
+
def initialize(mongodb, options = {})
|
16
|
+
super options
|
17
|
+
@mongo = mongodb
|
18
|
+
options = sanitize_options(options)
|
19
|
+
@id = options[:id]
|
20
|
+
|
21
|
+
@body = nil
|
22
|
+
@mongo = mongodb
|
23
|
+
|
24
|
+
collection_name = [organization.to_s, topic, 'itemset'].join('.')
|
25
|
+
@collection = @mongo.collection(collection_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.create(mongodb, document, options={})
|
29
|
+
self.new(mongodb, options).update(document)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.find(mongodb, options={})
|
33
|
+
self.new(mongodb, options).find
|
34
|
+
end
|
35
|
+
|
36
|
+
def find
|
37
|
+
result = @collection.find_one({_id: @id})
|
38
|
+
|
39
|
+
if result
|
40
|
+
result.delete("_id")
|
41
|
+
@body = result["d"]
|
42
|
+
self
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def update(document)
|
49
|
+
raise Vayacondios::Error::BadRequest.new if !document.is_a?(Array)
|
50
|
+
|
51
|
+
@body = document
|
52
|
+
|
53
|
+
@collection.update({:_id => @id}, {:_id => @id, 'd' => @body }, {upsert: true})
|
54
|
+
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def patch(document)
|
59
|
+
raise Vayacondios::Error::BadRequest.new if !document.is_a?(Array)
|
60
|
+
|
61
|
+
# Merge ourselves
|
62
|
+
if @body
|
63
|
+
@body = body + document
|
64
|
+
else
|
65
|
+
@body = document
|
66
|
+
end
|
67
|
+
|
68
|
+
@collection.update({:_id => @id}, {
|
69
|
+
'$addToSet' => {
|
70
|
+
'd' => {
|
71
|
+
'$each'=> document
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}, {upsert: true})
|
75
|
+
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def destroy(document)
|
80
|
+
raise Vayacondios::Error::BadRequest.new if !document.is_a?(Array)
|
81
|
+
|
82
|
+
@body -= document
|
83
|
+
|
84
|
+
@collection.update({:_id => @id}, {
|
85
|
+
'$pullAll' => {
|
86
|
+
'd' => document
|
87
|
+
}
|
88
|
+
})
|
89
|
+
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
def sanitize_options(options)
|
96
|
+
options = options.symbolize_keys
|
97
|
+
|
98
|
+
topic = options[:topic]
|
99
|
+
|
100
|
+
if (topic.is_a?(Hash) && topic["$oid"].present?)
|
101
|
+
topic = BSON::ObjectId(topic["$oid"])
|
102
|
+
elsif topic.is_a?(String)
|
103
|
+
topic = topic.gsub(/\W/,'')
|
104
|
+
if topic.to_s.match(/^[a-f0-9]{24}$/)
|
105
|
+
topic = BSON::ObjectId(topic)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
field = options[:field].gsub(/\W/, '') if options[:field].present?
|
110
|
+
|
111
|
+
options.merge(topic: topic, field: field)
|
112
|
+
end
|
113
|
+
end
|
data/lib/vayacondios/version.rb
CHANGED
data/spec/server/config_spec.rb
CHANGED
@@ -12,9 +12,9 @@ describe HttpShim do
|
|
12
12
|
context 'Configuration management' do
|
13
13
|
it 'requires a topic' do
|
14
14
|
with_api(HttpShim) do |api|
|
15
|
-
|
15
|
+
put_request({
|
16
16
|
:path => '/v1/infochimps/config/',
|
17
|
-
:body => {:level=>"awesome"}
|
17
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
18
18
|
}, err) do |c|
|
19
19
|
c.response_header.status.should == 400
|
20
20
|
end
|
@@ -23,9 +23,9 @@ describe HttpShim do
|
|
23
23
|
|
24
24
|
it 'requires an id' do
|
25
25
|
with_api(HttpShim) do |api|
|
26
|
-
|
26
|
+
put_request({
|
27
27
|
:path => '/v1/infochimps/config/power',
|
28
|
-
:body => {:level=>"awesome"}
|
28
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
29
29
|
}, err) do |c|
|
30
30
|
c.response_header.status.should == 400
|
31
31
|
end
|
@@ -34,19 +34,11 @@ describe HttpShim do
|
|
34
34
|
|
35
35
|
it 'stores configuration' do
|
36
36
|
with_api(HttpShim) do |api|
|
37
|
-
|
37
|
+
put_request({
|
38
38
|
:path => '/v1/infochimps/config/power/level',
|
39
|
-
:body => {:level=>"awesome"}
|
39
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
40
40
|
}, err) do |c|
|
41
41
|
c.response_header.status.should == 200
|
42
|
-
MultiJson.load(c.response).should eql ({
|
43
|
-
"topic" => "power",
|
44
|
-
"id" => "level",
|
45
|
-
"status" => "success",
|
46
|
-
"cargo" => {
|
47
|
-
"level" => "awesome"
|
48
|
-
}
|
49
|
-
})
|
50
42
|
end
|
51
43
|
|
52
44
|
get_mongo_db do |db|
|
@@ -57,9 +49,9 @@ describe HttpShim do
|
|
57
49
|
|
58
50
|
it 'rejects deep IDs' do
|
59
51
|
with_api(HttpShim) do |api|
|
60
|
-
|
52
|
+
put_request({
|
61
53
|
:path => '/v1/infochimps/config/power/level/is/invalid',
|
62
|
-
:body => {:level=>"awesome"}
|
54
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
63
55
|
}, err) do |c|
|
64
56
|
c.response_header.status.should == 400
|
65
57
|
end
|
@@ -72,9 +64,9 @@ describe HttpShim do
|
|
72
64
|
|
73
65
|
it 'retrieves configuration' do
|
74
66
|
with_api(HttpShim) do |api|
|
75
|
-
|
67
|
+
put_request({
|
76
68
|
:path => '/v1/infochimps/config/power/level',
|
77
|
-
:body => {:level=>"awesome"}
|
69
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
78
70
|
}, err)
|
79
71
|
end
|
80
72
|
with_api(HttpShim) do |api|
|
@@ -87,15 +79,15 @@ describe HttpShim do
|
|
87
79
|
|
88
80
|
it 'merge deep configuration' do
|
89
81
|
with_api(HttpShim) do |api|
|
90
|
-
|
82
|
+
put_request({
|
91
83
|
:path => '/v1/infochimps/config/merge/test',
|
92
|
-
:body => { :foo => { :bar => 3 } }
|
84
|
+
:body => MultiJson.dump({ :foo => { :bar => 3 } })
|
93
85
|
}, err)
|
94
86
|
end
|
95
87
|
with_api(HttpShim) do |api|
|
96
|
-
|
88
|
+
put_request({
|
97
89
|
:path => '/v1/infochimps/config/merge/test',
|
98
|
-
:body => { :foo => { :baz => 7 } }
|
90
|
+
:body => MultiJson.dump({ :foo => { :baz => 7 } })
|
99
91
|
}, err)
|
100
92
|
end
|
101
93
|
with_api(HttpShim) do |api|
|
@@ -103,8 +95,8 @@ describe HttpShim do
|
|
103
95
|
c.response_header.status.should == 200
|
104
96
|
MultiJson.load(c.response).should eql({
|
105
97
|
"foo" => {
|
106
|
-
"bar" =>
|
107
|
-
"baz" =>
|
98
|
+
"bar" => 3,
|
99
|
+
"baz" => 7
|
108
100
|
}
|
109
101
|
})
|
110
102
|
end
|
data/spec/server/event_spec.rb
CHANGED
@@ -12,9 +12,9 @@ describe HttpShim do
|
|
12
12
|
context 'Event tracking' do
|
13
13
|
it 'requires a topic' do
|
14
14
|
with_api(HttpShim) do |api|
|
15
|
-
|
15
|
+
put_request({
|
16
16
|
:path => '/v1/infochimps/event/',
|
17
|
-
:body => {:level=>"awesome"}
|
17
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
18
18
|
}, err) do |c|
|
19
19
|
c.response_header.status.should == 400
|
20
20
|
end
|
@@ -23,9 +23,9 @@ describe HttpShim do
|
|
23
23
|
|
24
24
|
it 'does not require an id' do
|
25
25
|
with_api(HttpShim) do |api|
|
26
|
-
|
26
|
+
put_request({
|
27
27
|
:path => '/v1/infochimps/event/power',
|
28
|
-
:body => {:level=>"awesome"}
|
28
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
29
29
|
}, err) do |c|
|
30
30
|
c.response_header.status.should == 200
|
31
31
|
end
|
@@ -34,9 +34,9 @@ describe HttpShim do
|
|
34
34
|
|
35
35
|
it 'will accept an id' do
|
36
36
|
with_api(HttpShim) do |api|
|
37
|
-
|
37
|
+
put_request({
|
38
38
|
:path => '/v1/infochimps/event/power/level',
|
39
|
-
:body => {:level=>"awesome"}
|
39
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
40
40
|
}, err) do |c|
|
41
41
|
c.response_header.status.should == 200
|
42
42
|
end
|
@@ -45,9 +45,9 @@ describe HttpShim do
|
|
45
45
|
|
46
46
|
it 'rejects deep IDs' do
|
47
47
|
with_api(HttpShim) do |api|
|
48
|
-
|
48
|
+
put_request({
|
49
49
|
:path => '/v1/infochimps/event/power/level/is/invalid',
|
50
|
-
:body => {:level=>"awesome"}
|
50
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
51
51
|
}, err) do |c|
|
52
52
|
c.response_header.status.should == 400
|
53
53
|
end
|
@@ -60,12 +60,11 @@ describe HttpShim do
|
|
60
60
|
|
61
61
|
it 'stores events' do
|
62
62
|
with_api(HttpShim) do |api|
|
63
|
-
|
63
|
+
put_request({
|
64
64
|
:path => '/v1/infochimps/event/power/level',
|
65
|
-
:body => {:level=>"awesome"}
|
65
|
+
:body => MultiJson.dump({:level=>"awesome"})
|
66
66
|
}, err) do |c|
|
67
67
|
c.response_header.status.should == 200
|
68
|
-
MultiJson.load(c.response).should eql ({"level" => "awesome"})
|
69
68
|
end
|
70
69
|
|
71
70
|
get_mongo_db do |db|
|
@@ -80,12 +79,11 @@ describe HttpShim do
|
|
80
79
|
it 'retrieves events' do
|
81
80
|
current_time = Time.now
|
82
81
|
with_api(HttpShim) do |api|
|
83
|
-
|
82
|
+
put_request({
|
84
83
|
:path => '/v1/infochimps/event/power/level',
|
85
|
-
:body => {:level=>"awesome", :_timestamp => current_time}
|
84
|
+
:body => MultiJson.dump({:level=>"awesome", :_timestamp => current_time})
|
86
85
|
}, err) do |c|
|
87
86
|
c.response_header.status.should == 200
|
88
|
-
MultiJson.load(c.response).should eql ({"level" => "awesome", "_timestamp" => current_time.to_s})
|
89
87
|
end
|
90
88
|
end
|
91
89
|
with_api(HttpShim) do |api|
|
@@ -0,0 +1,294 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), '../../', 'app/http_shim')
|
6
|
+
|
7
|
+
describe HttpShim do
|
8
|
+
include Goliath::TestHelper
|
9
|
+
|
10
|
+
let(:err) { Proc.new{ |c| fail "HTTP Request Failed #{c.response}" } }
|
11
|
+
|
12
|
+
context 'Basic requirements' do
|
13
|
+
it 'requires a topic' do
|
14
|
+
with_api(HttpShim) do |api|
|
15
|
+
put_request({
|
16
|
+
:path => '/v1/infochimps/itemset/',
|
17
|
+
:body => MultiJson.dump(["foo"])
|
18
|
+
}, err) do |c|
|
19
|
+
c.response_header.status.should == 400
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'requires an id' do
|
25
|
+
with_api(HttpShim) do |api|
|
26
|
+
put_request({
|
27
|
+
:path => '/v1/infochimps/itemset/power',
|
28
|
+
:body => MultiJson.dump(["foo"])
|
29
|
+
}, err) do |c|
|
30
|
+
c.response_header.status.should == 400
|
31
|
+
end
|
32
|
+
|
33
|
+
get_mongo_db do |db|
|
34
|
+
db.collection("infochimps.power.itemset").find_one().should be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'rejects deep IDs' do
|
40
|
+
with_api(HttpShim) do |api|
|
41
|
+
put_request({
|
42
|
+
:path => '/v1/infochimps/itemset/power/level/is/invalid',
|
43
|
+
:body => MultiJson.dump(["foo"])
|
44
|
+
}, err) do |c|
|
45
|
+
c.response_header.status.should == 400
|
46
|
+
end
|
47
|
+
|
48
|
+
get_mongo_db do |db|
|
49
|
+
db.collection("infochimps.power.itemset").find_one({:_id => "level"}).should be_nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'handles PUT requests' do
|
56
|
+
it 'only accepts arrays' do
|
57
|
+
with_api(HttpShim) do |api|
|
58
|
+
put_request({
|
59
|
+
:path => '/v1/infochimps/itemset/power/level',
|
60
|
+
:body => {"foo" => "bar"}
|
61
|
+
}, err) do |c|
|
62
|
+
c.response_header.status.should == 400
|
63
|
+
end
|
64
|
+
|
65
|
+
get_mongo_db do |db|
|
66
|
+
db.collection("infochimps.itemset").find_one({:_id => "power"}).should be_nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
with_api(HttpShim) do |api|
|
70
|
+
put_request({
|
71
|
+
:path => '/v1/infochimps/itemset/power/level',
|
72
|
+
:body => "foo"
|
73
|
+
}, err) do |c|
|
74
|
+
c.response_header.status.should == 400
|
75
|
+
end
|
76
|
+
|
77
|
+
get_mongo_db do |db|
|
78
|
+
db.collection("infochimps.itemset").find_one({:_id => "power"}).should be_nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "stores the array when the resource doesn't exist" do
|
84
|
+
with_api(HttpShim) do |api|
|
85
|
+
put_request({
|
86
|
+
:path => '/v1/infochimps/itemset/power/level',
|
87
|
+
:body => MultiJson.dump(["foo", "bar"]).to_s
|
88
|
+
}, err) do |c|
|
89
|
+
c.response_header.status.should == 200 # TODO Make this 201 Created
|
90
|
+
c.response.should eql ""
|
91
|
+
end
|
92
|
+
|
93
|
+
get_mongo_db do |db|
|
94
|
+
db.collection("infochimps.power.itemset").find_one({:_id => "level"})["d"].should eql ["foo", "bar"]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "clobbers the previous array when the resource does exist" do
|
100
|
+
with_api(HttpShim) do |api|
|
101
|
+
put_request({
|
102
|
+
:path => '/v1/infochimps/itemset/power/level',
|
103
|
+
:body => MultiJson.dump(["chimpanzee", "bonobo"])
|
104
|
+
}, err) do |c|
|
105
|
+
c.response_header.status.should == 200 # TODO Make this 204 No content
|
106
|
+
c.response.should eql ""
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Verify the first was created
|
111
|
+
get_mongo_db do |db|
|
112
|
+
db.collection("infochimps.power.itemset").find_one({:_id => "level"})["d"].should eql ["chimpanzee", "bonobo"]
|
113
|
+
end
|
114
|
+
|
115
|
+
with_api(HttpShim) do |api|
|
116
|
+
put_request({
|
117
|
+
:path => '/v1/infochimps/itemset/power/level',
|
118
|
+
:body => MultiJson.dump(["foo", "bar"])
|
119
|
+
}, err) do |c|
|
120
|
+
c.response_header.status.should == 200
|
121
|
+
c.response.should eql ""
|
122
|
+
end
|
123
|
+
|
124
|
+
# Verify the first was clobbered
|
125
|
+
get_mongo_db do |db|
|
126
|
+
db.collection("infochimps.power.itemset").find_one({:_id => "level"})["d"].should eql ["foo", "bar"]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'can handle GET requests' do
|
133
|
+
it 'and return 404 for missing resources' do
|
134
|
+
with_api(HttpShim) do |api|
|
135
|
+
get_request({:path => '/v1/infochimps/itemset/missing/resource'}, err) do |c|
|
136
|
+
c.response_header.status.should == 404
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'and return an array for valid resources' do
|
142
|
+
with_api(HttpShim) do |api|
|
143
|
+
put_request({
|
144
|
+
:path => '/v1/infochimps/itemset/power/level',
|
145
|
+
:body => MultiJson.dump(["foo", "bar"])
|
146
|
+
}, err)
|
147
|
+
end
|
148
|
+
with_api(HttpShim) do |api|
|
149
|
+
get_request({:path => '/v1/infochimps/itemset/power/level'}, err) do |c|
|
150
|
+
c.response_header.status.should == 200
|
151
|
+
MultiJson.load(c.response).should eql(["foo", "bar"])
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "will not handle POST requests" do
|
158
|
+
it 'fails on POST' do
|
159
|
+
with_api(HttpShim) do |api|
|
160
|
+
post_request({
|
161
|
+
:path => '/v1/infochimps/itemset/post/unsupported',
|
162
|
+
:body => MultiJson.dump({ :totally => :ignored })
|
163
|
+
}, err) do |c|
|
164
|
+
c.response_header.status.should eql 405
|
165
|
+
c.response_header["ALLOW"].should_not be_nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "handles PATCH requests" do
|
172
|
+
it 'creates a missing resource' do
|
173
|
+
with_api(HttpShim) do |api|
|
174
|
+
put_request({
|
175
|
+
:path => '/v1/infochimps/itemset/power/level',
|
176
|
+
:head => ({'X-Method' => 'PATCH'}),
|
177
|
+
:body => MultiJson.dump(["bar"])
|
178
|
+
}, err) do |c|
|
179
|
+
c.response_header.status.should eql 200 # TODO Make this 201 Created
|
180
|
+
c.response.should eql ""
|
181
|
+
end
|
182
|
+
|
183
|
+
# Verify the resource was created
|
184
|
+
get_mongo_db do |db|
|
185
|
+
db.collection("infochimps.power.itemset").find_one({:_id => "level"})["d"].should eql ["bar"]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'merges with PATCH' do
|
191
|
+
with_api(HttpShim) do |api|
|
192
|
+
put_request({
|
193
|
+
:path => '/v1/infochimps/itemset/merge/test',
|
194
|
+
:body => MultiJson.dump(["foo"])
|
195
|
+
}, err)
|
196
|
+
end
|
197
|
+
with_api(HttpShim) do |api|
|
198
|
+
put_request({
|
199
|
+
:path => '/v1/infochimps/itemset/merge/test',
|
200
|
+
:head => ({'X-Method' => 'PATCH'}),
|
201
|
+
:body => MultiJson.dump(["bar"])
|
202
|
+
}, err)
|
203
|
+
end
|
204
|
+
with_api(HttpShim) do |api|
|
205
|
+
get_request({:path => '/v1/infochimps/itemset/merge/test'}, err) do |c|
|
206
|
+
c.response_header.status.should == 200
|
207
|
+
MultiJson.load(c.response).should eql(["foo", "bar"])
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "will handle DELETE requests" do
|
214
|
+
it 'will not delete a missing resource' do
|
215
|
+
with_api(HttpShim) do |api|
|
216
|
+
delete_request({
|
217
|
+
:path => '/v1/infochimps/itemset/merge/test',
|
218
|
+
:body => MultiJson.dump(["bar"])
|
219
|
+
}, err) do |c|
|
220
|
+
c.response_header.status.should == 404
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
it "will be ok to delete items that are don't exist" do
|
226
|
+
with_api(HttpShim) do |api|
|
227
|
+
put_request({
|
228
|
+
:path => '/v1/infochimps/itemset/power/level',
|
229
|
+
:body => MultiJson.dump(["foo"])
|
230
|
+
}, err) do |c|
|
231
|
+
c.response_header.status.should == 200 # TODO Make this 201 Created
|
232
|
+
end
|
233
|
+
end
|
234
|
+
with_api(HttpShim) do |api|
|
235
|
+
delete_request({
|
236
|
+
:path => '/v1/infochimps/itemset/power/level',
|
237
|
+
:body => MultiJson.dump(["bar"])
|
238
|
+
}, err) do |c|
|
239
|
+
c.response_header.status.should == 200 # TODO Make this 204 No content
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
it "will be delete items that exist" do
|
245
|
+
with_api(HttpShim) do |api|
|
246
|
+
put_request({
|
247
|
+
:path => '/v1/infochimps/itemset/power/level',
|
248
|
+
:body => MultiJson.dump(["foo", "bar"])
|
249
|
+
}, err) do |c|
|
250
|
+
c.response_header.status.should == 200 # TODO Makes this 201 Created
|
251
|
+
end
|
252
|
+
end
|
253
|
+
with_api(HttpShim) do |api|
|
254
|
+
delete_request({
|
255
|
+
:path => '/v1/infochimps/itemset/power/level',
|
256
|
+
:body => MultiJson.dump(["bar"])
|
257
|
+
}, err) do |c|
|
258
|
+
c.response_header.status.should == 200 # TODO Make this 204 No content
|
259
|
+
end
|
260
|
+
end
|
261
|
+
with_api(HttpShim) do |api|
|
262
|
+
get_request({:path => '/v1/infochimps/itemset/power/level'}, err) do |c|
|
263
|
+
c.response_header.status.should == 200
|
264
|
+
MultiJson.load(c.response).should eql ["foo"]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
#
|
269
|
+
it "leaves behind an empty array if everything is deleted" do
|
270
|
+
with_api(HttpShim) do |api|
|
271
|
+
put_request({
|
272
|
+
:path => '/v1/infochimps/itemset/power/level',
|
273
|
+
:body => MultiJson.dump(["foo", "bar"])
|
274
|
+
}, err) do |c|
|
275
|
+
c.response_header.status.should == 200 # TODO Makes this 201 Created
|
276
|
+
end
|
277
|
+
end
|
278
|
+
with_api(HttpShim) do |api|
|
279
|
+
delete_request({
|
280
|
+
:path => '/v1/infochimps/itemset/power/level',
|
281
|
+
:body => MultiJson.dump(["foo", "bar"])
|
282
|
+
}, err) do |c|
|
283
|
+
c.response_header.status.should == 200 # TODO Make this 204 No content
|
284
|
+
end
|
285
|
+
end
|
286
|
+
with_api(HttpShim) do |api|
|
287
|
+
get_request({:path => '/v1/infochimps/itemset/power/level'}, err) do |c|
|
288
|
+
c.response_header.status.should == 200
|
289
|
+
MultiJson.load(c.response).should eql []
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vayacondios-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -205,11 +205,14 @@ files:
|
|
205
205
|
- lib/vayacondios/client/http_client.rb
|
206
206
|
- lib/vayacondios/client/notifier.rb
|
207
207
|
- lib/vayacondios/server/errors/bad_request.rb
|
208
|
+
- lib/vayacondios/server/errors/not_found.rb
|
208
209
|
- lib/vayacondios/server/handlers/config_handler.rb
|
209
210
|
- lib/vayacondios/server/handlers/event_handler.rb
|
211
|
+
- lib/vayacondios/server/handlers/itemset_handler.rb
|
210
212
|
- lib/vayacondios/server/model/config_document.rb
|
211
213
|
- lib/vayacondios/server/model/document.rb
|
212
214
|
- lib/vayacondios/server/model/event_document.rb
|
215
|
+
- lib/vayacondios/server/model/itemset_document.rb
|
213
216
|
- lib/vayacondios/version.rb
|
214
217
|
- scripts/hadoop_monitor/configurable.rb
|
215
218
|
- scripts/hadoop_monitor/hadoop_client.rb
|
@@ -222,6 +225,7 @@ files:
|
|
222
225
|
- spec/client/notifier_spec.rb
|
223
226
|
- spec/server/config_spec.rb
|
224
227
|
- spec/server/event_spec.rb
|
228
|
+
- spec/server/itemset_spec.rb
|
225
229
|
- spec/server/server_spec.rb
|
226
230
|
- spec/spec_helper.rb
|
227
231
|
- spec/support/mongo_cleaner.rb
|
@@ -241,7 +245,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
241
245
|
version: '0'
|
242
246
|
segments:
|
243
247
|
- 0
|
244
|
-
hash:
|
248
|
+
hash: 4400394871557096309
|
245
249
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
246
250
|
none: false
|
247
251
|
requirements:
|
@@ -250,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
250
254
|
version: '0'
|
251
255
|
segments:
|
252
256
|
- 0
|
253
|
-
hash:
|
257
|
+
hash: 4400394871557096309
|
254
258
|
requirements: []
|
255
259
|
rubyforge_project:
|
256
260
|
rubygems_version: 1.8.23
|
@@ -261,6 +265,7 @@ test_files:
|
|
261
265
|
- spec/client/notifier_spec.rb
|
262
266
|
- spec/server/config_spec.rb
|
263
267
|
- spec/server/event_spec.rb
|
268
|
+
- spec/server/itemset_spec.rb
|
264
269
|
- spec/server/server_spec.rb
|
265
270
|
- spec/spec_helper.rb
|
266
271
|
- spec/support/mongo_cleaner.rb
|