diff --git a/CHANGES.rst b/CHANGES.rst index f3c211c8..632bead9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,5 +1,6 @@ 2.6.0 (Unreleased) ================== +- [NEW] Added ``Cloudant.bluemix()`` class method to the Cloudant client allowing service credentials to be passed using the CloudFoundry VCAP_SERVICES environment variable. - [FIXED] Fixed client construction in ``cloudant_bluemix`` context manager. - [FIXED] Fixed validation for feed options to accept zero as a valid value. diff --git a/src/cloudant/client.py b/src/cloudant/client.py index e70e9af3..3c7da688 100755 --- a/src/cloudant/client.py +++ b/src/cloudant/client.py @@ -31,7 +31,8 @@ USER_AGENT, append_response_error_content, InfiniteSession, - ClientSession) + ClientSession, + CloudFoundryService) class CouchDB(dict): @@ -764,3 +765,31 @@ def _write_cors_configuration(self, config): resp.raise_for_status() return resp.json() + + @classmethod + def bluemix(cls, vcap_services, instance_name=None, **kwargs): + """ + Create a Cloudant session using a VCAP_SERVICES environment variable. + + :param vcap_services: VCAP_SERVICES environment variable + :type vcap_services: dict or str + :param str instance_name: Optional Bluemix instance name. Only required + if multiple Cloudant instances are available. + + Example usage: + + .. code-block:: python + + import os + from cloudant.client import Cloudant + + client = Cloudant.bluemix(os.getenv('VCAP_SERVICES'), + 'Cloudant NoSQL DB') + + print client.all_dbs() + """ + service = CloudFoundryService(vcap_services, instance_name) + return Cloudant(service.username, + service.password, + url=service.url, + **kwargs) diff --git a/tests/unit/client_tests.py b/tests/unit/client_tests.py index 137c26fb..b2dbcbf0 100644 --- a/tests/unit/client_tests.py +++ b/tests/unit/client_tests.py @@ -539,6 +539,114 @@ def test_constructor_with_account(self): 'https://{0}.cloudant.com'.format(self.account) ) + def test_bluemix_constructor(self): + """ + Test instantiating a client object using a VCAP_SERVICES environment + variable. + """ + instance_name = 'Cloudant NoSQL DB-lv' + vcap_services = {'cloudantNoSQLDB': [{ + 'credentials': { + 'username': self.user, + 'password': self.pwd, + 'host': '{0}.cloudant.com'.format(self.account), + 'port': 443, + 'url': self.url + }, + 'name': instance_name + }]} + + # create Cloudant Bluemix client + c = Cloudant.bluemix(vcap_services) + + try: + c.connect() + self.assertIsInstance(c, Cloudant) + self.assertIsInstance(c.r_session, requests.Session) + self.assertEquals(c.session()['userCtx']['name'], self.user) + + except Exception as err: + self.fail('Exception {0} was raised.'.format(str(err))) + + finally: + c.disconnect() + + def test_bluemix_constructor_specify_instance_name(self): + """ + Test instantiating a client object using a VCAP_SERVICES environment + variable and specifying which instance name to use. + """ + instance_name = 'Cloudant NoSQL DB-lv' + vcap_services = {'cloudantNoSQLDB': [{ + 'credentials': { + 'username': self.user, + 'password': self.pwd, + 'host': '{0}.cloudant.com'.format(self.account), + 'port': 443, + 'url': self.url + }, + 'name': instance_name + }]} + + # create Cloudant Bluemix client + c = Cloudant.bluemix(vcap_services, instance_name=instance_name) + + try: + c.connect() + self.assertIsInstance(c, Cloudant) + self.assertIsInstance(c.r_session, requests.Session) + self.assertEquals(c.session()['userCtx']['name'], self.user) + + except Exception as err: + self.fail('Exception {0} was raised.'.format(str(err))) + + finally: + c.disconnect() + + def test_bluemix_constructor_with_multiple_services(self): + """ + Test instantiating a client object using a VCAP_SERVICES environment + variable that contains multiple services. + """ + instance_name = 'Cloudant NoSQL DB-lv' + vcap_services = {'cloudantNoSQLDB': [ + { + 'credentials': { + 'username': self.user, + 'password': self.pwd, + 'host': '{0}.cloudant.com'.format(self.account), + 'port': 443, + 'url': self.url + }, + 'name': instance_name + }, + { + 'credentials': { + 'username': 'foo', + 'password': 'bar', + 'host': 'baz.com', + 'port': 1234, + 'url': 'https://foo:bar@baz.com:1234' + }, + 'name': 'Cloudant NoSQL DB-yu' + } + ]} + + # create Cloudant Bluemix client + c = Cloudant.bluemix(vcap_services, instance_name=instance_name) + + try: + c.connect() + self.assertIsInstance(c, Cloudant) + self.assertIsInstance(c.r_session, requests.Session) + self.assertEquals(c.session()['userCtx']['name'], self.user) + + except Exception as err: + self.fail('Exception {0} was raised.'.format(str(err))) + + finally: + c.disconnect() + def test_connect_headers(self): """ Test that the appropriate request headers are set