Skip to content

Commit a304b1a

Browse files
authored
Merge pull request #28 from IBM/order
Change order of credential path and read_external_sources and more
2 parents 7ecc329 + 164d02e commit a304b1a

File tree

6 files changed

+85
-82
lines changed

6 files changed

+85
-82
lines changed

ibm_cloud_sdk_core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@
1919
from .jwt_token_manager import JWTTokenManager
2020
from .cp4d_token_manager import CP4DTokenManager
2121
from .api_exception import ApiException
22-
from .utils import datetime_to_string, string_to_datetime, get_authenticator_from_environment
22+
from .utils import datetime_to_string, string_to_datetime, get_authenticator_from_environment, read_external_sources

ibm_cloud_sdk_core/base_service.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import requests
2222
from requests.structures import CaseInsensitiveDict
2323
from .version import __version__
24-
from .utils import has_bad_first_or_last_char, remove_null_values, cleanup_values, read_from_env_variables
24+
from .utils import has_bad_first_or_last_char, remove_null_values, cleanup_values, read_external_sources
2525
from .detailed_response import DetailedResponse
2626
from .api_exception import ApiException
2727
from .authenticators import Authenticator
@@ -37,15 +37,13 @@ class BaseService(object):
3737
SDK_NAME = 'ibm-python-sdk-core'
3838

3939
def __init__(self,
40-
service_url,
40+
service_url=None,
4141
authenticator=None,
42-
disable_ssl_verification=False,
43-
display_name=None):
42+
disable_ssl_verification=False):
4443
"""
45-
:attr str url: The url for service api calls
44+
:attr str service_url: The url for service api calls
4645
:attr Authenticator authenticator: The authenticator for authentication
4746
:attr bool disable_ssl_verification: enables/ disables ssl verification
48-
:attr str display_name the name used for mapping services in environment file
4947
"""
5048
self.service_url = service_url
5149
self.http_config = {}
@@ -63,15 +61,6 @@ def __init__(self,
6361
raise ValueError(
6462
'authenticator should be of type Authenticator')
6563

66-
if display_name:
67-
service_name = display_name.replace(' ', '_').lower()
68-
config = read_from_env_variables(service_name)
69-
if config.get('url'):
70-
self.service_url = config.get('url')
71-
if config.get('disable_ssl'):
72-
self.disable_ssl_verification = config.get('disable_ssl')
73-
74-
7564
def _get_system_info(self):
7665
return '{0} {1} {2}'.format(
7766
platform.system(), # OS

ibm_cloud_sdk_core/utils.py

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -54,62 +54,71 @@ def string_to_datetime(string):
5454
"""
5555
return date_parser.parse(string)
5656

57-
def get_authenticator_from_environment(service_name):
57+
def read_external_sources(service_name):
5858
"""
59-
Checks the credentials file and VCAP_SERVICES environment variable
59+
Try to get config from external sources, with the following priority:
60+
1. Credentials file(ibm-credentials.env)
61+
2. Environment variables
62+
3. VCAP Services(Cloud Foundry)
6063
:param service_name: The service name
61-
:return: the authenticator
64+
:return: dict
6265
"""
63-
authenticator = None
64-
# 1. Credentials from credential file
66+
config = {}
67+
6568
config = read_from_credential_file(service_name)
66-
if config:
67-
authenticator = contruct_authenticator(config)
6869

69-
# 2. From env variables
70-
if not authenticator:
70+
if not config:
7171
config = read_from_env_variables(service_name)
72-
if config:
73-
authenticator = contruct_authenticator(config)
7472

75-
# 3. Credentials from VCAP
76-
if not authenticator:
73+
if not config:
7774
config = read_from_vcap_services(service_name)
78-
if config:
79-
authenticator = contruct_authenticator(config)
75+
76+
return config
77+
78+
def get_authenticator_from_environment(service_name):
79+
"""
80+
Try to get authenticator from external sources, with the following priority:
81+
1. Credentials file(ibm-credentials.env)
82+
2. Environment variables
83+
3. VCAP Services(Cloud Foundry)
84+
:param service_name: The service name
85+
:return: the authenticator
86+
"""
87+
authenticator = None
88+
config = read_external_sources(service_name)
89+
if config:
90+
authenticator = _construct_authenticator(config)
8091
return authenticator
8192

8293
def read_from_env_variables(service_name):
8394
"""
8495
:return dict config: parsed env variables
8596
"""
86-
service_name = service_name.replace(' ', '_').lower()
8797
config = {}
8898
for key, value in environ.items():
89-
_parse_key_and_update_config(config, service_name.lower(), key.lower(), value)
99+
_parse_key_and_update_config(config, service_name, key, value)
90100
return config
91101

92102
def read_from_credential_file(service_name, separator='='):
93103
"""
94104
:param str service_name: The service name
95105
:return dict config: parsed key values pairs
96106
"""
97-
service_name = service_name.replace(' ', '_').lower()
98107
DEFAULT_CREDENTIALS_FILE_NAME = 'ibm-credentials.env'
99108

100109
# File path specified by an env variable
101110
credential_file_path = getenv('IBM_CREDENTIALS_FILE')
102111

103-
# Home directory
112+
# Current working directory
104113
if credential_file_path is None:
105-
file_path = join(expanduser('~'), DEFAULT_CREDENTIALS_FILE_NAME)
114+
file_path = join(
115+
dirname(dirname(abspath(__file__))), DEFAULT_CREDENTIALS_FILE_NAME)
106116
if isfile(file_path):
107117
credential_file_path = file_path
108118

109-
# Top-level of the project directory
119+
# Home directory
110120
if credential_file_path is None:
111-
file_path = join(
112-
dirname(dirname(abspath(__file__))), DEFAULT_CREDENTIALS_FILE_NAME)
121+
file_path = join(expanduser('~'), DEFAULT_CREDENTIALS_FILE_NAME)
113122
if isfile(file_path):
114123
credential_file_path = file_path
115124

@@ -119,62 +128,62 @@ def read_from_credential_file(service_name, separator='='):
119128
for line in fp:
120129
key_val = line.strip().split(separator)
121130
if len(key_val) == 2:
122-
key = key_val[0].lower()
131+
key = key_val[0]
123132
value = key_val[1]
124133
_parse_key_and_update_config(config, service_name, key, value)
125134
return config
126135

127136
def _parse_key_and_update_config(config, service_name, key, value):
128-
if service_name in key:
129-
index = key.find('_')
130-
if index != -1:
131-
config[key[index + 1:]] = value
137+
service_name = service_name.replace(' ', '_').replace('-', '_').upper()
138+
if key.startswith(service_name):
139+
config[key[len(service_name) + 1:]] = value
132140

133141
def read_from_vcap_services(service_name):
134-
service_name = service_name.replace(' ', '_').lower()
135142
vcap_services = getenv('VCAP_SERVICES')
136-
vcap_service_credentials = None
143+
vcap_service_credentials = {}
137144
if vcap_services:
138145
services = json_import.loads(vcap_services)
139146

140147
for key in services.keys():
141-
name = key.replace('-', '_')
142-
if name == service_name:
148+
if key == service_name:
143149
vcap_service_credentials = services[key][0]['credentials']
144150
if vcap_service_credentials is not None and isinstance(vcap_service_credentials, dict):
145151
if vcap_service_credentials.get('username') and vcap_service_credentials.get('password'): # cf
146-
vcap_service_credentials['auth_type'] = 'basic'
152+
vcap_service_credentials['AUTH_TYPE'] = 'basic'
153+
vcap_service_credentials['USERNAME'] = vcap_service_credentials.get('username')
154+
vcap_service_credentials['PASSWORD'] = vcap_service_credentials.get('password')
147155
elif vcap_service_credentials.get('apikey'): # rc
148-
vcap_service_credentials['auth_type'] = 'iam'
156+
vcap_service_credentials['AUTH_TYPE'] = 'iam'
157+
vcap_service_credentials['APIKEY'] = vcap_service_credentials.get('apikey')
149158
else: # no other auth mechanism is supported
150-
vcap_service_credentials = None
159+
vcap_service_credentials = {}
151160
return vcap_service_credentials
152161

153-
def contruct_authenticator(config):
154-
auth_type = config.get('auth_type').lower() if config.get('auth_type') else 'iam'
162+
def _construct_authenticator(config):
163+
auth_type = config.get('AUTH_TYPE').lower() if config.get('AUTH_TYPE') else 'iam'
155164
authenticator = None
156165
from .authenticators import BasicAuthenticator, BearerTokenAuthenticator, CloudPakForDataAuthenticator, IAMAuthenticator, NoAuthAuthenticator
157166

158167
if auth_type == 'basic':
159168
authenticator = BasicAuthenticator(
160-
username=config.get('username'),
161-
password=config.get('password'))
169+
username=config.get('USERNAME'),
170+
password=config.get('PASSWORD'))
162171
elif auth_type == 'bearertoken':
163172
authenticator = BearerTokenAuthenticator(
164-
bearer_token=config.get('bearer_token'))
173+
bearer_token=config.get('BEARER_TOKEN'))
165174
elif auth_type == 'cp4d':
166175
authenticator = CloudPakForDataAuthenticator(
167-
username=config.get('username'),
168-
password=config.get('password'),
169-
url=config.get('auth_url'),
170-
disable_ssl_verification=config.get('auth_disable_ssl'))
171-
elif auth_type == 'iam' and config.get('apikey'):
176+
username=config.get('USERNAME'),
177+
password=config.get('PASSWORD'),
178+
url=config.get('AUTH_URL'),
179+
disable_ssl_verification=config.get('AUTH_DISABLE_SSL'))
180+
elif auth_type == 'iam' and config.get('APIKEY'):
172181
authenticator = IAMAuthenticator(
173-
apikey=config.get('apikey'),
174-
url=config.get('auth_url'),
175-
client_id=config.get('client_id'),
176-
client_secret=config.get('client_secret'),
177-
disable_ssl_verification=config.get('auth_disable_ssl'))
182+
apikey=config.get('APIKEY'),
183+
url=config.get('AUTH_URL'),
184+
client_id=config.get('CLIENT_ID'),
185+
client_secret=config.get('CLIENT_SECRET'),
186+
disable_ssl_verification=config.get('AUTH_DISABLE_SSL'))
178187
elif auth_type == 'noauth':
179188
authenticator = NoAuthAuthenticator()
180189

resources/ibm-credentials-iam.env

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
WATSON_APIKEY=5678efgh
2-
WATSON_AUTH_TYPE=iam
1+
IBM_WATSON_APIKEY=5678efgh
2+
IBM_WATSON_AUTH_TYPE=iam
3+
IBM_WATSON_URL=https://gateway-s.watsonplatform.net/watson/api
4+
IBM_WATSON_DISABLE_SSL=False

test/test_base_service.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ibm_cloud_sdk_core import ApiException
1010
from ibm_cloud_sdk_core import CP4DTokenManager
1111
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator, NoAuthAuthenticator, Authenticator, BasicAuthenticator, CloudPakForDataAuthenticator
12+
from ibm_cloud_sdk_core import get_authenticator_from_environment
1213

1314

1415
class AnyServiceV1(BaseService):
@@ -23,8 +24,7 @@ def __init__(self,
2324
self,
2425
service_url=service_url,
2526
authenticator=authenticator,
26-
disable_ssl_verification=disable_ssl_verification,
27-
display_name='Watson')
27+
disable_ssl_verification=disable_ssl_verification)
2828
self.version = version
2929

3030
def op_with_path_params(self, path0, path1):
@@ -135,17 +135,13 @@ def test_fail_http_config():
135135

136136
@responses.activate
137137
def test_iam():
138-
iam_authenticator = IAMAuthenticator('my_apikey', 'https://iam-test.cloud.ibm.com/identity/token')
139138
file_path = os.path.join(
140139
os.path.dirname(__file__), '../resources/ibm-credentials-iam.env')
141140
os.environ['IBM_CREDENTIALS_FILE'] = file_path
142-
os.environ['WATSON_URL'] = 'https://gateway-s.watsonplatform.net/watson/api'
143-
os.environ['WATSON_DISABLE_SSL'] = 'False'
141+
iam_authenticator = get_authenticator_from_environment('ibm-watson')
144142
service = AnyServiceV1('2017-07-07', authenticator=iam_authenticator)
145-
assert service.service_url == 'https://gateway-s.watsonplatform.net/watson/api'
143+
assert service.service_url == 'https://gateway.watsonplatform.net/test/api'
146144
del os.environ['IBM_CREDENTIALS_FILE']
147-
del os.environ['WATSON_URL']
148-
del os.environ['WATSON_DISABLE_SSL']
149145
assert service.authenticator is not None
150146

151147
response = {
@@ -157,12 +153,12 @@ def test_iam():
157153
}
158154
responses.add(
159155
responses.POST,
160-
url='https://iam-test.cloud.ibm.com/identity/token',
156+
url='https://iam.cloud.ibm.com/identity/token',
161157
body=json.dumps(response),
162158
status=200)
163159
responses.add(
164160
responses.GET,
165-
url='https://gateway-s.watsonplatform.net/watson/api',
161+
url='https://gateway.watsonplatform.net/test/api',
166162
body=json.dumps({
167163
"foobar": "baz"
168164
}),
@@ -422,7 +418,7 @@ def test_json():
422418
assert req.get('data') == "{\"hello\": \"world\"}"
423419

424420
def test_service_url_not_set():
425-
service = BaseService(service_url='', authenticator=NoAuthAuthenticator(), display_name='Watson')
421+
service = BaseService(service_url='', authenticator=NoAuthAuthenticator())
426422
with pytest.raises(ValueError) as err:
427423
service.prepare_request('POST', url='')
428424
assert str(err.value) == 'The service_url is required'

test/test_utils.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ def test_datetime_conversion():
99

1010
def test_get_authenticator_from_credential_file():
1111
file_path = os.path.join(
12-
os.path.dirname(__file__), '../resources/ibm-credentials-iam.env')
12+
os.path.dirname(__file__), '../resources/ibm-credentials-iam.env')
1313
os.environ['IBM_CREDENTIALS_FILE'] = file_path
14-
authenticator = get_authenticator_from_environment('watson')
14+
authenticator = get_authenticator_from_environment('ibm watson')
1515
assert authenticator is not None
1616
assert authenticator.token_manager.apikey == '5678efgh'
1717
del os.environ['IBM_CREDENTIALS_FILE']
@@ -48,7 +48,7 @@ def test_get_authenticator_from_credential_file():
4848
assert authenticator.bearer_token is not None
4949
del os.environ['IBM_CREDENTIALS_FILE']
5050

51-
def test_get_authenticator_from_env_variabled():
51+
def test_get_authenticator_from_env_variables():
5252
os.environ['TEST_APIKEY'] = '5678efgh'
5353
authenticator = get_authenticator_from_environment('test')
5454
assert authenticator is not None
@@ -86,3 +86,10 @@ def test_vcap_credentials():
8686
authenticator = get_authenticator_from_environment('test')
8787
assert authenticator is None
8888
del os.environ['VCAP_SERVICES']
89+
90+
def test_multi_word_service_name():
91+
os.environ['PERSONALITY_INSIGHTS_APIKEY'] = '5678efgh'
92+
authenticator = get_authenticator_from_environment('personality-insights')
93+
assert authenticator is not None
94+
assert authenticator.token_manager.apikey == '5678efgh'
95+
del os.environ['PERSONALITY_INSIGHTS_APIKEY']

0 commit comments

Comments
 (0)