angus-remote 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/lib/angus-remote.rb +4 -0
  2. data/lib/angus/remote/builder.rb +204 -0
  3. data/lib/angus/remote/client.rb +79 -0
  4. data/lib/angus/remote/exceptions.rb +50 -0
  5. data/lib/angus/remote/http/multipart.rb +54 -0
  6. data/lib/angus/remote/http/multipart_methods/multipart_base.rb +36 -0
  7. data/lib/angus/remote/http/multipart_methods/multipart_post.rb +11 -0
  8. data/lib/angus/remote/http/multipart_methods/multipart_put.rb +11 -0
  9. data/lib/angus/remote/http/query_params.rb +53 -0
  10. data/lib/angus/remote/message.rb +14 -0
  11. data/lib/angus/remote/proxy_client.rb +58 -0
  12. data/lib/angus/remote/proxy_client_utils.rb +70 -0
  13. data/lib/angus/remote/remote_response.rb +44 -0
  14. data/lib/angus/remote/representation.rb +18 -0
  15. data/lib/angus/remote/response/builder.rb +308 -0
  16. data/lib/angus/remote/response/hash.rb +47 -0
  17. data/lib/angus/remote/response/serializer.rb +43 -0
  18. data/lib/angus/remote/service_directory.rb +217 -0
  19. data/lib/angus/remote/utils.rb +119 -0
  20. data/lib/angus/remote/version.rb +4 -2
  21. data/lib/angus/unmarshalling.rb +33 -0
  22. data/spec/angus/remote/builder_spec.rb +105 -0
  23. data/spec/angus/remote/client_spec.rb +75 -0
  24. data/spec/angus/remote/http/multipart_methods/multipart_base_spec.rb +36 -0
  25. data/spec/angus/remote/http/multipart_spec.rb +120 -0
  26. data/spec/angus/remote/http/query_params_spec.rb +28 -0
  27. data/spec/angus/remote/proxy_client_utils_spec.rb +102 -0
  28. data/spec/angus/remote/response/builder_spec.rb +69 -0
  29. data/spec/angus/remote/service_directory_spec.rb +76 -0
  30. data/spec/angus/remote/utils_spec.rb +204 -0
  31. metadata +192 -32
  32. data/.gitignore +0 -17
  33. data/Gemfile +0 -4
  34. data/LICENSE.txt +0 -22
  35. data/README.md +0 -29
  36. data/Rakefile +0 -1
  37. data/angus-remote.gemspec +0 -23
  38. data/lib/angus/remote.rb +0 -7
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ require 'angus/remote/client'
4
+ require 'angus/remote/builder'
5
+
6
+ describe Angus::Remote::Builder do
7
+
8
+ subject(:builder) { Angus::Remote::Builder }
9
+
10
+ describe '.build' do
11
+
12
+ let(:code_name) { 'vpos' }
13
+ let(:operation) { double(:operation, :code_name=> 'get_users', :service_name => 'vpos', :path => '/users', :method => :get) }
14
+ let(:proxy_operation) { double(:proxy_operation, :code_name=> 'get_users_proxy', :service_name => 'vpos', :path => '/users', :method => :get) }
15
+ let(:glossary) { double(:glossary, :terms_hash_with_long_names => {}) }
16
+ let(:service_definition) { double(:vpos, :name => 'Vpos', :operations => { 'users' => [operation] },
17
+ :proxy_operations => { 'users' => [proxy_operation] }, :version => '0.1', :glossary => glossary) }
18
+
19
+ let(:api_url) { 'http://localhost:8085/vpos/api/0.1/' }
20
+
21
+ describe 'the returned class' do
22
+
23
+ subject(:client) { builder.build(code_name, service_definition, api_url) }
24
+
25
+ it 'is of class Angus::Remote::Client' do
26
+ should be_kind_of(Angus::Remote::Client)
27
+ end
28
+
29
+ it 'responds to the defined operation' do
30
+ should respond_to(:get_users)
31
+ end
32
+
33
+ describe 'the generated operation' do
34
+
35
+ let(:response) { double(:response, :code => 200, :body => JSON({ :status => 'success' })) }
36
+
37
+ let(:service_configuration) {
38
+ {
39
+ 'v0.1' => { 'doc_url' => 'some_url/doc', 'api_url' => 'some_url/api' }
40
+ }
41
+ }
42
+
43
+ let(:service_def) {
44
+ {
45
+ 'service' => { 'service' => 'vpos' }, 'code_name' => 'vpos', 'version' => '0.1',
46
+ 'operations' => { 'users' => { 'get_users' => { 'name' => 'Obtener usuarios'} } }
47
+ }
48
+
49
+ }
50
+
51
+ before do
52
+ Angus::Remote::ServiceDirectory.stub(:service_configuration => service_configuration)
53
+ Angus::Remote::ServiceDirectory.stub(:fetch_remote_service_definition => service_def)
54
+ end
55
+
56
+ it 'makes a request to the remote service' do
57
+ client.should_receive(:make_request).and_return(response)
58
+
59
+ client.get_users
60
+ end
61
+
62
+ describe 'the generated proxy operation' do
63
+
64
+ before do
65
+ Angus::Remote::ServiceDirectory.stub(:service_configuration => { 'v0.1' => { 'doc_url' => 'some_url/doc', 'api_url' => 'some_url/api' } })
66
+ Angus::Remote::ServiceDirectory.stub(:fetch_remote_service_definition => { 'service' => { 'service' => 'vpos' }, 'code_name' => 'vpos', 'version' => '0.1', 'operations' => { 'users' => { 'get_users_proxy' => { 'name' => 'Obtener usuarios' } } } })
67
+ end
68
+
69
+ it 'makes a request to the remote service' do
70
+ client.should_receive(:make_request).and_return(response)
71
+
72
+ client.get_users_proxy
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ describe '.build_client_class' do
84
+
85
+ let(:name) { 'Foo' }
86
+ let(:url) { 'http://bar' }
87
+
88
+ it 'returns a client class' do
89
+ client_class = builder.build_client_class(name)
90
+
91
+ client = client_class.new(url)
92
+
93
+ client.should be_kind_of(Angus::Remote::Client)
94
+ end
95
+
96
+ describe 'the returned class' do
97
+ subject { builder.build_client_class(name) }
98
+
99
+ its(:name) { should include(name) }
100
+ its(:to_s) { should include(name) }
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ require 'angus/remote/client'
4
+
5
+ describe Angus::Remote::Client do
6
+
7
+ let(:url) { 'http://bar' }
8
+ subject(:client) { Angus::Remote::Client.new(url) }
9
+
10
+ describe '#to_s' do
11
+
12
+ it 'returns the class name and the object id' do
13
+ client.to_s.should eq("#<#{client.class}:#{client.object_id}>")
14
+ end
15
+
16
+ end
17
+
18
+ describe '#make_request' do
19
+
20
+ it 'returns the remote service response' do
21
+ response = double(:response, :code => 200, :body => '[]')
22
+ PersistentHTTP.any_instance.stub(:request => response)
23
+
24
+ client.make_request('/users', 'get', false, [], {}).should eq(response)
25
+ end
26
+
27
+ context 'when an invalid method is used' do
28
+ it 'raises MethodArgumentError' do
29
+ expect {
30
+ client.make_request('/', 'INVALID_METHOD', false, [], {})
31
+ }.to raise_error(Angus::Remote::MethodArgumentError)
32
+ end
33
+ end
34
+
35
+ context 'when less path_params that expected' do
36
+ it 'raises PathArgumentError' do
37
+ expect {
38
+ client.make_request('/a/:b/c/:d', 'get', false, [], {})
39
+ }.to raise_error(Angus::Remote::PathArgumentError)
40
+ end
41
+ end
42
+
43
+ context 'when more path_params that expected' do
44
+ it 'raises PathArgumentError' do
45
+ expect {
46
+ client.make_request('/a/:b/c/:d', 'get', false, [1, 2, 3], {})
47
+ }.to raise_error(Angus::Remote::PathArgumentError)
48
+ end
49
+ end
50
+
51
+ context 'when the remote service returns a severe error response' do
52
+ let(:error_response) { double(:error_response, :code => 500, :body => '') }
53
+
54
+ before { PersistentHTTP.any_instance.stub(:request => error_response) }
55
+
56
+ it 'raises RemoteSevereError' do
57
+ expect {
58
+ client.make_request('/users', 'get', false, [], {})
59
+ }.to raise_error(Angus::Remote::RemoteSevereError)
60
+ end
61
+ end
62
+
63
+ context 'when the remote service rejects the connection' do
64
+ before { PersistentHTTP.any_instance.stub(:request).and_raise(Errno::ECONNREFUSED) }
65
+
66
+ it 'raises RemoteConnectionError' do
67
+ expect {
68
+ client.make_request('/users', 'get', false, [], {})
69
+ }.to raise_error(Angus::Remote::RemoteConnectionError)
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ require 'angus/remote/http/multipart_methods/multipart_base'
4
+
5
+ describe Http::MultipartMethods::MultipartBase do
6
+
7
+ let :request_class do
8
+ Class.new(Net::HTTP::Post) do
9
+ include Http::MultipartMethods::MultipartBase
10
+ end
11
+ end
12
+
13
+ context 'headers set by .body= are retained if .initialize_http_header is called afterwards' do
14
+ def request_with_headers(headers)
15
+ request_class.new('/path').tap do |request|
16
+ request.body = { :some => :var }
17
+ request.initialize_http_header(headers)
18
+ end
19
+ end
20
+
21
+ context 'with a header' do
22
+ subject { request_with_headers({'a' => 'header'}).to_hash }
23
+
24
+ it { should include('content-length') }
25
+ it { should include('a') }
26
+ end
27
+
28
+ context 'without a header' do
29
+ subject { request_with_headers(nil).to_hash }
30
+
31
+ it { should include('content-length') }
32
+ it { should_not include('a') }
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ require 'fakefs/spec_helpers'
4
+
5
+ require 'angus/remote/http/multipart'
6
+
7
+ describe Http::Multipart do
8
+
9
+ include FakeFS::SpecHelpers
10
+
11
+ let(:file_name) { 'some_file.txt' }
12
+
13
+ let(:file) { File.new(file_name) }
14
+ let(:tempfile) { Tempfile.new('tempfile', temp_dir) }
15
+ let(:temp_dir) { '/tmp' }
16
+ let(:some_upload_io) { UploadIO.new(file, 'application/octet-stream') }
17
+
18
+ before do
19
+ Dir.mkdir('/tmp')
20
+
21
+ File.new(file_name, 'w')
22
+ end
23
+
24
+ describe '.hash_contains_files?' do
25
+
26
+ it 'should return true if one of the values in the passed hash is a file' do
27
+ Http::Multipart.hash_contains_files?({:a => 1, :file => file}).should be_true
28
+ end
29
+
30
+ it 'should return true if one of the values in the passed hash is an upload io' do
31
+ Http::Multipart.hash_contains_files?({:a => 1, :file => some_upload_io}).should be_true
32
+ end
33
+
34
+ it 'should return true if one of the values in the passed hash is a tempfile' do
35
+ Http::Multipart.hash_contains_files?({:a => 1, :file => tempfile}).should be_true
36
+ end
37
+
38
+ it 'should return false if none of the values in the passed hash is a file' do
39
+ Http::Multipart.hash_contains_files?({:a => 1, :b => 'nope'}).should be_false
40
+ end
41
+
42
+ it 'should return true if passed hash includes an a array of files' do
43
+ Http::Multipart.hash_contains_files?({:files => [file, file]}).should be_true
44
+ end
45
+
46
+ end
47
+
48
+
49
+ describe '.file_to_upload_io' do
50
+
51
+ it 'should get the physical name of a file' do
52
+ Http::Multipart.file_to_upload_io(file).original_filename.should == file_name
53
+ end
54
+
55
+ it 'should get the physical name of a file' do
56
+ # Let's pretend this is a file upload to a rack app.
57
+ tempfile.stub(:original_filename => 'stuff.txt')
58
+
59
+ Http::Multipart.file_to_upload_io(tempfile).original_filename.should == 'stuff.txt'
60
+ end
61
+
62
+ end
63
+
64
+ describe '.flatten_params' do
65
+
66
+ it 'should handle complex hashs' do
67
+ Http::Multipart.flatten_params({
68
+ :foo => 'bar',
69
+ :deep => {
70
+ :deeper => 1,
71
+ :deeper2 => 2,
72
+ :deeparray => [1,2,3],
73
+ :deephasharray => [
74
+ {:id => 1},
75
+ {:id => 2}
76
+ ]
77
+ }
78
+ }).sort_by(&:join).should == [
79
+ ['foo', 'bar'],
80
+ ['deep[deeper]', 1],
81
+ ['deep[deeper2]', 2],
82
+ ['deep[deeparray][]', 1],
83
+ ['deep[deeparray][]', 2],
84
+ ['deep[deeparray][]', 3],
85
+ ['deep[deephasharray][][id]', 1],
86
+ ['deep[deephasharray][][id]', 2],
87
+ ].sort_by(&:join)
88
+ end
89
+
90
+ end
91
+
92
+ describe '::QUERY_STRING_NORMALIZER' do
93
+
94
+ subject { Http::Multipart::QUERY_STRING_NORMALIZER }
95
+
96
+ it 'should map a file to UploadIO' do
97
+ (first_k, first_v) = subject.call({
98
+ :file => file
99
+ }).first
100
+
101
+ first_v.should be_an UploadIO
102
+ end
103
+
104
+ it 'should map a Tempfile to UploadIO' do
105
+ (first_k, first_v) = subject.call({
106
+ :file => tempfile
107
+ }).first
108
+
109
+ first_v.should be_an UploadIO
110
+ end
111
+
112
+ it 'should map an array of files to UploadIOs' do
113
+ subject.call({
114
+ :file => [file, tempfile]
115
+ }).each { |(k,v)| v.should be_an UploadIO }
116
+ end
117
+
118
+ end
119
+
120
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ require 'angus/remote/http/query_params'
4
+
5
+ describe Http::QueryParams do
6
+
7
+ describe '.to_params' do
8
+
9
+ let(:params) {
10
+ { :name => 'Bob',
11
+ :address => {
12
+ :phones => %w[111-111-1111 222-222-2222],
13
+ :street => '111 Ruby Ave.',
14
+ :zone => {
15
+ :country => 'Ruby',
16
+ :city => 'Gem Central',
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ it 'returns the expected string' do
23
+ Http::QueryParams.to_params(params).should eq('name=Bob&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111%20Ruby%20Ave.&address[zone][country]=Ruby&address[zone][city]=Gem%20Central')
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,102 @@
1
+ require 'net/http'
2
+
3
+ require 'json'
4
+
5
+ require 'angus/remote/proxy_client_utils'
6
+
7
+ describe Angus::Remote::ProxyClientUtils do
8
+
9
+ subject(:utils) { Angus::Remote::ProxyClientUtils }
10
+
11
+ describe '.build_request' do
12
+
13
+ let(:path) { '/' }
14
+ let(:query) { 'q=listing' }
15
+
16
+ shared_examples 'a request builder' do |method, kind_of|
17
+ context "when #{method}" do
18
+ it "returns a kind_of #{kind_of}" do
19
+ res = utils.build_request(method, path, query)
20
+
21
+ res.should be_a(kind_of)
22
+ end
23
+ end
24
+ end
25
+
26
+ it_behaves_like 'a request builder', :get, Net::HTTP::Get
27
+ it_behaves_like 'a request builder', :post, Net::HTTP::Post
28
+ it_behaves_like 'a request builder', :put, Net::HTTP::Put
29
+ it_behaves_like 'a request builder', :delete, Net::HTTP::Delete
30
+
31
+ context 'with headers' do
32
+ it 'sets the to the request' do
33
+ headers = { 'a' => 'A', 'b' => 'B' }
34
+
35
+ res = utils.build_request(:get, path, query, headers)
36
+
37
+ res['a'].should eq('A')
38
+ res['b'].should eq('B')
39
+ end
40
+ end
41
+
42
+ context 'with body' do
43
+ it 'sets the body to the request' do
44
+ body = 'BODY'
45
+
46
+ res = utils.build_request(:get, path, query, {}, body)
47
+
48
+ res.body.should eq(body)
49
+ end
50
+ end
51
+
52
+ context 'when invalid http method' do
53
+ it 'raises MethodArgumentError' do
54
+ expect {
55
+ utils.build_request(:invalid, path, query)
56
+ }.to raise_error(Angus::Remote::MethodArgumentError)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '.filter_response_headers' do
62
+
63
+ it 'rejects non allowed headers' do
64
+ headers = {:not_allowed => 'header'}
65
+
66
+ res = utils.filter_response_headers(headers)
67
+
68
+ res.should_not include(:not_allowed)
69
+ end
70
+
71
+ it 'does not reject allowed headers' do
72
+ headers = {'content-type' => 'header'}
73
+
74
+ res = utils.filter_response_headers(headers)
75
+
76
+ res.should include('content-type')
77
+ end
78
+ end
79
+
80
+ describe '.normalize_headers' do
81
+ context 'when a header value is an array' do
82
+ it 'takes the first array element' do
83
+ headers = {'content-type' => ['application/json', 'image/gif']}
84
+
85
+ res = utils.normalize_headers(headers)
86
+
87
+ res.should include('content-type' => 'application/json')
88
+ end
89
+ end
90
+
91
+ context 'when simple headers' do
92
+ it 'does not affect anything' do
93
+ headers = {'content-type' => 'application/json'}
94
+
95
+ res = utils.normalize_headers(headers)
96
+
97
+ res.should include('content-type' => 'application/json')
98
+ end
99
+ end
100
+ end
101
+
102
+ end