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