angus-remote 0.0.1 → 0.0.2

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