vra-restapi 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module Vra
20
+ class Resources
21
+ attr_reader :client
22
+
23
+ def initialize(client)
24
+ @client = client
25
+ end
26
+
27
+ def all_resources
28
+ resources = []
29
+
30
+ items = client.http_get_paginated_array!('/catalog-service/api/consumer/resources')
31
+ items.each do |item|
32
+ resources << Vra::Resource.new(client, data: item)
33
+ end
34
+
35
+ resources
36
+ end
37
+
38
+ def by_id(id)
39
+ Vra::Resource.new(client, id: id)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module Vra
20
+ VERSION = '1.5.2'.freeze
21
+ end
@@ -0,0 +1,98 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::CatalogRequest do
22
+ let(:client) do
23
+ Vra::Client.new(username: 'user@corp.local',
24
+ password: 'password',
25
+ tenant: 'tenant',
26
+ base_url: 'https://vra.corp.local')
27
+ end
28
+
29
+ let(:catalog_id) { '9e98042e-5443-4082-afd5-ab5a32939bbc' }
30
+
31
+ let(:catalog_item_payload) do
32
+ {
33
+ '@type' => 'CatalogItem',
34
+ 'id' => '9e98042e-5443-4082-afd5-ab5a32939bbc',
35
+ 'version' => 2,
36
+ 'name' => 'CentOS 6.6',
37
+ 'description' => 'Blueprint for deploying a CentOS Linux development server',
38
+ 'status' => 'PUBLISHED',
39
+ 'statusName' => 'Published',
40
+ 'organization' => {
41
+ 'tenantRef' => 'vsphere.local',
42
+ 'tenantLabel' => 'vsphere.local',
43
+ 'subtenantRef' => '962ab3f9-858c-4483-a49f-fa97392c314b',
44
+ 'subtenantLabel' => 'catalog_subtenant'
45
+ },
46
+ 'providerBinding' => {
47
+ 'bindingId' => '33af5413-4f20-4b3b-8268-32edad434dfb',
48
+ 'providerRef' => {
49
+ 'id' => 'c3b2bc30-47b0-454f-b57d-df02a7356fe6',
50
+ 'label' => 'iaas-service'
51
+ }
52
+ }
53
+ }
54
+ end
55
+
56
+ describe '#initialize' do
57
+ it 'raises an error if no ID or catalog item data have been provided' do
58
+ expect { Vra::CatalogItem.new }.to raise_error(ArgumentError)
59
+ end
60
+
61
+ it 'raises an error if an ID and catalog item data have both been provided' do
62
+ expect { Vra::CatalogItem.new(id: 123, data: 'foo') }.to raise_error(ArgumentError)
63
+ end
64
+
65
+ context 'when an ID is provided' do
66
+ it 'fetches the catalog_item record' do
67
+ catalog_item = Vra::CatalogItem.allocate
68
+ expect(catalog_item).to receive(:fetch_catalog_item)
69
+ catalog_item.send(:initialize, client, id: catalog_id)
70
+ end
71
+ end
72
+
73
+ context 'when catalog item data is provided' do
74
+ it 'populates the ID correctly' do
75
+ catalog_item = Vra::Resource.new(client, data: catalog_item_payload)
76
+ expect(catalog_item.id).to eq catalog_id
77
+ end
78
+ end
79
+ end
80
+
81
+ describe '#fetch_catalog_item' do
82
+ context 'when the catalog item exists' do
83
+ let(:response) { double('response', code: 200, body: catalog_item_payload.to_json) }
84
+
85
+ it 'calls http_get against the catalog_service' do
86
+ expect(client).to receive(:http_get).with('/catalog-service/api/consumer/catalogItems/catalog-12345').and_return(response)
87
+ Vra::CatalogItem.new(client, id: 'catalog-12345')
88
+ end
89
+ end
90
+
91
+ context 'when the catalog item does not exist' do
92
+ it 'raises an exception' do
93
+ allow(client).to receive(:http_get).with('/catalog-service/api/consumer/catalogItems/catalog-12345').and_raise(Vra::Exception::HTTPNotFound)
94
+ expect { Vra::CatalogItem.new(client, id: 'catalog-12345') }.to raise_error(Vra::Exception::NotFound)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::CatalogRequest do
22
+ before(:each) do
23
+ catalog_item = double('catalog_item')
24
+ allow(catalog_item).to receive(:blueprint_id).and_return('catalog_blueprint')
25
+ allow(catalog_item).to receive(:tenant_id).and_return('catalog_tenant')
26
+ allow(catalog_item).to receive(:subtenant_id).and_return('catalog_subtenant')
27
+ allow(Vra::CatalogItem).to receive(:new).and_return(catalog_item)
28
+ end
29
+
30
+ let(:client) do
31
+ Vra::Client.new(username: 'user@corp.local',
32
+ password: 'password',
33
+ tenant: 'tenant',
34
+ base_url: 'https://vra.corp.local')
35
+ end
36
+
37
+ context 'when no subtenant ID is provided' do
38
+ let(:request) do
39
+ client.catalog.request('catalog-12345',
40
+ cpus: 2,
41
+ memory: 1024,
42
+ lease_days: 15,
43
+ requested_for: 'tester@corp.local',
44
+ notes: 'test notes')
45
+ end
46
+
47
+ it 'uses the subtenant ID from the catalog item' do
48
+ expect(request.subtenant_id).to eq 'catalog_subtenant'
49
+ end
50
+ end
51
+
52
+ context 'when subtenant is provided, and all shared tests' do
53
+ let(:request) do
54
+ client.catalog.request('catalog-12345',
55
+ cpus: 2,
56
+ memory: 1024,
57
+ lease_days: 15,
58
+ requested_for: 'tester@corp.local',
59
+ notes: 'test notes',
60
+ subtenant_id: 'user_subtenant')
61
+ end
62
+
63
+ describe '#initialize' do
64
+ it 'sets the appropriate instance vars' do
65
+ expect(request.catalog_id).to eq 'catalog-12345'
66
+ expect(request.cpus).to eq 2
67
+ expect(request.memory).to eq 1024
68
+ expect(request.lease_days).to eq 15
69
+ expect(request.requested_for).to eq 'tester@corp.local'
70
+ expect(request.notes).to eq 'test notes'
71
+ expect(request.subtenant_id).to eq 'user_subtenant'
72
+ end
73
+ end
74
+
75
+ describe '#validate_params!' do
76
+ context 'when all required params are provided' do
77
+ it 'does not raise an exception' do
78
+ expect { request.validate_params! }.to_not raise_error
79
+ end
80
+ end
81
+
82
+ context 'when a required parameter is not provided' do
83
+ it 'raises an exception' do
84
+ request.cpus = nil
85
+ expect { request.validate_params! }.to raise_error(ArgumentError)
86
+ end
87
+ end
88
+ end
89
+
90
+ describe '#request_payload' do
91
+ it 'properly handles additional parameters' do
92
+ request.set_parameter('param1', 'string', 'my string')
93
+ request.set_parameter('param2', 'integer', '2468')
94
+
95
+ payload = request.request_payload
96
+ param1 = payload['requestData']['entries'].find { |x| x['key'] == 'param1' }
97
+ param2 = payload['requestData']['entries'].find { |x| x['key'] == 'param2' }
98
+
99
+ expect(param1).to be_a(Hash)
100
+ expect(param2).to be_a(Hash)
101
+ expect(param1['value']['value']).to eq 'my string'
102
+ expect(param2['value']['value']).to eq 2468
103
+ end
104
+ end
105
+
106
+ describe '#submit' do
107
+ before do
108
+ allow(request).to receive(:request_payload).and_return({})
109
+ response = double('response', code: 200, headers: { location: '/requests/request-12345' })
110
+ allow(client).to receive(:http_post).with('/catalog-service/api/consumer/requests', '{}').and_return(response)
111
+ end
112
+
113
+ it 'calls http_post' do
114
+ expect(client).to receive(:http_post).with('/catalog-service/api/consumer/requests', '{}')
115
+
116
+ request.submit
117
+ end
118
+
119
+ it 'returns a Vra::Request object' do
120
+ expect(request.submit).to be_an_instance_of(Vra::Request)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::Catalog do
22
+ let(:client) do
23
+ Vra::Client.new(username: 'user@corp.local',
24
+ password: 'password',
25
+ tenant: 'tenant',
26
+ base_url: 'https://vra.corp.local')
27
+ end
28
+
29
+ let(:catalog_item) do
30
+ {
31
+ '@type' => 'CatalogItem',
32
+ 'id' => 'a9cd6148-6e0b-4a80-ac47-f5255c52b43d',
33
+ 'version' => 2,
34
+ 'name' => 'CentOS 6.6',
35
+ 'description' => 'Blueprint for deploying a CentOS Linux development server',
36
+ 'status' => 'PUBLISHED',
37
+ 'statusName' => 'Published',
38
+ 'organization' => {
39
+ 'tenantRef' => 'vsphere.local',
40
+ 'tenantLabel' => 'vsphere.local',
41
+ 'subtenantRef' => nil,
42
+ 'subtenantLabel' => nil
43
+ },
44
+ 'providerBinding' => {
45
+ 'bindingId' => '33af5413-4f20-4b3b-8268-32edad434dfb',
46
+ 'providerRef' => {
47
+ 'id' => 'c3b2bc30-47b0-454f-b57d-df02a7356fe6',
48
+ 'label' => 'iaas-service'
49
+ }
50
+ }
51
+ }
52
+ end
53
+
54
+ let(:entitled_catalog_item) do
55
+ {
56
+ '@type' => 'ConsumerEntitledCatalogItem',
57
+ 'catalogItem' => {
58
+ 'id' => 'd29efd6b-3cd6-4f8d-b1d8-da4ddd4e52b1',
59
+ 'version' => 2,
60
+ 'name' => 'WindowsServer2012',
61
+ 'description' => 'Windows Server 2012 with the latest updates and patches.',
62
+ 'status' => 'PUBLISHED',
63
+ 'statusName' => 'Published',
64
+ 'organization' => {
65
+ 'tenantRef' => 'vsphere.local',
66
+ 'tenantLabel' => 'vsphere.local',
67
+ 'subtenantRef' => nil,
68
+ 'subtenantLabel' => nil
69
+ },
70
+ 'providerBinding' => {
71
+ 'bindingId' => '59fd02a1-acca-4918-9d3d-2298d310caef',
72
+ 'providerRef' => {
73
+ 'id' => 'c3b2bc30-47b0-454f-b57d-df02a7356fe6',
74
+ 'label' => 'iaas-service'
75
+ }
76
+ }
77
+ }
78
+ }
79
+ end
80
+
81
+ describe '#all_items' do
82
+ it 'calls the catalogItems endpoint' do
83
+ expect(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/catalogItems')
84
+ .and_return([ catalog_item ])
85
+
86
+ client.catalog.all_items
87
+ end
88
+
89
+ it 'returns a Vra::CatalogItem object' do
90
+ allow(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/catalogItems')
91
+ .and_return([ catalog_item ])
92
+
93
+ items = client.catalog.all_items
94
+
95
+ expect(items.first).to be_an_instance_of(Vra::CatalogItem)
96
+ end
97
+ end
98
+
99
+ describe '#entitled_items' do
100
+ it 'calls the entitledCatalogItems endpoint' do
101
+ expect(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/entitledCatalogItems')
102
+ .and_return([ entitled_catalog_item ])
103
+
104
+ client.catalog.entitled_items
105
+ end
106
+
107
+ it 'returns a Vra::CatalogItem object' do
108
+ allow(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/entitledCatalogItems')
109
+ .and_return([ entitled_catalog_item ])
110
+
111
+ items = client.catalog.entitled_items
112
+
113
+ expect(items.first).to be_an_instance_of(Vra::CatalogItem)
114
+ end
115
+ end
116
+
117
+ describe '#request' do
118
+ it 'returns a new Vra::CatalogRequest object' do
119
+ allow(Vra::CatalogItem).to receive(:new)
120
+ request = client.catalog.request('blueprint-1', cpus: 2)
121
+ expect(request).to be_an_instance_of(Vra::CatalogRequest)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,481 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::Client do
22
+ let(:client) do
23
+ Vra::Client.new(username: 'user@corp.local',
24
+ password: 'password',
25
+ tenant: 'tenant',
26
+ base_url: 'https://vra.corp.local')
27
+ end
28
+
29
+ describe '#initialize' do
30
+ it 'calls validate_client_options!' do
31
+ client = Vra::Client.allocate
32
+ expect(client).to receive(:validate_client_options!)
33
+ client.send(:initialize, username: 'user@corp.local',
34
+ password: 'password',
35
+ tenant: 'tenant',
36
+ base_url: 'https://vra.corp.local')
37
+ end
38
+ end
39
+
40
+ describe '#bearer_token_request_body' do
41
+ it 'gets the correct password from the PasswordMasker object' do
42
+ expect(client.bearer_token_request_body['password']).to eq('password')
43
+ end
44
+ end
45
+
46
+ describe '#request_headers' do
47
+ context 'when bearer token exists' do
48
+ it 'has an Authorization header' do
49
+ client.bearer_token = '12345'
50
+ expect(client.request_headers.key?('Authorization')).to be true
51
+ end
52
+ end
53
+
54
+ context 'when bearer token does not exist' do
55
+ it 'has an Authorization header' do
56
+ expect(client.request_headers.key?('Authorization')).to be false
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#authorize!' do
62
+ context 'when a token is not authorized or no token exists' do
63
+ it 'generates a token successfully' do
64
+ allow(client).to receive(:authorized?).twice.and_return(false, true)
65
+ expect(client).to receive(:generate_bearer_token)
66
+
67
+ client.authorize!
68
+ end
69
+
70
+ it 'raises an exception if token generation fails' do
71
+ allow(client).to receive(:authorized?).and_return(false)
72
+ allow(client).to receive(:generate_bearer_token).and_raise(Vra::Exception::Unauthorized)
73
+
74
+ expect { client.authorize! }.to raise_error(Vra::Exception::Unauthorized)
75
+ end
76
+
77
+ it 'raises an exception if a generated token is unauthorized' do
78
+ allow(client).to receive(:authorized).twice.and_return(false, false)
79
+ allow(client).to receive(:generate_bearer_token)
80
+
81
+ expect { client.authorize! }.to raise_error(Vra::Exception::Unauthorized)
82
+ end
83
+ end
84
+
85
+ context 'when a token is authorized' do
86
+ it 'does not generate a new token' do
87
+ allow(client).to receive(:authorized?).and_return(true)
88
+ expect(client).to_not receive(:generate_bearer_token)
89
+
90
+ client.authorize!
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#authorized?' do
96
+ context 'when token does not exist' do
97
+ it 'returns false' do
98
+ expect(client.authorized?).to be false
99
+ end
100
+ end
101
+
102
+ context 'when token exists' do
103
+ before(:each) do
104
+ client.bearer_token = '12345'
105
+ end
106
+
107
+ url = '/identity/api/tokens/12345'
108
+
109
+ it 'returns true if the token validates successfully' do
110
+ response = double('response')
111
+ allow(response).to receive(:code).and_return(204)
112
+ allow(client).to receive(:http_head).with(url, :skip_auth).and_return(response)
113
+
114
+ expect(client.authorized?).to be true
115
+ end
116
+
117
+ it 'returns false if the token validates unsuccessfully' do
118
+ response = double('response')
119
+ allow(response).to receive(:code).and_return(500)
120
+ allow(client).to receive(:http_head).with(url, :skip_auth).and_return(response)
121
+
122
+ expect(client.authorized?).to be false
123
+ end
124
+ end
125
+ end
126
+
127
+ describe '#generate_bearer_token' do
128
+ payload = {
129
+ 'username' => 'user@corp.local',
130
+ 'password' => 'password',
131
+ 'tenant' => 'tenant'
132
+ }.to_json
133
+
134
+ it 'posts to the tokens API endpoint' do
135
+ response = double('response')
136
+ allow(response).to receive(:code).and_return(200)
137
+ allow(response).to receive(:body).and_return('{"id":"12345"}')
138
+ expect(client).to receive(:http_post).with('/identity/api/tokens',
139
+ payload,
140
+ :skip_auth).and_return(response)
141
+
142
+ client.generate_bearer_token
143
+ end
144
+
145
+ context 'when token is generated successfully' do
146
+ it 'sets the token' do
147
+ response = double('response')
148
+ allow(response).to receive(:code).and_return(200)
149
+ allow(response).to receive(:body).and_return('{"id":"12345"}')
150
+ allow(client).to receive(:http_post).with('/identity/api/tokens',
151
+ payload,
152
+ :skip_auth).and_return(response)
153
+
154
+ client.generate_bearer_token
155
+
156
+ expect(client.bearer_token).to eq '12345'
157
+ end
158
+ end
159
+
160
+ context 'when token is not generated successfully' do
161
+ it 'raises an exception' do
162
+ response = double('response')
163
+ allow(response).to receive(:code).and_return(500)
164
+ allow(response).to receive(:body).and_return('error string')
165
+ allow(client).to receive(:http_post).with('/identity/api/tokens',
166
+ payload,
167
+ :skip_auth)
168
+ .and_return(response)
169
+
170
+ expect { client.generate_bearer_token }.to raise_error(Vra::Exception::Unauthorized)
171
+ end
172
+ end
173
+ end
174
+
175
+ describe '#full_url' do
176
+ it 'returns a properly formatted url' do
177
+ expect(client.full_url('/mypath')).to eq 'https://vra.corp.local/mypath'
178
+ end
179
+ end
180
+
181
+ describe '#http_head' do
182
+ context 'when skip_auth is nil' do
183
+ it 'authorizes before proceeding' do
184
+ response = double('response')
185
+ allow(RestClient::Request).to receive(:execute).and_return(response)
186
+ expect(client).to receive(:authorize!)
187
+
188
+ client.http_head('/test')
189
+ end
190
+ end
191
+
192
+ context 'when skip_auth is not nil' do
193
+ it 'does not authorize before proceeding' do
194
+ response = double('response')
195
+ allow(RestClient::Request).to receive(:execute).and_return(response)
196
+ expect(client).to_not receive(:authorize!)
197
+
198
+ client.http_head('/test', :skip_auth)
199
+ end
200
+ end
201
+
202
+ it 'calls RestClient::Request#execute' do
203
+ response = double('response')
204
+ path = '/test'
205
+ full_url = 'https://vra.corp.local/test'
206
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
207
+ verify_ssl = true
208
+
209
+ allow(client).to receive(:authorize!)
210
+ expect(RestClient::Request).to receive(:execute).with(method: :head,
211
+ url: full_url,
212
+ headers: headers,
213
+ verify_ssl: verify_ssl)
214
+ .and_return(response)
215
+
216
+ client.http_head(path)
217
+ end
218
+ end
219
+
220
+ describe '#http_get' do
221
+ context 'when skip_auth is nil' do
222
+ it 'authorizes before proceeding' do
223
+ response = double('response')
224
+ allow(RestClient::Request).to receive(:execute).and_return(response)
225
+ expect(client).to receive(:authorize!)
226
+
227
+ client.http_get('/test')
228
+ end
229
+ end
230
+
231
+ context 'when skip_auth is not nil' do
232
+ it 'does not authorize before proceeding' do
233
+ response = double('response')
234
+ allow(RestClient::Request).to receive(:execute).and_return(response)
235
+ expect(client).to_not receive(:authorize!)
236
+
237
+ client.http_get('/test', :skip_auth)
238
+ end
239
+ end
240
+
241
+ it 'calls RestClient::Request#execute' do
242
+ response = double('response')
243
+ path = '/test'
244
+ full_url = 'https://vra.corp.local/test'
245
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
246
+ verify_ssl = true
247
+
248
+ allow(client).to receive(:authorize!)
249
+ expect(RestClient::Request).to receive(:execute).with(method: :get,
250
+ url: full_url,
251
+ headers: headers,
252
+ verify_ssl: verify_ssl)
253
+ .and_return(response)
254
+
255
+ client.http_get(path)
256
+ end
257
+
258
+ it 'calls raise_http_exception upon a RestClient error' do
259
+ allow(client).to receive(:authorize!)
260
+ allow(RestClient::Request).to receive(:execute).and_raise(RestClient::ResourceNotFound)
261
+ expect(client).to receive(:raise_http_exception)
262
+
263
+ client.http_get('/404')
264
+ end
265
+ end
266
+
267
+ describe '#http_get!' do
268
+ it 'returns the response body' do
269
+ response = double('response', body: 'body text')
270
+ allow(client).to receive(:http_get).with('/test').and_return(response)
271
+
272
+ expect(client.http_get!('/test')).to eq 'body text'
273
+ end
274
+ end
275
+
276
+ describe '#http_get_paginated_array!' do
277
+ it 'allows a limit override' do
278
+ client.page_size = 10
279
+ expect(client).to receive(:http_get!)
280
+ .with('/test?limit=10&page=1')
281
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 1 } }.to_json)
282
+
283
+ client.http_get_paginated_array!('/test')
284
+ end
285
+
286
+ it 'only calls http_get! once when total pages is 0 (no items)' do
287
+ expect(client).to receive(:http_get!)
288
+ .once
289
+ .with('/test?limit=20&page=1')
290
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 0 } }.to_json)
291
+
292
+ client.http_get_paginated_array!('/test')
293
+ end
294
+
295
+ it 'only calls http_get! once when total pages is 1' do
296
+ expect(client).to receive(:http_get!)
297
+ .once
298
+ .with('/test?limit=20&page=1')
299
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 1 } }.to_json)
300
+
301
+ client.http_get_paginated_array!('/test')
302
+ end
303
+
304
+ it 'calls http_get! 3 times if there are 3 pages of response' do
305
+ expect(client).to receive(:http_get!)
306
+ .with('/test?limit=20&page=1')
307
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 3 } }.to_json)
308
+ expect(client).to receive(:http_get!)
309
+ .with('/test?limit=20&page=2')
310
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 3 } }.to_json)
311
+ expect(client).to receive(:http_get!)
312
+ .with('/test?limit=20&page=3')
313
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 3 } }.to_json)
314
+
315
+ client.http_get_paginated_array!('/test')
316
+ end
317
+
318
+ it 'raises an exception if duplicate items are returned by the API' do
319
+ allow(client).to receive(:http_get!)
320
+ .with('/test?limit=20&page=1')
321
+ .and_return({ 'content' => [ 1, 2, 3, 1 ], 'metadata' => { 'totalPages' => 1 } }.to_json)
322
+
323
+ expect { client.http_get_paginated_array!('/test') }.to raise_error(Vra::Exception::DuplicateItemsDetected)
324
+ end
325
+ end
326
+
327
+ describe '#http_post' do
328
+ context 'when skip_auth is nil' do
329
+ it 'authorizes before proceeding' do
330
+ response = double('response')
331
+ allow(RestClient::Request).to receive(:execute).and_return(response)
332
+ expect(client).to receive(:authorize!)
333
+
334
+ client.http_post('/test', 'some payload')
335
+ end
336
+ end
337
+
338
+ context 'when skip_auth is not nil' do
339
+ it 'does not authorize before proceeding' do
340
+ response = double('response')
341
+ allow(RestClient::Request).to receive(:execute).and_return(response)
342
+ expect(client).to_not receive(:authorize!)
343
+
344
+ client.http_post('/test', 'some payload', :skip_auth)
345
+ end
346
+ end
347
+
348
+ it 'calls RestClient::Request#execute' do
349
+ response = double('response')
350
+ path = '/test'
351
+ full_url = 'https://vra.corp.local/test'
352
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
353
+ payload = 'some payload'
354
+ verify_ssl = true
355
+
356
+ allow(client).to receive(:authorize!)
357
+ expect(RestClient::Request).to receive(:execute).with(method: :post,
358
+ url: full_url,
359
+ headers: headers,
360
+ payload: payload,
361
+ verify_ssl: verify_ssl)
362
+ .and_return(response)
363
+
364
+ client.http_post(path, payload)
365
+ end
366
+
367
+ it 'calls raise_http_exception upon a RestClient error' do
368
+ allow(client).to receive(:authorize!)
369
+ allow(RestClient::Request).to receive(:execute).and_raise(RestClient::ResourceNotFound)
370
+ expect(client).to receive(:raise_http_exception)
371
+
372
+ client.http_post('/404', 'test payload')
373
+ end
374
+ end
375
+
376
+ describe '#http_post!' do
377
+ it 'returns the response body' do
378
+ response = double('response', body: 'body text')
379
+ allow(client).to receive(:http_post).with('/test', 'test payload').and_return(response)
380
+
381
+ expect(client.http_post!('/test', 'test payload')).to eq 'body text'
382
+ end
383
+ end
384
+
385
+ describe '#raise_http_exception' do
386
+ context 'when a 404 is received' do
387
+ let(:exception) do
388
+ double('RestClient::ResourceNotFound',
389
+ http_code: 404,
390
+ message: 'Not Found',
391
+ response: '404 Not Found')
392
+ end
393
+
394
+ it 'raises a Vra::Exception::HTTPNotFound exception' do
395
+ expect { client.raise_http_exception(exception, '/test') }.to raise_error(Vra::Exception::HTTPNotFound)
396
+ end
397
+ end
398
+
399
+ context 'when an unspecified http error is received' do
400
+ let(:exception) do
401
+ double('RestClient::BadRequest',
402
+ http_code: 400,
403
+ message: 'Bad Request',
404
+ response: '400 Bad Request')
405
+ end
406
+
407
+ it 'raises a Vra::Exception::HTTPError exception' do
408
+ expect { client.raise_http_exception(exception, '/test') }.to raise_error(Vra::Exception::HTTPError)
409
+ end
410
+ end
411
+ end
412
+
413
+ describe '#validate_client_options!' do
414
+ context 'when all required options are supplied' do
415
+ it 'does not raise an exception' do
416
+ expect { client.validate_client_options! }.not_to raise_error
417
+ end
418
+ end
419
+
420
+ context 'when username is missing' do
421
+ let(:client) do
422
+ Vra::Client.new(password: 'password',
423
+ tenant: 'tenant',
424
+ base_url: 'https://vra.corp.local')
425
+ end
426
+
427
+ it 'raises an exception' do
428
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
429
+ end
430
+ end
431
+
432
+ context 'when password is missing' do
433
+ let(:client) do
434
+ Vra::Client.new(username: 'username',
435
+ tenant: 'tenant',
436
+ base_url: 'https://vra.corp.local')
437
+ end
438
+
439
+ it 'raises an exception' do
440
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
441
+ end
442
+ end
443
+
444
+ context 'when tenant is missing' do
445
+ let(:client) do
446
+ Vra::Client.new(username: 'username',
447
+ password: 'password',
448
+ base_url: 'https://vra.corp.local')
449
+ end
450
+
451
+ it 'raises an exception' do
452
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
453
+ end
454
+ end
455
+
456
+ context 'when base URL is missing' do
457
+ let(:client) do
458
+ Vra::Client.new(username: 'username',
459
+ password: 'password',
460
+ tenant: 'tenant')
461
+ end
462
+
463
+ it 'raises an exception' do
464
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
465
+ end
466
+ end
467
+
468
+ context 'when base URL is not a valid HTTP URL' do
469
+ let(:client) do
470
+ Vra::Client.new(username: 'username',
471
+ password: 'password',
472
+ tenant: 'tenant',
473
+ base_url: 'something-that-is-not-a-HTTP-URI')
474
+ end
475
+
476
+ it 'raises an exception' do
477
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
478
+ end
479
+ end
480
+ end
481
+ end