Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit ef15286

Browse files
authored
Merge pull request #332 from cloudant/330-specify-vcap-service-name
Allow service name to be specified when instantiating from VCAP
2 parents e1b5a32 + 8e89338 commit ef15286

File tree

6 files changed

+108
-26
lines changed

6 files changed

+108
-26
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Unreleased
22
==========
33
- [NEW] Added ``Result.all()`` convenience method.
4+
- [NEW] Allow ``service_name`` to be specified when instantiating from a Bluemix VCAP_SERVICES environment variable.
45
- [IMPROVED] Updated ``posixpath.join`` references to use ``'/'.join`` when concatenating URL parts.
56
- [IMPROVED] Updated documentation by replacing deprecated Cloudant links with the latest Bluemix links.
67

src/cloudant/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def cloudant_iam(account_name, api_key, **kwargs):
9292
cloudant_session.disconnect()
9393

9494
@contextlib.contextmanager
95-
def cloudant_bluemix(vcap_services, instance_name=None, **kwargs):
95+
def cloudant_bluemix(vcap_services, instance_name=None, service_name=None, **kwargs):
9696
"""
9797
Provides a context manager to create a Cloudant session and provide access
9898
to databases, docs etc.
@@ -101,6 +101,7 @@ def cloudant_bluemix(vcap_services, instance_name=None, **kwargs):
101101
:type vcap_services: dict or str
102102
:param str instance_name: Optional Bluemix instance name. Only required if
103103
multiple Cloudant instances are available.
104+
:param str service_name: Optional Bluemix service name.
104105
:param str encoder: Optional json Encoder object used to encode
105106
documents for storage. Defaults to json.JSONEncoder.
106107
@@ -149,11 +150,10 @@ def cloudant_bluemix(vcap_services, instance_name=None, **kwargs):
149150
print client.all_dbs()
150151
# ...
151152
"""
152-
service = CloudFoundryService(vcap_services, instance_name)
153-
cloudant_session = Cloudant(
154-
service.username,
155-
service.password,
156-
url=service.url,
153+
cloudant_session = Cloudant.bluemix(
154+
vcap_services,
155+
instance_name=instance_name,
156+
service_name=service_name,
157157
**kwargs
158158
)
159159
cloudant_session.connect()

src/cloudant/_common_util.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -498,18 +498,18 @@ def _get_access_token(self):
498498
class CloudFoundryService(object):
499499
""" Manages Cloud Foundry service configuration. """
500500

501-
def __init__(self, vcap_services, name=None):
501+
def __init__(self, vcap_services, instance_name=None, service_name=None):
502502
try:
503503
services = vcap_services
504504
if not isinstance(vcap_services, dict):
505505
services = json.loads(vcap_services)
506506

507-
cloudant_services = services.get('cloudantNoSQLDB', [])
507+
cloudant_services = services.get(service_name, [])
508508

509509
# use first service if no name given and only one service present
510-
use_first = name is None and len(cloudant_services) == 1
510+
use_first = instance_name is None and len(cloudant_services) == 1
511511
for service in cloudant_services:
512-
if use_first or service.get('name') == name:
512+
if use_first or service.get('name') == instance_name:
513513
credentials = service['credentials']
514514
self._host = credentials['host']
515515
self._name = service.get('name')

src/cloudant/client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,14 +754,15 @@ def _write_cors_configuration(self, config):
754754
return resp.json()
755755

756756
@classmethod
757-
def bluemix(cls, vcap_services, instance_name=None, **kwargs):
757+
def bluemix(cls, vcap_services, instance_name=None, service_name=None, **kwargs):
758758
"""
759759
Create a Cloudant session using a VCAP_SERVICES environment variable.
760760
761761
:param vcap_services: VCAP_SERVICES environment variable
762762
:type vcap_services: dict or str
763763
:param str instance_name: Optional Bluemix instance name. Only required
764764
if multiple Cloudant instances are available.
765+
:param str service_name: Optional Bluemix service name.
765766
766767
Example usage:
767768
@@ -775,7 +776,10 @@ def bluemix(cls, vcap_services, instance_name=None, **kwargs):
775776
776777
print client.all_dbs()
777778
"""
778-
service = CloudFoundryService(vcap_services, instance_name)
779+
service_name = service_name or 'cloudantNoSQLDB' # default service
780+
service = CloudFoundryService(vcap_services,
781+
instance_name=instance_name,
782+
service_name=service_name)
779783
return Cloudant(service.username,
780784
service.password,
781785
url=service.url,

tests/unit/client_tests.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,34 @@ def test_cloudant_bluemix_context_helper(self):
552552
except Exception as err:
553553
self.fail('Exception {0} was raised.'.format(str(err)))
554554

555+
def test_cloudant_bluemix_dedicated_context_helper(self):
556+
"""
557+
Test that the cloudant_bluemix context helper works as expected when
558+
specifying a service name.
559+
"""
560+
instance_name = 'Cloudant NoSQL DB-wq'
561+
service_name = 'cloudantNoSQLDB Dedicated'
562+
vcap_services = {service_name: [{
563+
'credentials': {
564+
'username': self.user,
565+
'password': self.pwd,
566+
'host': '{0}.cloudant.com'.format(self.account),
567+
'port': 443,
568+
'url': self.url
569+
},
570+
'name': instance_name,
571+
}]}
572+
573+
try:
574+
with cloudant_bluemix(vcap_services,
575+
instance_name=instance_name,
576+
service_name=service_name) as c:
577+
self.assertIsInstance(c, Cloudant)
578+
self.assertIsInstance(c.r_session, requests.Session)
579+
self.assertEquals(c.session()['userCtx']['name'], self.user)
580+
except Exception as err:
581+
self.fail('Exception {0} was raised.'.format(str(err)))
582+
555583
def test_constructor_with_account(self):
556584
"""
557585
Test instantiating a client object using an account name

tests/unit/cloud_foundry_tests.py

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,68 +91,104 @@ def __init__(self, *args, **kwargs):
9191
]
9292
}
9393
]})
94+
self._test_vcap_services_dedicated = json.dumps({
95+
'cloudantNoSQLDB Dedicated': [ # dedicated service name
96+
{
97+
'name': 'Cloudant NoSQL DB 1', # valid service
98+
'credentials': {
99+
'host': 'example.cloudant.com',
100+
'password': 'pa$$w0rd01',
101+
'port': 1234,
102+
'username': 'example'
103+
}
104+
}
105+
]
106+
})
94107

95108
def test_get_vcap_service_default_success(self):
96-
service = CloudFoundryService(self._test_vcap_services_single)
109+
service = CloudFoundryService(
110+
self._test_vcap_services_single,
111+
service_name='cloudantNoSQLDB'
112+
)
97113
self.assertEqual('Cloudant NoSQL DB 1', service.name)
98114

99115
def test_get_vcap_service_default_success_as_dict(self):
100116
service = CloudFoundryService(
101-
json.loads(self._test_vcap_services_single)
117+
json.loads(self._test_vcap_services_single),
118+
service_name='cloudantNoSQLDB'
102119
)
103120
self.assertEqual('Cloudant NoSQL DB 1', service.name)
104121

105122
def test_get_vcap_service_default_failure_multiple_services(self):
106123
with self.assertRaises(CloudantException) as cm:
107-
CloudFoundryService(self._test_vcap_services_multiple)
124+
CloudFoundryService(
125+
self._test_vcap_services_multiple,
126+
service_name='cloudantNoSQLDB'
127+
)
108128
self.assertEqual('Missing service in VCAP_SERVICES', str(cm.exception))
109129

110130
def test_get_vcap_service_instance_host(self):
111131
service = CloudFoundryService(
112-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
132+
self._test_vcap_services_multiple,
133+
instance_name='Cloudant NoSQL DB 1',
134+
service_name='cloudantNoSQLDB'
113135
)
114136
self.assertEqual('example.cloudant.com', service.host)
115137

116138
def test_get_vcap_service_instance_password(self):
117139
service = CloudFoundryService(
118-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
140+
self._test_vcap_services_multiple,
141+
instance_name='Cloudant NoSQL DB 1',
142+
service_name='cloudantNoSQLDB'
119143
)
120144
self.assertEqual('pa$$w0rd01', service.password)
121145

122146
def test_get_vcap_service_instance_port(self):
123147
service = CloudFoundryService(
124-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
148+
self._test_vcap_services_multiple,
149+
instance_name='Cloudant NoSQL DB 1',
150+
service_name='cloudantNoSQLDB'
125151
)
126152
self.assertEqual('1234', service.port)
127153

128154
def test_get_vcap_service_instance_port_default(self):
129155
service = CloudFoundryService(
130-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 2'
156+
self._test_vcap_services_multiple,
157+
instance_name='Cloudant NoSQL DB 2',
158+
service_name='cloudantNoSQLDB'
131159
)
132160
self.assertEqual('443', service.port)
133161

134162
def test_get_vcap_service_instance_url(self):
135163
service = CloudFoundryService(
136-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
164+
self._test_vcap_services_multiple,
165+
instance_name='Cloudant NoSQL DB 1',
166+
service_name='cloudantNoSQLDB'
137167
)
138168
self.assertEqual('https://example.cloudant.com:1234', service.url)
139169

140170
def test_get_vcap_service_instance_username(self):
141171
service = CloudFoundryService(
142-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
172+
self._test_vcap_services_multiple,
173+
instance_name='Cloudant NoSQL DB 1',
174+
service_name='cloudantNoSQLDB'
143175
)
144176
self.assertEqual('example', service.username)
145177

146178
def test_raise_error_for_missing_host(self):
147179
with self.assertRaises(CloudantException):
148180
CloudFoundryService(
149-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 3'
181+
self._test_vcap_services_multiple,
182+
instance_name='Cloudant NoSQL DB 3',
183+
service_name='cloudantNoSQLDB'
150184
)
151185

152186
def test_raise_error_for_missing_password(self):
153187
with self.assertRaises(CloudantException) as cm:
154188
CloudFoundryService(
155-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 4'
189+
self._test_vcap_services_multiple,
190+
instance_name='Cloudant NoSQL DB 4',
191+
service_name='cloudantNoSQLDB'
156192
)
157193
self.assertEqual(
158194
"Invalid service: 'password' missing",
@@ -162,7 +198,9 @@ def test_raise_error_for_missing_password(self):
162198
def test_raise_error_for_missing_username(self):
163199
with self.assertRaises(CloudantException) as cm:
164200
CloudFoundryService(
165-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 5'
201+
self._test_vcap_services_multiple,
202+
instance_name='Cloudant NoSQL DB 5',
203+
service_name='cloudantNoSQLDB'
166204
)
167205
self.assertEqual(
168206
"Invalid service: 'username' missing",
@@ -172,7 +210,9 @@ def test_raise_error_for_missing_username(self):
172210
def test_raise_error_for_invalid_credentials_type(self):
173211
with self.assertRaises(CloudantException) as cm:
174212
CloudFoundryService(
175-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 6'
213+
self._test_vcap_services_multiple,
214+
instance_name='Cloudant NoSQL DB 6',
215+
service_name='cloudantNoSQLDB'
176216
)
177217
self.assertEqual(
178218
'Failed to decode VCAP_SERVICES service credentials',
@@ -182,11 +222,20 @@ def test_raise_error_for_invalid_credentials_type(self):
182222
def test_raise_error_for_missing_service(self):
183223
with self.assertRaises(CloudantException) as cm:
184224
CloudFoundryService(
185-
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 7'
225+
self._test_vcap_services_multiple,
226+
instance_name='Cloudant NoSQL DB 7',
227+
service_name='cloudantNoSQLDB'
186228
)
187229
self.assertEqual('Missing service in VCAP_SERVICES', str(cm.exception))
188230

189231
def test_raise_error_for_invalid_vcap(self):
190232
with self.assertRaises(CloudantException) as cm:
191233
CloudFoundryService('{', 'Cloudant NoSQL DB 1') # invalid JSON
192234
self.assertEqual('Failed to decode VCAP_SERVICES JSON', str(cm.exception))
235+
236+
def test_get_vcap_service_with_dedicated_service_name_success(self):
237+
service = CloudFoundryService(
238+
self._test_vcap_services_dedicated,
239+
service_name='cloudantNoSQLDB Dedicated'
240+
)
241+
self.assertEqual('Cloudant NoSQL DB 1', service.name)

0 commit comments

Comments
 (0)