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 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