restfully 0.6.3 → 0.7.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +166 -0
- data/Rakefile +35 -35
- data/bin/restfully +68 -10
- data/lib/restfully.rb +8 -14
- data/lib/restfully/collection.rb +70 -90
- data/lib/restfully/error.rb +2 -0
- data/lib/restfully/http.rb +3 -3
- data/lib/restfully/http/error.rb +1 -20
- data/lib/restfully/http/helper.rb +49 -0
- data/lib/restfully/http/request.rb +60 -24
- data/lib/restfully/http/response.rb +55 -24
- data/lib/restfully/link.rb +32 -24
- data/lib/restfully/media_type.rb +70 -0
- data/lib/restfully/media_type/abstract_media_type.rb +162 -0
- data/lib/restfully/media_type/application_json.rb +21 -0
- data/lib/restfully/media_type/application_vnd_bonfire_xml.rb +177 -0
- data/lib/restfully/media_type/application_x_www_form_urlencoded.rb +33 -0
- data/lib/restfully/media_type/grid5000.rb +67 -0
- data/lib/restfully/media_type/wildcard.rb +27 -0
- data/lib/restfully/rack.rb +1 -0
- data/lib/restfully/rack/basic_auth.rb +26 -0
- data/lib/restfully/resource.rb +134 -197
- data/lib/restfully/session.rb +127 -70
- data/lib/restfully/version.rb +3 -0
- data/spec/fixtures/bonfire-collection-with-fragments.xml +6 -0
- data/spec/fixtures/bonfire-compute-existing.xml +43 -0
- data/spec/fixtures/bonfire-empty-collection.xml +4 -0
- data/spec/fixtures/bonfire-experiment-collection.xml +51 -0
- data/spec/fixtures/bonfire-network-collection.xml +35 -0
- data/spec/fixtures/bonfire-network-existing.xml +6 -0
- data/spec/fixtures/bonfire-root.xml +5 -0
- data/spec/fixtures/grid5000-rennes-jobs.json +988 -146
- data/spec/fixtures/grid5000-rennes.json +63 -0
- data/spec/restfully/collection_spec.rb +87 -0
- data/spec/restfully/http/helper_spec.rb +18 -0
- data/spec/restfully/http/request_spec.rb +97 -0
- data/spec/restfully/http/response_spec.rb +53 -0
- data/spec/restfully/link_spec.rb +80 -0
- data/spec/restfully/media_type/application_vnd_bonfire_xml_spec.rb +153 -0
- data/spec/restfully/media_type_spec.rb +117 -0
- data/spec/restfully/resource_spec.rb +109 -0
- data/spec/restfully/session_spec.rb +229 -0
- data/spec/spec_helper.rb +10 -9
- metadata +162 -83
- data/.document +0 -5
- data/CHANGELOG +0 -62
- data/README.rdoc +0 -146
- data/TODO.rdoc +0 -3
- data/VERSION +0 -1
- data/examples/grid5000.rb +0 -33
- data/examples/scratch.rb +0 -37
- data/lib/restfully/extensions.rb +0 -34
- data/lib/restfully/http/adapters/abstract_adapter.rb +0 -29
- data/lib/restfully/http/adapters/patron_adapter.rb +0 -16
- data/lib/restfully/http/adapters/rest_client_adapter.rb +0 -75
- data/lib/restfully/http/headers.rb +0 -20
- data/lib/restfully/parsing.rb +0 -66
- data/lib/restfully/special_array.rb +0 -5
- data/lib/restfully/special_hash.rb +0 -5
- data/restfully.gemspec +0 -114
- data/spec/collection_spec.rb +0 -120
- data/spec/fixtures/configuration_file.yml +0 -4
- data/spec/fixtures/grid5000-sites.json +0 -540
- data/spec/http/error_spec.rb +0 -18
- data/spec/http/headers_spec.rb +0 -17
- data/spec/http/request_spec.rb +0 -49
- data/spec/http/response_spec.rb +0 -19
- data/spec/http/rest_client_adapter_spec.rb +0 -35
- data/spec/link_spec.rb +0 -61
- data/spec/parsing_spec.rb +0 -40
- data/spec/resource_spec.rb +0 -320
- data/spec/restfully_spec.rb +0 -16
- data/spec/session_spec.rb +0 -171
data/spec/http/error_spec.rb
DELETED
@@ -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
|
data/spec/http/headers_spec.rb
DELETED
@@ -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
|
data/spec/http/request_spec.rb
DELETED
@@ -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
|
data/spec/http/response_spec.rb
DELETED
@@ -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
|
data/spec/resource_spec.rb
DELETED
@@ -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
|