vayacondios-server 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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 'time'
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::Render, 'json' # auto-negotiate response format
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
- if method_name == :update
24
- record = klass.new(mongo).update(env.params, path_params)
25
- elsif method_name == :get
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
- if %{PUT POST}.include?(env['REQUEST_METHOD'].upcase)
59
- :update
60
- elsif env['REQUEST_METHOD'].downcase == 'get'
61
- :get
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
@@ -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'
@@ -0,0 +1,6 @@
1
+ class Vayacondios
2
+ module Error
3
+ class NotFound < Exception
4
+ end
5
+ end
6
+ end
@@ -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
- return nil unless existing_document
36
- existing_document.body
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
- return nil unless existing_document
30
- existing_document.body
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
@@ -1,3 +1,3 @@
1
1
  class Vayacondios
2
- VERSION = '0.0.8'
2
+ VERSION = '0.0.9'
3
3
  end
@@ -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
- post_request({
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
- post_request({
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
- post_request({
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
- post_request({
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
- post_request({
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
- post_request({
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
- post_request({
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" => "3",
107
- "baz" => "7"
98
+ "bar" => 3,
99
+ "baz" => 7
108
100
  }
109
101
  })
110
102
  end
@@ -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
- post_request({
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
- post_request({
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
- post_request({
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
- post_request({
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
- post_request({
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
- post_request({
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.8
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: -662112230859084251
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: -662112230859084251
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