restfully 0.6.3 → 0.7.0.pre

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.
Files changed (73) hide show
  1. data/README.md +166 -0
  2. data/Rakefile +35 -35
  3. data/bin/restfully +68 -10
  4. data/lib/restfully.rb +8 -14
  5. data/lib/restfully/collection.rb +70 -90
  6. data/lib/restfully/error.rb +2 -0
  7. data/lib/restfully/http.rb +3 -3
  8. data/lib/restfully/http/error.rb +1 -20
  9. data/lib/restfully/http/helper.rb +49 -0
  10. data/lib/restfully/http/request.rb +60 -24
  11. data/lib/restfully/http/response.rb +55 -24
  12. data/lib/restfully/link.rb +32 -24
  13. data/lib/restfully/media_type.rb +70 -0
  14. data/lib/restfully/media_type/abstract_media_type.rb +162 -0
  15. data/lib/restfully/media_type/application_json.rb +21 -0
  16. data/lib/restfully/media_type/application_vnd_bonfire_xml.rb +177 -0
  17. data/lib/restfully/media_type/application_x_www_form_urlencoded.rb +33 -0
  18. data/lib/restfully/media_type/grid5000.rb +67 -0
  19. data/lib/restfully/media_type/wildcard.rb +27 -0
  20. data/lib/restfully/rack.rb +1 -0
  21. data/lib/restfully/rack/basic_auth.rb +26 -0
  22. data/lib/restfully/resource.rb +134 -197
  23. data/lib/restfully/session.rb +127 -70
  24. data/lib/restfully/version.rb +3 -0
  25. data/spec/fixtures/bonfire-collection-with-fragments.xml +6 -0
  26. data/spec/fixtures/bonfire-compute-existing.xml +43 -0
  27. data/spec/fixtures/bonfire-empty-collection.xml +4 -0
  28. data/spec/fixtures/bonfire-experiment-collection.xml +51 -0
  29. data/spec/fixtures/bonfire-network-collection.xml +35 -0
  30. data/spec/fixtures/bonfire-network-existing.xml +6 -0
  31. data/spec/fixtures/bonfire-root.xml +5 -0
  32. data/spec/fixtures/grid5000-rennes-jobs.json +988 -146
  33. data/spec/fixtures/grid5000-rennes.json +63 -0
  34. data/spec/restfully/collection_spec.rb +87 -0
  35. data/spec/restfully/http/helper_spec.rb +18 -0
  36. data/spec/restfully/http/request_spec.rb +97 -0
  37. data/spec/restfully/http/response_spec.rb +53 -0
  38. data/spec/restfully/link_spec.rb +80 -0
  39. data/spec/restfully/media_type/application_vnd_bonfire_xml_spec.rb +153 -0
  40. data/spec/restfully/media_type_spec.rb +117 -0
  41. data/spec/restfully/resource_spec.rb +109 -0
  42. data/spec/restfully/session_spec.rb +229 -0
  43. data/spec/spec_helper.rb +10 -9
  44. metadata +162 -83
  45. data/.document +0 -5
  46. data/CHANGELOG +0 -62
  47. data/README.rdoc +0 -146
  48. data/TODO.rdoc +0 -3
  49. data/VERSION +0 -1
  50. data/examples/grid5000.rb +0 -33
  51. data/examples/scratch.rb +0 -37
  52. data/lib/restfully/extensions.rb +0 -34
  53. data/lib/restfully/http/adapters/abstract_adapter.rb +0 -29
  54. data/lib/restfully/http/adapters/patron_adapter.rb +0 -16
  55. data/lib/restfully/http/adapters/rest_client_adapter.rb +0 -75
  56. data/lib/restfully/http/headers.rb +0 -20
  57. data/lib/restfully/parsing.rb +0 -66
  58. data/lib/restfully/special_array.rb +0 -5
  59. data/lib/restfully/special_hash.rb +0 -5
  60. data/restfully.gemspec +0 -114
  61. data/spec/collection_spec.rb +0 -120
  62. data/spec/fixtures/configuration_file.yml +0 -4
  63. data/spec/fixtures/grid5000-sites.json +0 -540
  64. data/spec/http/error_spec.rb +0 -18
  65. data/spec/http/headers_spec.rb +0 -17
  66. data/spec/http/request_spec.rb +0 -49
  67. data/spec/http/response_spec.rb +0 -19
  68. data/spec/http/rest_client_adapter_spec.rb +0 -35
  69. data/spec/link_spec.rb +0 -61
  70. data/spec/parsing_spec.rb +0 -40
  71. data/spec/resource_spec.rb +0 -320
  72. data/spec/restfully_spec.rb +0 -16
  73. data/spec/session_spec.rb +0 -171
@@ -1,18 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
- describe Restfully::HTTP::Error do
3
- it "should have a response reader" do
4
- response = mock("restfully response", :status => 404, :body => {'title' => 'Not Found', 'message' => 'The requested resource cannot be found.', 'code' => 404})
5
- error = Restfully::HTTP::Error.new(response)
6
- error.response.should == response
7
- end
8
- it "should work properly" do
9
- response = mock("restfully response", :status => 404, :body => {'title' => 'Not Found', 'message' => 'The requested resource cannot be found.', 'code' => 404})
10
- begin
11
- raise Restfully::HTTP::Error.new(response)
12
- rescue Restfully::HTTP::Error => e
13
- e.response.should == response
14
- e.message.should == "404 Not Found. The requested resource cannot be found."
15
- e.backtrace.should_not be_empty
16
- end
17
- end
18
- end
@@ -1,17 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
- describe Restfully::HTTP::Headers do
3
- class IncludeHeadersModule
4
- include Restfully::HTTP::Headers
5
- end
6
-
7
- it "should correctly parse headers" do
8
- sanitized_headers = IncludeHeadersModule.new.sanitize_http_headers('accept' => 'application/json', :x_remote_ident => 'crohr', 'X_GVI' => 'sid', 'CACHE-CONTROL' => ['max-age=0', 'no-cache'], 'Content-Length' => 22)
9
- sanitized_headers.should == {
10
- 'Accept' => 'application/json',
11
- 'X-Remote-Ident' => 'crohr',
12
- 'X-Gvi' => 'sid',
13
- 'Cache-Control' => 'max-age=0, no-cache',
14
- 'Content-Length' => 22
15
- }
16
- end
17
- end
@@ -1,49 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
- describe Restfully::HTTP::Request do
3
-
4
- it "should correctly initialize the attributes" do
5
- request = Restfully::HTTP::Request.new(
6
- 'https://api.grid5000.fr/sid/grid5000?q1=v1&q2=v2',
7
- :headers => {'accept' => 'application/xml', :cache_control => 'max-age=0', 'Content-Type' => 'application/json'},
8
- :query => {'custom_param1' => [3, 4, 5, 6], 'custom_param2' => 'value_custom_param2'},
9
- :body => {"key" => "value"}.to_json
10
- )
11
- request.uri.should be_a URI
12
- request.uri.to_s.should == 'https://api.grid5000.fr/sid/grid5000?q1=v1&q2=v2&custom_param1%5B%5D=3&custom_param1%5B%5D=4&custom_param1%5B%5D=5&custom_param1%5B%5D=6&custom_param2=value_custom_param2'
13
- request.uri.query.should == "q1=v1&q2=v2&custom_param1%5B%5D=3&custom_param1%5B%5D=4&custom_param1%5B%5D=5&custom_param1%5B%5D=6&custom_param2=value_custom_param2"
14
- request.headers.should == {
15
- "Content-Type"=>"application/json",
16
- 'Accept' => 'application/xml',
17
- 'Cache-Control' => 'max-age=0'
18
- }
19
- request.body.should == {"key" => "value"}
20
- request.raw_body.should == "{\"key\":\"value\"}"
21
- request.retries.should == 0
22
- end
23
-
24
- it "should accept a URI object as url" do
25
- request = Restfully::HTTP::Request.new(uri=URI.parse('https://api.grid5000.fr/sid/grid5000'))
26
- request.uri.to_s.should == 'https://api.grid5000.fr/sid/grid5000'
27
- end
28
-
29
- it "should not fail if there are query parameters but no query string in the given URL" do
30
- request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000', :query => {:q1 => 'v1'})
31
- request.uri.query.should == "q1=v1"
32
- end
33
-
34
- it "should not change the query string if none is given as an option" do
35
- request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000?q1=v1&q2=v2')
36
- request.uri.to_s.should == 'https://api.grid5000.fr/grid5000?q1=v1&q2=v2'
37
- request.headers.should == {}
38
- end
39
-
40
- it "should offer a function to add headers" do
41
- request = Restfully::HTTP::Request.new('https://api.grid5000.fr/grid5000?q1=v1&q2=v2', :headers => {:accept => 'application/json'})
42
- expected_headers = {
43
- 'Accept' => 'application/json',
44
- 'Cache-Control' => 'max-age=0'
45
- }
46
- request.add_headers(:cache_control => 'max-age=0').should == expected_headers
47
- request.headers.should == expected_headers
48
- end
49
- end
@@ -1,19 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
- describe Restfully::HTTP::Response do
3
-
4
- it "should correctly initialize the attributes" do
5
- response = Restfully::HTTP::Response.new(404, {:content_type => 'application/json;charset=utf-8'}, '{"property1": "value1", "property2": "value2"}')
6
- response.status.should == 404
7
- response.headers.should == {
8
- 'Content-Type' => 'application/json;charset=utf-8'
9
- }
10
- response.body.should == {
11
- 'property1' => 'value1',
12
- 'property2' => 'value2'
13
- }
14
- end
15
- it "should return the unserialized body" do
16
- response = Restfully::HTTP::Response.new(404, {:content_type => 'application/json;charset=utf-8'}, '{"property1": "value1", "property2": "value2"}')
17
- response.raw_body.should == "{\"property1\": \"value1\", \"property2\": \"value2\"}"
18
- end
19
- end
@@ -1,35 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/../spec_helper')
2
- describe Restfully::HTTP::Adapters::RestClientAdapter do
3
- it "should correctly get the resource corresponding to the request" do
4
- adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr", :username => 'crohr', :password => 'password')
5
- request = Restfully::HTTP::Request.new('http://api.local/sid/grid5000', :headers => {:accept => 'application/json'}, :query => {:q1 => 'v1'})
6
- RestClient::Resource.should_receive(:new).with('http://api.local/sid/grid5000?q1=v1', :password => 'password', :user => 'crohr').and_return(resource = mock("restclient resource"))
7
- resource.should_receive(:get).with({:accept => 'application/json'}).and_return(mock("restclient response", :headers => {:content_type => 'application/json;charset=utf-8'}, :to_s => "", :code => 200))
8
- response = adapter.get(request)
9
- response.status.should == 200
10
- response.body.should be_nil
11
- response.headers.should == {
12
- 'Content-Type' => 'application/json;charset=utf-8'
13
- }
14
- end
15
- it "should transform the username option into a user option" do
16
- adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr", :username => 'crohr', :password => 'password')
17
- adapter.options[:user].should == 'crohr'
18
- adapter.options[:username].should be_nil
19
- end
20
- it "should raise a not implemented error when trying to use functions not implemented yet" do
21
- require 'restfully/http/adapters/patron_adapter'
22
- adapter = Restfully::HTTP::Adapters::PatronAdapter.new("https://api.grid5000.fr")
23
- lambda{adapter.put(mock("restfully request"))}.should raise_error NotImplementedError, "PUT is not supported by your adapter."
24
- end
25
- it "should rescue any RestClient::Exception and correctly populate the response" do
26
- res = mock(RestClient::Response, :code => 404, :to_s => '{"message":"whatever"}', :headers => {:content_type => 'application/json;charset=utf-8', :content_length => 22})
27
- RestClient::Resource.should_receive(:new).and_raise RestClient::ResourceNotFound.new(res)
28
- adapter = Restfully::HTTP::Adapters::RestClientAdapter.new("https://api.grid5000.fr", :username => 'crohr', :password => 'password')
29
- response = adapter.get(mock("request", :uri => "uri"))
30
- response.status.should == 404
31
- response.headers.should == {'Content-Type' => 'application/json;charset=utf-8', 'Content-Length' => 22}
32
- response.raw_body.should == '{"message":"whatever"}'
33
- response.body.should == {"message"=>"whatever"}
34
- end
35
- end
data/spec/link_spec.rb DELETED
@@ -1,61 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/spec_helper')
2
-
3
- include Restfully
4
- describe Link do
5
- before do
6
- @uri = URI.parse('/x/y/z')
7
- end
8
- it "should have a rel reader" do
9
- link = Link.new
10
- link.should_not respond_to(:rel=)
11
- link.should respond_to(:rel)
12
- end
13
- it "should have a href reader" do
14
- link = Link.new
15
- link.should_not respond_to(:href=)
16
- link.should respond_to(:href)
17
- end
18
- it "should have a title reader" do
19
- link = Link.new
20
- link.should_not respond_to(:title=)
21
- link.should respond_to(:title)
22
- end
23
- it "should have a errors reader" do
24
- link = Link.new
25
- link.should_not respond_to(:errors=)
26
- link.should respond_to(:errors)
27
- end
28
- it "should respond to valid?" do
29
- link = Link.new
30
- link.should respond_to(:valid?)
31
- end
32
- it "should respond to resolved?" do
33
- link = Link.new("href" => @uri)
34
- link.should respond_to(:resolved?)
35
- end
36
- it "should respond to resolvable?" do
37
- link = Link.new
38
- link.should respond_to(:resolvable?)
39
- end
40
- it "by default, should not be resolvable" do
41
- link = Link.new
42
- link.should_not be_resolvable
43
- end
44
- it "by default, should not be resolved" do
45
- link = Link.new
46
- link.should_not be_resolved
47
- end
48
- it "should be valid even if the href is ''" do
49
- link = Link.new 'rel' => 'collection', 'title' => 'my collection'
50
- link.should be_valid
51
- link.href.should == URI.parse("")
52
- end
53
- it "should not be valid if there is no rel" do
54
- link = Link.new 'href' => '/', 'title' => 'my collection'
55
- link.should_not be_valid
56
- end
57
- it "should not be valid if the rel is valid but requires a title that is not given" do
58
- link = Link.new 'rel' => 'collection', 'href' => '/'
59
- link.should_not be_valid
60
- end
61
- end
data/spec/parsing_spec.rb DELETED
@@ -1,40 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/spec_helper')
2
-
3
- include Restfully::Parsing
4
-
5
- describe Restfully::Parsing do
6
- class IncludesParsingModule
7
- include Restfully::Parsing
8
- end
9
- it "should make available the serialize and unserialize methods" do
10
- klass = IncludesParsingModule.new
11
- klass.should respond_to(:unserialize)
12
- klass.should respond_to(:serialize)
13
- end
14
- it "should raise a ParserNotFound error if the object cannot be parsed" do
15
- lambda{unserialize("whatever", :content_type => 'unknown')}.should raise_error(Restfully::Parsing::ParserNotFound, "Cannot find a parser to parse 'unknown' content.")
16
- end
17
- it "should correctly unserialize json content" do
18
- object = {'p1' => 'v1'}
19
- unserialize(object.to_json, :content_type => 'application/json;charset=utf-8').should == object
20
- end
21
- it "should correctly serialize an object into json" do
22
- object = {'p1' => 'v1'}
23
- serialize(object, :content_type => 'application/json;charset=utf-8').should == object.to_json
24
- end
25
- it "should correctly unserialize text content" do
26
- object = "anything"
27
- unserialize(object, :content_type => 'text/plain;charset=utf-8').should == object
28
- end
29
- it "should allow to define its own parser" do
30
- Restfully::Parsing::PARSERS.push({
31
- :supported_types => "text/unknown",
32
- :parse => lambda{|object, options| object.gsub(/x/, "y")},
33
- :dump => lambda{|object, options| object.gsub(/y/, "x")}
34
- })
35
- object = "xyz"
36
- parsed = unserialize(object, :content_type => 'text/unknown')
37
- parsed.should == "yyz"
38
- serialize(parsed, :content_type => 'text/unknown').should == "xxz"
39
- end
40
- end
@@ -1,320 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__)+'/spec_helper')
2
-
3
- include Restfully
4
-
5
- describe Resource do
6
- before do
7
- @logger = Logger.new(STDOUT)
8
- @uri = URI.parse("http://api.local/x/y/z")
9
- end
10
-
11
- describe "accessors" do
12
- before(:each) do
13
- @resource = Resource.new(@uri, @session=mock("session"))
14
- end
15
- it "should have a reader on the session" do
16
- @resource.should_not respond_to(:session=)
17
- @resource.session.should == @session
18
- end
19
- it "should have a reader on the uri" do
20
- @resource.should_not respond_to(:uri=)
21
- @resource.uri.should == @uri
22
- end
23
- end
24
-
25
- describe "reloading" do
26
- it "should reload the resource" do
27
- resource = Resource.new(@uri, mock('session'))
28
- resource.should_receive(:load).with(:reload => true)
29
- resource.reload
30
- end
31
- end
32
-
33
- describe "loading" do
34
- before do
35
- @session = Restfully::Session.new(:base_uri => "http://api.local")
36
- @raw = {
37
- 'links' => [
38
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
39
- {'rel' => 'parent', 'href' => '/grid5000'},
40
- {'rel' => 'invalid_rel', 'href' => '/whatever'},
41
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/status', 'title' => 'status'},
42
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/versions/123', 'title' => 'version'},
43
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'},
44
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters', 'resolvable' => true, 'resolved' => true, 'title' => 'clusters'},
45
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/environments/versions/123', 'resolvable' => true, 'resolved' => false, 'title' => 'environments'},
46
- {'rel' => 'collection', 'href' => '/has/no/title'}
47
- ],
48
- 'uid' => 'rennes',
49
- 'whatever' => 'whatever',
50
- 'an_array' => [1, 2, 3],
51
- 'clusters' => {
52
- 'paradent' => {
53
- 'uid' => 'paradent',
54
- 'links' => [
55
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes/clusters/paradent'},
56
- {'rel' => 'parent', 'href' => '/grid5000/sites/rennes'},
57
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/nodes', 'title' => 'nodes', 'resolvable' => true, 'resolved' => false},
58
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/versions', 'resolvable' => false, 'title' => 'versions'},
59
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/clusters/paradent/versions/123', 'title' => 'version'},
60
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paradent/status', 'title' => 'status'}
61
- ],
62
- 'model' => 'XYZ'
63
- },
64
- 'paramount' => {
65
- 'uid' => 'paramount',
66
- 'links' => [
67
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes/clusters/paramount'},
68
- {'rel' => 'parent', 'href' => '/grid5000/sites/rennes'},
69
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/nodes', 'title' => 'nodes', 'resolvable' => true, 'resolved' => false},
70
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/versions', 'resolvable' => false, 'title' => 'versions'},
71
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/clusters/paramount/versions/123', 'title' => 'version'},
72
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/clusters/paramount/status', 'title' => 'status'}
73
- ],
74
- 'model' => 'XYZ1b'
75
- }
76
- }
77
- }
78
- @response_200 = Restfully::HTTP::Response.new(200, {'Content-Type' => 'application/json;utf-8', 'Content-Length' => @raw.length}, @raw.to_json)
79
- end
80
-
81
- it "should not be loaded in its initial state" do
82
- resource = Resource.new(@uri, mock('session'))
83
- resource.executed_requests.should == {}
84
- end
85
- it "should get the raw representation of the resource via the session if it doesn't have it" do
86
- resource = Resource.new(@uri, session = mock("session", :logger => Logger.new(STDOUT)))
87
- resource.stub!(:define_link) # do not define links
88
- session.should_receive(:get).with(@uri, {}).and_return(@response_200)
89
- resource.load
90
- end
91
- it "should get the raw representation of the resource via the session if there are query parameters" do
92
- resource = Resource.new(@uri, session = mock("session", :logger => Logger.new(STDOUT)))
93
- resource.stub!(:define_link) # do not define links
94
- session.should_receive(:get).with(@uri, {:query => {:q1 => 'v1'}}).and_return(@response_200)
95
- resource.load(:query => {:q1 => 'v1'})
96
- end
97
- it "should get the raw representation of the resource if forced to do so" do
98
- resource = Resource.new(@uri, session = mock("session", :logger => Logger.new(STDOUT)))
99
- resource.stub!(:define_link) # do not define links
100
- session.should_receive(:get).with(@uri, {}).and_return(@response_200)
101
- resource.load(:reload => true)
102
- end
103
- it "should correctly define the functions to access simple values" do
104
- stub_request(:get, @uri.to_s).to_return(
105
- :status => 200,
106
- :body => @raw.to_json,
107
- :headers => {'Content-Type' => 'application/json', 'Content-Length' => @raw.length}
108
- )
109
- resource = Resource.new(@uri, @session)
110
- resource.stub!(:define_link) # do not define links
111
- resource.load
112
- resource['whatever'].should == 'whatever'
113
- resource.uri.should == @uri
114
- resource["uid"].should == 'rennes'
115
- resource['an_array'].should be_a(SpecialArray)
116
- resource['an_array'].should == [1,2,3]
117
- end
118
-
119
- it "should correctly send custom headers" do
120
- stub_request(:get, @uri.to_s).with(:headers => {
121
- 'User-Agent'=>"Restfully/#{Restfully::VERSION}",
122
- 'Accept-Encoding'=>'gzip, deflate',
123
- 'Accept'=>'application/json'
124
- }).to_return(
125
- :status => 200,
126
- :body => @raw.to_json,
127
- :headers => {'Content-Type' => 'application/json', 'Content-Length' => @raw.length}
128
- )
129
- resource = Resource.new(@uri, @session)
130
- resource.load(:headers => {:accept => 'application/json'})
131
- end
132
-
133
- it "should correctly define a collection link" do
134
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
135
- 'links' => [
136
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
137
- {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'}
138
- ],
139
- 'uid' => 'rennes'
140
- }, :headers => {}), :logger => @logger))
141
- Collection.should_receive(:new).with(@uri.merge('/grid5000/sites/rennes/versions'), session, :title => 'versions').and_return(collection=mock("restfully collection"))
142
- resource.load
143
- resource.links['versions'].should == collection
144
- end
145
- it "should NOT update the URI with the self link" do
146
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
147
- 'links' => [
148
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'}
149
- ],
150
- 'uid' => 'rennes'
151
- }, :headers => {}), :logger => @logger))
152
- resource.uri.should == @uri
153
- resource.load
154
- resource.uri.should == @uri
155
- end
156
- it "should correctly define a member association" do
157
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
158
- 'links' => [
159
- {'rel' => 'member', 'href' => '/grid5000/sites/rennes/versions/123', 'title' => 'version'}
160
- ],
161
- 'uid' => 'rennes'
162
- }, :headers => {}), :logger => @logger))
163
- Resource.should_receive(:new).with(@uri.merge('/grid5000/sites/rennes/versions/123'), session, :title => 'version').and_return(member=mock("restfully resource"))
164
- resource.load
165
- resource.links['version'].should == member
166
- end
167
- it "should correctly define a parent association" do
168
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
169
- 'links' => [
170
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
171
- {'rel' => 'parent', 'href' => '/grid5000'}
172
- ],
173
- 'uid' => 'rennes'
174
- }, :headers => {}), :logger => @logger))
175
- Resource.should_receive(:new).with(@uri.merge('/grid5000'), session).and_return(parent=mock("restfully resource"))
176
- resource.load
177
- resource.links['parent'].should == parent
178
- end
179
- it "should ignore bad links" do
180
- resource = Resource.new(@uri, session = mock("session", :get => mock("restfully response", :body => {
181
- 'links' => [
182
- {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
183
- {'rel' => 'invalid_rel', 'href' => '/whatever'},
184
- {'rel' => 'collection', 'href' => '/has/no/title'}
185
- ],
186
- 'uid' => 'rennes'
187
- }, :headers => {}), :logger => @logger))
188
- resource.load
189
- resource.links.should be_empty
190
- end
191
-
192
- it "should correctly define the functions to access links [integration test]" do
193
- resource = Resource.new(@uri, session = mock("session", :get => @response_200, :logger => @logger))
194
- @logger.should_receive(:warn).with(/collection \/has\/no\/title has no title/)
195
- @logger.should_receive(:warn).with(/invalid_rel is not a valid link relationship/)
196
- resource.load
197
- resource.links.keys.should =~ ['versions', 'clusters', 'environments', 'status', 'parent', 'version']
198
- end
199
-
200
- it "should reload the resource if user forces reload [first loading]" do
201
- resource = Resource.new(@uri, session = mock("session"))
202
- session.should_receive(:get).and_return(response = mock("response", :headers => {}, :body => {}))
203
- resource.load(:reload => true, :body => mock("body"))
204
- end
205
- it "should reload the resource when user forces reload [has been loaded at least once before]" do
206
- resource = Resource.new(@uri, session = mock("session"))
207
- resource.instance_variable_set "@status", :loaded
208
- resource.should_not be_stale
209
- resource.should_receive(:executed_requests).at_least(1).and_return({
210
- 'GET' => {'options' => {:query => {:q=>1}}, 'body' => {'a' => 'b'}}
211
- })
212
- session.should_receive(:get).and_return(response = mock("response", :headers => {}, :body => {}))
213
- resource.load(:reload => true, :query => {:q => 1})
214
- end
215
- end
216
-
217
-
218
- describe "submitting" do
219
- before do
220
- @resource = Resource.new(@uri, @session = mock("session", :logger => @logger))
221
- @resource.stub!(:http_methods).and_return(['GET', 'POST'])
222
- @resource.stub!(:executed_requests).and_return({
223
- 'GET' => {'headers' => {'Content-Type' => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json'}, 'options' => {:query => {:q1 => 'v1'}}}
224
- })
225
- end
226
- describe "setting input body and options" do
227
- before do
228
- @resource.stub!(:reload).and_return(@resource)
229
- @response = mock("http response", :status => 200)
230
- end
231
- it "should raise a NotImplementedError if the resource does not allow POST" do
232
- @resource.should_receive(:http_methods).and_return(['GET'])
233
- lambda{@resource.submit("whatever")}.should raise_error(NotImplementedError, /POST method is not allowed for this resource/)
234
- end
235
- it "should raise an error if the input body is nil" do
236
- lambda{@resource.submit(nil)}.should raise_error(ArgumentError, /You must pass a payload/)
237
- end
238
- it "should pass the body as-is if the given body is a string" do
239
- @session.should_receive(:post).with(@resource.uri, "whatever", :headers => {
240
- :accept => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json',
241
- :content_type => 'application/json'}
242
- ).and_return(@response)
243
- @resource.submit("whatever")
244
- end
245
- it "should also pass the body as-is if the given body is an object" do
246
- body = {:key => 'value'}
247
- @session.should_receive(:post).with(@resource.uri, body, :headers => {
248
- :accept => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json',
249
- :content_type => 'application/json'}).and_return(@response)
250
- @resource.submit(body)
251
- end
252
- it "should set the Content-Type header to the specified mime-type if given" do
253
- @session.should_receive(:post).with(@resource.uri, "whatever", :headers => {
254
- :accept => 'application/vnd.fr.grid5000.api.Job+json;level=1,application/json',
255
- :content_type => 'application/xml'}).and_return(@response)
256
- @resource.submit("whatever", :headers => {:content_type => 'application/xml'})
257
- end
258
- end
259
- [201, 202].each do |status|
260
- it "should return the resource referenced in the Location header after a successful submit (status=#{status})" do
261
- @session.should_receive(:post).and_return(response = mock("http response", :status => status, :headers => {'Location' => '/path/to/new/resource'}))
262
- @resource.should_receive(:uri_for).with('/path/to/new/resource').and_return(new_resource_uri = mock("uri"))
263
- Resource.should_receive(:new).with(new_resource_uri, @session).and_return(new_resource = mock("resource"))
264
- new_resource.should_receive(:load).and_return(new_resource)
265
- @resource.submit("whatever").should == new_resource
266
- end
267
- end
268
- it "should reload the resource if the response status is 2xx (and not 201 or 202)" do
269
- @session.should_receive(:post).and_return(response = mock("http response", :status => 200, :headers => {'Location' => '/path/to/new/resource'}))
270
- @resource.should_receive(:reload).and_return(@resource)
271
- @resource.submit("whatever").should == @resource
272
- end
273
- end
274
-
275
- describe "deleting" do
276
- before do
277
- @resource = Resource.new(@uri, @session = mock("session", :logger => @logger))
278
- @resource.stub!(:http_methods).and_return(['GET', 'DELETE'])
279
- end
280
- it "should raise an error if the DELETE method is not supported by the service" do
281
- @resource.stub!(:http_methods).and_return(['GET'])
282
- lambda{@resource.delete}.should raise_error(NotImplementedError, /DELETE method is not allowed/)
283
- end
284
- it "should send a DELETE request to the resource URI" do
285
- @session.should_receive(:delete).with(@uri, :query => {:q => 'v'}, :headers => {'Accept' => 'application/json'}).and_return(response = mock("http response", :status => 204))
286
- @resource.delete(:query => {:q => 'v'}, :headers => {'Accept' => 'application/json'}).should be_true
287
- end
288
- end
289
-
290
- describe "supported http methodes" do
291
- before do
292
- @resource = Resource.new(@uri, @session = mock("session", :logger => @logger))
293
- end
294
- it "should reload if a GET request has not been executed before" do
295
- @resource.should_receive(:executed_requests).ordered.and_return({})
296
- @resource.should_receive(:reload).ordered
297
- @resource.should_receive(:executed_requests).ordered.and_return({
298
- 'GET' => {'body' => "xxx", "headers" => {'Allow' => 'POST, PUT'}}
299
- })
300
- @resource.http_methods.should == ['POST','PUT']
301
- end
302
- it "should reload if the headers are empty" do
303
- @resource.should_receive(:executed_requests).exactly(3).ordered.and_return({
304
- 'GET' => {'body' => "xxx", "headers" => {}}
305
- })
306
- @resource.should_receive(:reload).ordered
307
- @resource.should_receive(:executed_requests).once.ordered.and_return({
308
- 'GET' => {'body' => "xxx", "headers" => {'Allow' => 'POST, PUT'}}
309
- })
310
- @resource.http_methods.should == ['POST','PUT']
311
- end
312
- it "should not reload if the headers are not empty" do
313
- @resource.should_receive(:executed_requests).exactly(4).and_return({
314
- 'GET' => {'body' => "xxx", "headers" => {'Allow' => 'POST, PUT'}}
315
- })
316
- @resource.should_not_receive(:reload)
317
- @resource.http_methods.should == ['POST','PUT']
318
- end
319
- end
320
- end