ibm-cloud-sdk-core-1.6.2/0000775000372000037200000000000013621341546015747 5ustar travistravis00000000000000ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/0000775000372000037200000000000013621341546021555 5ustar travistravis00000000000000ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/base_service.py0000664000372000037200000003372413621341247024570 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from http.cookiejar import CookieJar import json as json_import from os.path import basename import platform import sys from typing import Dict, List, Optional, Tuple, Union import requests from requests.structures import CaseInsensitiveDict from ibm_cloud_sdk_core.authenticators import Authenticator from .version import __version__ from .utils import has_bad_first_or_last_char, remove_null_values, cleanup_values, read_external_sources from .detailed_response import DetailedResponse from .api_exception import ApiException from .jwt_token_manager import JWTTokenManager # Uncomment this to enable http debugging # import http.client as http_client # http_client.HTTPConnection.debuglevel = 1 class BaseService: """Common functionality shared by generated service classes. The base service authenticates requests via its authenticator, stores cookies, and wraps responses from the service endpoint in DetailedResponse or APIException objects. Keyword Arguments: service_url: Url to the service endpoint. Defaults to None. authenticator: Adds authentication data to service requests. Defaults to None. disable_ssl_verification: A flag that indicates whether verification of the server's SSL certificate should be disabled or not. Defaults to False. Attributes: service_url (str): Url to the service endpoint. authenticator (Authenticator): Adds authentication data to service requests. disable_ssl_verification (bool): A flag that indicates whether verification of the server's SSL certificate should be disabled or not. default_headers (dict): A dictionary of headers to be sent with every HTTP request to the service endpoint. jar (http.cookiejar.CookieJar): Stores cookies received from the service. http_config (dict): A dictionary containing values that control the timeout, proxies, and etc of HTTP requests. Raises: ValueError: If Authenticator is not provided or invalid type. """ SDK_NAME = 'ibm-python-sdk-core' ERROR_MSG_DISABLE_SSL = 'The connection failed because the SSL certificate is not valid. To use a self-signed '\ 'certificate, disable verification of the server\'s SSL certificate by invoking the '\ 'set_disable_ssl_verification(True) on your service instance and/ or use the '\ 'disable_ssl_verification option of the authenticator.' def __init__(self, service_url: str = None, authenticator: Authenticator = None, disable_ssl_verification: bool = False): self.service_url = service_url self.http_config = {} self.jar = CookieJar() self.authenticator = authenticator self.disable_ssl_verification = disable_ssl_verification self.default_headers = None self._set_user_agent_header(self._build_user_agent()) if not self.authenticator: raise ValueError('authenticator must be provided') if not isinstance(self.authenticator, Authenticator): raise ValueError( 'authenticator should be of type Authenticator') @staticmethod def _get_system_info(): return '{0} {1} {2}'.format( platform.system(), # OS platform.release(), # OS version platform.python_version() # Python version ) def _build_user_agent(self): return '{0}-{1} {2}'.format(self.SDK_NAME, __version__, self._get_system_info()) def configure_service(self, service_name: str): """Look for external configuration of a service. Set service properties. Try to get config from external sources, with the following priority: 1. Credentials file(ibm-credentials.env) 2. Environment variables 3. VCAP Services(Cloud Foundry) Args: service_name: The service name Raises: ValueError: If service_name is not a string. """ if not isinstance(service_name, str): raise ValueError('Service_name must be of type string.') config = read_external_sources(service_name) if config.get('URL'): self.set_service_url(config.get('URL')) if config.get('DISABLE_SSL'): self.set_disable_ssl_verification( bool(config.get('DISABLE_SSL')) ) def _set_user_agent_header(self, user_agent_string=None): self.user_agent_header = {'User-Agent': user_agent_string} def set_http_config(self, http_config: dict): """Sets the http config dictionary. The dictionary can contain values that control the timeout, proxies, and etc of HTTP requests. Arguments: http_config: Configuration values to customize HTTP behaviors. Raises: TypeError: http_config is not a dict. """ if isinstance(http_config, dict): self.http_config = http_config if (self.authenticator and hasattr(self.authenticator, 'token_manager') and isinstance(self.authenticator.token_manager, JWTTokenManager)): self.authenticator.token_manager.http_config = http_config else: raise TypeError("http_config parameter must be a dictionary") def set_disable_ssl_verification(self, status: bool = False): """Set the flag that indicates whether verification of the server's SSL certificate should be disabled or not. Keyword Arguments: status: set to true to disable ssl verification (default: {False}) """ self.disable_ssl_verification = status def set_service_url(self, service_url: str): """Set the url the service will make HTTP requests too. Arguments: service_url: The WHATWG URL standard origin ex. https://example.service.com Raises: ValueError: Improperly formatted service_url """ if has_bad_first_or_last_char(service_url): raise ValueError( 'The service url shouldn\'t start or end with curly brackets or quotes. ' 'Be sure to remove any {} and \" characters surrounding your service url' ) self.service_url = service_url def get_authenticator(self) -> Authenticator: """Get the authenticator currently used by the service. Returns: The authenticator currently used by the service. """ return self.authenticator def set_default_headers(self, headers: Dict[str, str]): """Set http headers to be sent in every request. Arguments: headers: A dictionary of headers """ if isinstance(headers, dict): self.default_headers = headers else: raise TypeError("headers parameter must be a dictionary") def send(self, request: requests.Request, **kwargs) -> DetailedResponse: """Send a request and wrap the response in a DetailedResponse or APIException. Args: request: The request to send to the service endpoint. Raises: ApiException: The exception from the API. Returns: The response from the request. """ # Use a one minute timeout when our caller doesn't give a timeout. # http://docs.python-requests.org/en/master/user/quickstart/#timeouts kwargs = dict({"timeout": 60}, **kwargs) kwargs = dict(kwargs, **self.http_config) if self.disable_ssl_verification: kwargs['verify'] = False try: response = requests.request(**request, cookies=self.jar, **kwargs) if 200 <= response.status_code <= 299: if response.status_code == 204 or request['method'] == 'HEAD': # There is no body content for a HEAD request or a 204 response result = None elif not response.text: result = None else: try: result = response.json() except: result = response return DetailedResponse(result, response.headers, response.status_code) error_message = None if response.status_code == 401: error_message = 'Unauthorized: Access is denied due to ' \ 'invalid credentials' raise ApiException( response.status_code, error_message, http_response=response) except requests.exceptions.SSLError: logging.exception(self.ERROR_MSG_DISABLE_SSL) raise except ApiException as err: logging.exception(err.message) raise except: logging.exception('Error in service call') raise def prepare_request(self, method: str, url: str, headers: Optional[dict] = None, params: Optional[dict] = None, data: Optional[Union[str, dict]] = None, files: Optional[Union[ Dict[str, Tuple[str]], List[Tuple[str, Tuple[str, ...]]] ]] = None, **kwargs) -> dict: """Build a dict that represents an HTTP service request. Clean up headers, add default http configuration, convert data into json, process files, and merge all into a single request dict. Args: method: The HTTP method of the request ex. GET, POST, etc. url: The origin + pathname according to WHATWG spec. Keyword Arguments: headers: Headers of the request. params: Querystring data to be appended to the url. data: The request body. Converted to json if a dict. files: 'files' can be a dictionary (i.e { '': ()}), or a list of tuples [ (, ())... ] Returns: Prepared request dictionary. """ # pylint: disable=unused-argument; necessary for kwargs request = {'method': method} # validate the service url is set if not self.service_url: raise ValueError('The service_url is required') request['url'] = self.service_url + url headers = remove_null_values(headers) if headers else {} headers = cleanup_values(headers) headers = CaseInsensitiveDict(headers) if self.default_headers is not None: headers.update(self.default_headers) if 'user-agent' not in headers: headers.update(self.user_agent_header) request['headers'] = headers params = remove_null_values(params) params = cleanup_values(params) request['params'] = params if sys.version_info >= (3, 0) and isinstance(data, str): data = data.encode('utf-8') if data and isinstance(data, dict): data = remove_null_values(data) headers.update({'content-type': 'application/json'}) data = json_import.dumps(data) request['data'] = data self.authenticator.authenticate(request) # Next, we need to process the 'files' argument to try to fill in # any missing filenames where possible. # 'files' can be a dictionary (i.e { '': ()} ) # or a list of tuples [ (, ())... ] # If 'files' is a dictionary we'll convert it to a list of tuples. new_files = [] if files is not None: # If 'files' is a dictionary, transform it into a list of tuples. if isinstance(files, dict): files = remove_null_values(files) files = files.items() # Next, fill in any missing filenames from file tuples. for part_name, file_tuple in files: if file_tuple and len(file_tuple) == 3 and file_tuple[0] is None: file = file_tuple[1] if file and hasattr(file, 'name'): filename = basename(file.name) file_tuple = (filename, file_tuple[1], file_tuple[2]) new_files.append((part_name, file_tuple)) request['files'] = new_files return request @staticmethod def encode_path_vars(*args: str) -> List[str]: """Encode path variables to be substituted into a URL path. Arguments: args: A list of strings to be URL path encoded Returns: A list of encoded strings that are safe to substitute into a URL path. """ return (requests.utils.quote(x, safe='') for x in args) # The methods below are kept for compatibility and should be removed # in the next major release. # pylint: disable=protected-access @staticmethod def _convert_model(val): if isinstance(val, str): val = json_import.loads(val) if hasattr(val, "_to_dict"): return val._to_dict() return val @staticmethod def _convert_list(val): if isinstance(val, list): return ",".join(val) return val @staticmethod def _encode_path_vars(*args): return (requests.utils.quote(x, safe='') for x in args) ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/iam_token_manager.py0000664000372000037200000001462413621341247025574 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Dict, Optional from .jwt_token_manager import JWTTokenManager class IAMTokenManager(JWTTokenManager): """The IAMTokenManager takes an api key and performs the necessary interactions with the IAM token service to obtain and store a suitable bearer token. Additionally, the IAMTokenManager will retrieve bearer tokens via basic auth using a supplied client_id and client_secret pair. If the current stored bearer token has expired a new bearer token will be retrieved. Attributes: apikey: A generated API key from ibmcloud. url (str): The IAM endpoint to token requests. client_id (str): The client_id and client_secret fields are used to form a "basic auth" Authorization header for interactions with the IAM token server. client_secret (str): The client_id and client_secret fields are used to form a "basic auth" Authorization header for interactions with the IAM token server. headers (dict): Default headers to be sent with every IAM token request. proxies (dict): Proxies to use for communicating with IAM. proxies.http (str): The proxy endpoint to use for HTTP requests. proxies.https (str): The proxy endpoint to use for HTTPS requests. http_config (dict): A dictionary containing values that control the timeout, proxies, and etc of HTTP requests. Args: apikey: A generated APIKey from ibmcloud. Keyword Args: url: The IAM endpoint to token requests. Defaults to None. client_id: The client_id and client_secret fields are used to form a "basic auth" Authorization header for interactions with the IAM token server. Defaults to None. client_secret: The client_id and client_secret fields are used to form a "basic auth" Authorization header for interactions with the IAM token server. Defaults to None. disable_ssl_verification: A flag that indicates whether verification of the server's SSL certificate should be disabled or not. Defaults to False. headers: Default headers to be sent with every IAM token request. Defaults to None. proxies: Proxies to use for communicating with IAM. Defaults to None. proxies.http: The proxy endpoint to use for HTTP requests. proxies.https: The proxy endpoint to use for HTTPS requests. """ DEFAULT_IAM_URL = 'https://iam.cloud.ibm.com/identity/token' CONTENT_TYPE = 'application/x-www-form-urlencoded' REQUEST_TOKEN_GRANT_TYPE = 'urn:ibm:params:oauth:grant-type:apikey' REQUEST_TOKEN_RESPONSE_TYPE = 'cloud_iam' TOKEN_NAME = 'access_token' def __init__(self, apikey: str, url: Optional[str] = None, client_id: Optional[str] = None, client_secret: Optional[str] = None, disable_ssl_verification: Optional[str] = False, headers: Optional[Dict[str, str]] = None, proxies: Optional[Dict[str, str]] = None): self.apikey = apikey self.url = url if url else self.DEFAULT_IAM_URL self.client_id = client_id self.client_secret = client_secret self.headers = headers self.proxies = proxies super(IAMTokenManager, self).__init__( self.url, disable_ssl_verification, self.TOKEN_NAME) def request_token(self) -> dict: """Request an IAM OAuth token given an API Key. If client_id and client_secret are specified use their values as a user and pass auth set according to WHATWG url spec. Returns: A dictionary containing the bearer token to be subsequently used service requests. """ headers = { 'Content-type': self.CONTENT_TYPE, 'Accept': 'application/json' } if self.headers is not None and isinstance(self.headers, dict): headers.update(self.headers) data = { 'grant_type': self.REQUEST_TOKEN_GRANT_TYPE, 'apikey': self.apikey, 'response_type': self.REQUEST_TOKEN_RESPONSE_TYPE } auth_tuple = None # If both the client_id and secret were specified by the user, then use them if self.client_id and self.client_secret: auth_tuple = (self.client_id, self.client_secret) response = self._request( method='POST', url=self.url, headers=headers, data=data, auth_tuple=auth_tuple, proxies=self.proxies) return response def set_client_id_and_secret(self, client_id: str, client_secret: str): """Set the client_id and client_secret. Args: client_id: The client id to be used for token requests. client_secret: The client secret to be used for token requests. """ self.client_id = client_id self.client_secret = client_secret def set_headers(self, headers: Dict[str, str]): """Headers to be sent with every CP4D token request. Args: headers: Headers to be sent with every IAM token request. """ if isinstance(headers, dict): self.headers = headers else: raise TypeError('headers must be a dictionary') def set_proxies(self, proxies: Dict[str, str]): """Sets the proxies the token manager will use to communicate with IAM on behalf of the host. Args: proxies: Proxies to use for communicating with IAM. proxies.http (str, optional): The proxy endpoint to use for HTTP requests. proxies.https (str, optional): The proxy endpoint to use for HTTPS requests. """ if isinstance(proxies, dict): self.proxies = proxies else: raise TypeError('proxies must be a dictionary') ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/jwt_token_manager.py0000664000372000037200000001343513621341247025631 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time from typing import Optional import jwt import requests from .api_exception import ApiException class JWTTokenManager: """An abstract class to contain functionality for parsing, storing, and requesting JWT tokens. get_token will retrieve a new token from the url in case the that there is no existing token, or the previous token has expired. Child classes will implement request_token, which will do the actual acquisition of a new token. Args: url: The url to request tokens from. Keyword Args: disable_ssl_verification: A flag that indicates whether verification of the server's SSL certificate should be disabled or not. Defaults to False. token_name: The key that maps to the token in the dictionary returned from request_token. Defaults to None. Attributes: url (str): The url to request tokens from. disable_ssl_verification (bool): A flag that indicates whether verification of the server's SSL certificate should be disabled or not. token_name (str): The key used of the token in the dict returned from request_token. token_info (dict): The most token_response from request_token. time_for_new_token (int): The time in epoch seconds when the current token within token_info will expire. http_config (dict): A dictionary containing values that control the timeout, proxies, and etc of HTTP requests. """ def __init__(self, url: str, disable_ssl_verification: bool = False, token_name: Optional[str] = None): self.url = url self.disable_ssl_verification = disable_ssl_verification self.token_name = token_name self.token_info = {} self.time_for_new_token = None self.http_config = {} def get_token(self) -> str: """Get a token to be used for authentication. The source of the token is determined by the following logic: 1. a) If this class does not yet have one, make a request for one b) If this class token has expired, request a new one 2. If this class is managing tokens and has a valid token stored, send it Returns: str: A valid access token """ if not self.token_info or self._is_token_expired(): token_response = self.request_token() self._save_token_info(token_response) return self.token_info.get(self.token_name) def set_disable_ssl_verification(self, status: bool = False): """Sets the ssl verification to enabled or disabled. Args: status: the flag to be used for determining status. """ self.disable_ssl_verification = status def request_token(self): """Should be overridden by child classes. Raises: NotImplementedError: Thrown when called. """ raise NotImplementedError( 'request_token MUST be overridden by a subclass of JWTTokenManager.' ) @staticmethod def _get_current_time(): return int(time.time()) def _is_token_expired(self): """ Check if currently stored token is expired. Using a buffer to prevent the edge case of the token expiring before the request could be made. The buffer will be a fraction of the total TTL. Using 80%. Returns ------- bool If token expired or not """ if not self.time_for_new_token: return True current_time = self._get_current_time() return self.time_for_new_token < current_time def _save_token_info(self, token_response): """ Decode the access token and save the response from the JWT service to the object's state Parameters ---------- token_response : str Response from token service """ access_token = token_response.get(self.token_name) # The time of expiration is found by decoding the JWT access token decoded_response = jwt.decode(access_token, verify=False) exp = decoded_response.get('exp') iat = decoded_response.get('iat') # exp is the time of expire and iat is the time of token retrieval time_to_live = exp - iat expire_time = exp fraction_of_ttl = 0.8 self.time_for_new_token = expire_time - (time_to_live * (1.0 - fraction_of_ttl)) self.token_info = token_response def _request(self, method, url, headers=None, params=None, data=None, auth_tuple=None, **kwargs) -> dict: kwargs = dict({"timeout": 60}, **kwargs) kwargs = dict(kwargs, **self.http_config) if self.disable_ssl_verification: kwargs['verify'] = False response = requests.request( method=method, url=url, headers=headers, params=params, data=data, auth=auth_tuple, **kwargs) if 200 <= response.status_code <= 299: return response.json() raise ApiException(response.status_code, http_response=response) ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/cp4d_token_manager.py0000664000372000037200000000761413621341247025661 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Dict, Optional from .jwt_token_manager import JWTTokenManager class CP4DTokenManager(JWTTokenManager): """Token Manager of CloudPak for data. The Token Manager performs basic auth with a username and password to acquire JWT tokens. Args: username: The username for authentication. password: The password for authentication. url: The endpoint for JWT token requests. Keyword Arguments: disable_ssl_verification: Disable ssl verification. Defaults to False. headers: Headers to be sent with every service token request. Defaults to None. proxies: Proxies to use for making request. Defaults to None. proxies.http (optional): The proxy endpoint to use for HTTP requests. proxies.https (optional): The proxy endpoint to use for HTTPS requests. Attributes: username (str): The username for authentication. password (str): The password for authentication. url (str): The endpoint for JWT token requests. headers (dict): Headers to be sent with every service token request. proxies (dict): Proxies to use for making token requests. proxies.http (str): The proxy endpoint to use for HTTP requests. proxies.https (str): The proxy endpoint to use for HTTPS requests. """ TOKEN_NAME = 'accessToken' VALIDATE_AUTH_PATH = '/v1/preauth/validateAuth' def __init__(self, username: str, password: str, url: str, disable_ssl_verification: bool = False, headers: Optional[Dict[str, str]] = None, proxies: Optional[Dict[str, str]] = None): self.username = username self.password = password if url and not self.VALIDATE_AUTH_PATH in url: url = url + '/v1/preauth/validateAuth' self.headers = headers self.proxies = proxies super(CP4DTokenManager, self).__init__(url, disable_ssl_verification, self.TOKEN_NAME) def request_token(self) -> dict: """Makes a request for a token. """ auth_tuple = (self.username, self.password) response = self._request( method='GET', headers=self.headers, url=self.url, auth_tuple=auth_tuple, proxies=self.proxies) return response def set_headers(self, headers: Dict[str, str]): """Headers to be sent with every CP4D token request. Args: headers: The headers to be sent with every CP4D token request. """ if isinstance(headers, dict): self.headers = headers else: raise TypeError('headers must be a dictionary') def set_proxies(self, proxies: Dict[str, str]): """Sets the proxies the token manager will use to communicate with CP4D on behalf of the host. Args: proxies: Proxies to use for making request. Defaults to None. proxies.http (optional): The proxy endpoint to use for HTTP requests. proxies.https (optional): The proxy endpoint to use for HTTPS requests. """ if isinstance(proxies, dict): self.proxies = proxies else: raise TypeError('proxies must be a dictionary') ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/get_authenticator.py0000664000372000037200000000513713621341247025644 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from .authenticators import (Authenticator, BasicAuthenticator, BearerTokenAuthenticator, CloudPakForDataAuthenticator, IAMAuthenticator, NoAuthAuthenticator) from .utils import read_external_sources def get_authenticator_from_environment(service_name: str) -> Authenticator: """Look for external configuration of authenticator. Try to get authenticator from external sources, with the following priority: 1. Credentials file(ibm-credentials.env) 2. Environment variables 3. VCAP Services(Cloud Foundry) Args: service_name: The service name. Returns: The authenticator found from service information. """ authenticator = None config = read_external_sources(service_name) if config: authenticator = __construct_authenticator(config) return authenticator def __construct_authenticator(config): auth_type = config.get('AUTH_TYPE').lower() if config.get('AUTH_TYPE') else 'iam' authenticator = None if auth_type == 'basic': authenticator = BasicAuthenticator( username=config.get('USERNAME'), password=config.get('PASSWORD')) elif auth_type == 'bearertoken': authenticator = BearerTokenAuthenticator( bearer_token=config.get('BEARER_TOKEN')) elif auth_type == 'cp4d': authenticator = CloudPakForDataAuthenticator( username=config.get('USERNAME'), password=config.get('PASSWORD'), url=config.get('AUTH_URL'), disable_ssl_verification=config.get('AUTH_DISABLE_SSL')) elif auth_type == 'iam' and config.get('APIKEY'): authenticator = IAMAuthenticator( apikey=config.get('APIKEY'), url=config.get('AUTH_URL'), client_id=config.get('CLIENT_ID'), client_secret=config.get('CLIENT_SECRET'), disable_ssl_verification=config.get('AUTH_DISABLE_SSL')) elif auth_type == 'noauth': authenticator = NoAuthAuthenticator() return authenticator ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/0000775000372000037200000000000013621341546024612 5ustar travistravis00000000000000ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/basic_authenticator.py0000664000372000037200000000566213621341247031206 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import base64 from requests import Request from .authenticator import Authenticator from ..utils import has_bad_first_or_last_char class BasicAuthenticator(Authenticator): """The BasicAuthenticator is used to add basic authentication information to requests. Basic Authorization will be sent as an Authorization header in the form: Authorization: Basic Args: username: User-supplied username for basic auth. password: User-supplied password for basic auth. Raises: ValueError: The username or password is not specified or contains invalid characters. """ authentication_type = 'basic' def __init__(self, username: str, password: str): self.username = username self.password = password self.validate() self.authorization_header = self.__construct_basic_auth_header() def validate(self): """Validate username and password. Ensure the username and password are valid for service operations. Raises: ValueError: The username and/or password is not valid for service operations. """ if self.username is None or self.password is None: raise ValueError('The username and password shouldn\'t be None.') if has_bad_first_or_last_char( self.username) or has_bad_first_or_last_char(self.password): raise ValueError( 'The username and password shouldn\'t start or end with curly brackets or quotes. ' 'Please remove any surrounding {, }, or \" characters.') def __construct_basic_auth_header(self): authstring = "{0}:{1}".format(self.username, self.password) base64_authorization = base64.b64encode(authstring.encode('utf-8')).decode('utf-8') return 'Basic {0}'.format(base64_authorization) def authenticate(self, req: Request): """Add basic authentication information to a request. Basic Authorization will be added to the request's headers in the form: Authorization: Basic Args: req: The request to add basic auth information too. Must contain a key to a dictionary called headers. """ headers = req.get('headers') headers['Authorization'] = self.authorization_header ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/iam_authenticator.py0000664000372000037200000001431213621341247030663 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Dict, Optional from requests import Request from .authenticator import Authenticator from ..iam_token_manager import IAMTokenManager from ..utils import has_bad_first_or_last_char class IAMAuthenticator(Authenticator): """The IAMAuthenticator utilizes an apikey, or client_id and client_secret pair to obtain a suitable bearer token, and adds it to requests. The bearer token will be sent as an Authorization header in the form: Authorization: Bearer Args: apikey: The IAM api key. Keyword Args: url: The URL representing the IAM token service endpoint. If not specified, a suitable default value is used. client_id: The client_id and client_secret fields are used to form a "basic" authorization header for IAM token requests. Defaults to None. client_secret: The client_id and client_secret fields are used to form a "basic" authorization header for IAM token requests. Defaults to None. disable_ssl_verification: A flag that indicates whether verification of the server's SSL certificate should be disabled or not. Defaults to False. headers: Default headers to be sent with every IAM token request. Defaults to None. proxies: Dictionary for mapping request protocol to proxy URL. Defaults to None. proxies.http (optional): The proxy endpoint to use for HTTP requests. proxies.https (optional): The proxy endpoint to use for HTTPS requests. Attributes: token_manager (IAMTokenManager): Retrives and manages IAM tokens from the endpoint specified by the url. Raises: ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests. """ authentication_type = 'iam' def __init__(self, apikey: str, url: Optional[str] = None, client_id: Optional[str] = None, client_secret: Optional[str] = None, disable_ssl_verification: Optional[bool] = False, headers: Optional[Dict[str, str]] = None, proxies: Optional[Dict[str, str]] = None): self.token_manager = IAMTokenManager( apikey, url, client_id, client_secret, disable_ssl_verification, headers, proxies) self.validate() def validate(self): """Validates the apikey, client_id, and client_secret for IAM token requests. Ensure the apikey is not none, and has no bad characters. Additionally, ensure the both the client_id and client_secret are both set if either of them are defined. Raises: ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests. """ if self.token_manager.apikey is None: raise ValueError('The apikey shouldn\'t be None.') if has_bad_first_or_last_char(self.token_manager.apikey): raise ValueError( 'The apikey shouldn\'t start or end with curly brackets or quotes. ' 'Please remove any surrounding {, }, or \" characters.') if (self.token_manager.client_id and not self.token_manager.client_secret) or ( not self.token_manager.client_id and self.token_manager.client_secret): raise ValueError( 'Both client_id and client_secret should be initialized.') def authenticate(self, req: Request): """Adds IAM authentication information to the request. The IAM bearer token will be added to the request's headers in the form: Authorization: Bearer Args: req: The request to add IAM authentication information too. Must contain a key to a dictionary called headers. """ headers = req.get('headers') bearer_token = self.token_manager.get_token() headers['Authorization'] = 'Bearer {0}'.format(bearer_token) def set_client_id_and_secret(self, client_id: str, client_secret: str): """Set the client_id and client_secret pair the token manager will use for IAM token requests. Args: client_id: The client id to be used in basic auth. client_secret: The client secret to be used in basic auth. Raises: ValueError: The apikey, client_id, and/or client_secret are not valid for IAM token requests. """ self.token_manager.set_client_id_and_secret(client_id, client_secret) self.validate() def set_disable_ssl_verification(self, status: bool = False): """Set the flag that indicates whether verification of the server's SSL certificate should be disabled or not. Defaults to False. Keyword Arguments: status: Headers to be sent with every IAM token request. Defaults to None. """ self.token_manager.set_disable_ssl_verification(status) def set_headers(self, headers: Dict[str, str]): """Headers to be sent with every IAM token request. Args: headers: Headers to be sent with every IAM token request. """ self.token_manager.set_headers(headers) def set_proxies(self, proxies: Dict[str, str]): """Sets the proxies the token manager will use to communicate with IAM on behalf of the host. Args: proxies: Dictionary for mapping request protocol to proxy URL. proxies.http (optional): The proxy endpoint to use for HTTP requests. proxies.https (optional): The proxy endpoint to use for HTTPS requests. """ self.token_manager.set_proxies(proxies) ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py0000664000372000037200000001257113621341247030754 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Dict, Optional from requests import Request from .authenticator import Authenticator from ..cp4d_token_manager import CP4DTokenManager from ..utils import has_bad_first_or_last_char class CloudPakForDataAuthenticator(Authenticator): """The CloudPakForDataAuthenticator utilizes a username and password pair to obtain a suitable bearer token, and adds it requests. The bearer token will be sent as an Authorization header in the form: Authorization: Bearer Args: username: The username used to obtain a bearer token. password: The password used to obtain a bearer token. url: The URL representing the Cloud Pak for Data token service endpoint. Keyword Args: disable_ssl_verification: A flag that indicates whether verification of the server's SSL certificate should be disabled or not. Defaults to False. headers: Default headers to be sent with every CP4D token request. Defaults to None. proxies: Dictionary for mapping request protocol to proxy URL. proxies.http (optional): The proxy endpoint to use for HTTP requests. proxies.https (optional): The proxy endpoint to use for HTTPS requests. Attributes: token_manager (CP4DTokenManager): Retrives and manages CP4D tokens from the endpoint specified by the url. Raises: ValueError: The username, password, and/or url are not valid for CP4D token requests. """ authenticationdict = 'cp4d' def __init__(self, username: str, password: str, url: str, disable_ssl_verification: bool = False, headers: Optional[Dict[str, str]] = None, proxies: Optional[Dict[str, str]] = None): self.token_manager = CP4DTokenManager( username, password, url, disable_ssl_verification, headers, proxies) self.validate() def validate(self): """Validate username, password, and url for token requests. Ensures the username, password, and url are not None. Additionally, ensures they do not contain invalid characters. Raises: ValueError: The username, password, and/or url are not valid for token requests. """ if self.token_manager.username is None or self.token_manager.password is None: raise ValueError('The username and password shouldn\'t be None.') if self.token_manager.url is None: raise ValueError('The url shouldn\'t be None.') if has_bad_first_or_last_char( self.token_manager.username) or has_bad_first_or_last_char(self.token_manager.password): raise ValueError( 'The username and password shouldn\'t start or end with curly brackets or quotes. ' 'Please remove any surrounding {, }, or \" characters.') if has_bad_first_or_last_char(self.token_manager.url): raise ValueError( 'The url shouldn\'t start or end with curly brackets or quotes. ' 'Please remove any surrounding {, }, or \" characters.') def authenticate(self, req: Request): """Adds CP4D authentication information to the request. The CP4D bearer token will be added to the request's headers in the form: Authorization: Bearer Args: req: The request to add CP4D authentication information too. Must contain a key to a dictionary called headers. """ headers = req.get('headers') bearer_token = self.token_manager.get_token() headers['Authorization'] = 'Bearer {0}'.format(bearer_token) def set_disable_ssl_verification(self, status: bool = False): """Set the flag that indicates whether verification of the server's SSL certificate should be disabled or not. Defaults to False. Args: status: Set to true in order to disable SSL certificate verification. Defaults to False. """ self.token_manager.set_disable_ssl_verification(status) def set_headers(self, headers: Dict[str, str]): """Default headers to be sent with every CP4D token request. Args: headers: The headers to be sent with every CP4D token request. """ self.token_manager.set_headers(headers) def set_proxies(self, proxies: Dict[str, str]): """Sets the proxies the token manager will use to communicate with CP4D on behalf of the host. Args: proxies: Dictionary for mapping request protocol to proxy URL. proxies.http (optional): The proxy endpoint to use for HTTP requests. proxies.https (optional): The proxy endpoint to use for HTTPS requests. """ self.token_manager.set_proxies(proxies) ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/authenticator.py0000664000372000037200000000225013621341247030033 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from abc import ABC, abstractmethod class Authenticator(ABC): """This interface defines the common methods and constants associated with an Authenticator implementation.""" @abstractmethod def authenticate(self, req: dict): """Perform the necessary authentication steps for the specified request. To be implemented by subclasses. """ pass @abstractmethod def validate(self): """Validates the current set of configuration information in the Authenticator. To be implemented by subclasses. """ pass ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py0000664000372000037200000000465113621341247032562 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from requests import Request from .authenticator import Authenticator class BearerTokenAuthenticator(Authenticator): """The BearerTokenAuthenticator will add a user-supplied bearer token to requests. The bearer token will be sent as an Authorization header in the form: Authorization: Bearer Args: bearer_token: The user supplied bearer token. Raises: ValueError: Bearer token is none. """ authentication_type = 'bearerToken' def __init__(self, bearer_token: str): self.bearer_token = bearer_token self.validate() def validate(self): """Validate the bearer token. Ensures the bearer token is valid for service operations. Raises: ValueError: The bearer token is not valid for service operations. """ if self.bearer_token is None: raise ValueError('The bearer token shouldn\'t be None.') def authenticate(self, req: Request): """Adds bearer authentication information to the request. The bearer token will be added to the request's headers in the form: Authorization: Bearer Args: req: The request to add bearer authentication information too. Must contain a key to a dictionary called headers. """ headers = req.get('headers') headers['Authorization'] = 'Bearer {0}'.format(self.bearer_token) def set_bearer_token(self, bearer_token: str): """Set a new bearer token to be sent in subsequent service operations. Args: bearer_token: The bearer token that will be sent in service requests. Raises: ValueError: The bearer token is not valid for service operations. """ self.bearer_token = bearer_token self.validate() ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/__init__.py0000664000372000037200000000364613621341247026732 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """The ibm_cloud_sdk_core project supports the following types of authentication: Basic Authentication Bearer Token Identity and Access Management (IAM) Cloud Pak for Data No Authentication The authentication types that are appropriate for a particular service may vary from service to service. Each authentication type is implemented as an Authenticator for consumption by a service. classes: Authenticator: Abstract Base Class. Implement this interface to provide custom authentication schemes to services. BasicAuthenticator: Authenticator for passing supplied basic authentication information to service endpoint. BearerTokenAuthenticator: Authenticator for passing supplied bearer token to service endpoint. CloudPakForDataAuthenticator: Authenticator for passing CP4D authentication information to service endpoint. IAMAuthenticator: Authenticator for passing IAM authentication information to service endpoint. NoAuthAuthenticator: Performs no authentication. Useful for testing purposes. """ from .authenticator import Authenticator from .basic_authenticator import BasicAuthenticator from .bearer_token_authenticator import BearerTokenAuthenticator from .cp4d_authenticator import CloudPakForDataAuthenticator from .iam_authenticator import IAMAuthenticator from .no_auth_authenticator import NoAuthAuthenticator ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/authenticators/no_auth_authenticator.py0000664000372000037200000000152113621341247031550 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from .authenticator import Authenticator class NoAuthAuthenticator(Authenticator): """Performs no authentication.""" authentication_type = 'noAuth' def validate(self): pass def authenticate(self, req): pass ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/utils.py0000664000372000037200000002265713621341247023301 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # from ibm_cloud_sdk_core.authenticators import Authenticator import datetime import json as json_import from os import getenv, environ, getcwd from os.path import isfile, join, expanduser from typing import List, Union import dateutil.parser as date_parser def has_bad_first_or_last_char(val: str) -> bool: """Returns true if a string starts with any of: {," ; or ends with any of: },". Args: val: The string to be tested. Returns: Whether or not the string starts or ends with bad characters. """ return val is not None and (val.startswith('{') or val.startswith('"') or val.endswith('}') or val.endswith('"')) def remove_null_values(dictionary: dict) -> dict: """Create a new dictionary without keys mapped to null values. Args: dictionary: The dictionary potentially containing keys mapped to values of None. Returns: A dict with no keys mapped to None. """ if isinstance(dictionary, dict): return {k: v for (k, v) in dictionary.items() if v is not None} return dictionary def cleanup_values(dictionary: dict) -> dict: """Create a new dictionary with boolean values converted to strings. Ex. true -> 'true', false -> 'false'. { 'key': true } -> { 'key': 'true' } Args: dictionary: The dictionary with keys mapped to booleans. Returns: The dictionary with certain keys mapped to s and not booleans. """ if isinstance(dictionary, dict): return {k: cleanup_value(v) for (k, v) in dictionary.items()} return dictionary def cleanup_value(value: any) -> any: """Convert a boolean value to string.""" if isinstance(value, bool): return 'true' if value else 'false' return value def datetime_to_string(val: datetime.datetime) -> str: """Convert a datetime object to string. If the supplied datetime does not specify a timezone, it is assumed to be UTC. Args: val: The datetime object. Returns: datetime serialized to iso8601 format. """ if isinstance(val, datetime.datetime): if val.tzinfo is None: return val.isoformat() + 'Z' val = val.astimezone(datetime.timezone.utc) return val.isoformat().replace('+00:00', 'Z') return val def string_to_datetime(string: str) -> datetime.datetime: """De-serializes string to datetime. Args: string: string containing datetime in iso8601 format. Returns: the de-serialized string as a datetime object. """ val = date_parser.parse(string) if val.tzinfo is not None: return val return val.replace(tzinfo=datetime.timezone.utc) def date_to_string(val: datetime.date) -> str: """Convert a date object to string. Args: val: The date object. Returns: date serialized to `YYYY-MM-DD` format. """ if isinstance(val, datetime.date): return str(val) return val def string_to_date(string: str) -> datetime.date: """De-serializes string to date. Args: string: string containing date in 'YYYY-MM-DD' format. Returns: the de-serialized string as a date object. """ return date_parser.parse(string).date() def convert_model(val: any) -> dict: """Convert a model object into an equivalent dict. Arguments: val: A dict or a model object Returns: A dict representation of the input object. """ if isinstance(val, dict): return val if hasattr(val, "to_dict"): return val.to_dict() # Consider raising a ValueError here in the next major release return val def convert_list(val: Union[str, List[str]]) -> str: """Convert a list of strings into comma-separated string. Arguments: val: A string or list of strings Returns: A comma-separated string of the items in the input list. """ if isinstance(val, str): return val if isinstance(val, list) and all(isinstance(x, str) for x in val): return ",".join(val) # Consider raising a ValueError here in the next major release return val def read_external_sources(service_name: str) -> dict: """Look for external configuration of a service. Try to get config from external sources, with the following priority: 1. Credentials file(ibm-credentials.env) 2. Environment variables 3. VCAP Services(Cloud Foundry) Args: service_name: The service name Returns: A dictionary containing relevant configuration for the service if found. """ config = {} config = __read_from_credential_file(service_name) if not config: config = __read_from_env_variables(service_name) if not config: config = __read_from_vcap_services(service_name) return config def __read_from_env_variables(service_name: str) -> dict: """Return a config object based on environment variables for a service. Args: service_name: The name of the service to look for in env variables. Returns: A set of service configuration key-value pairs. """ config = {} for key, value in environ.items(): _parse_key_and_update_config(config, service_name, key, value) return config def __read_from_credential_file(service_name: str, separator: str = '=') -> dict: """Return a config object based on credentials file for a service. Args: service_name: The name of the service to look for in env variables. Keyword Args: separator: The character to split on to de-serialize a line into a key-value pair. Returns: A set of service configuration key-value pairs. """ default_credentials_file_name = 'ibm-credentials.env' # File path specified by an env variable credential_file_path = getenv('IBM_CREDENTIALS_FILE') # Current working directory if credential_file_path is None: file_path = join( getcwd(), default_credentials_file_name) if isfile(file_path): credential_file_path = file_path # Home directory if credential_file_path is None: file_path = join(expanduser('~'), default_credentials_file_name) if isfile(file_path): credential_file_path = file_path config = {} if credential_file_path is not None: with open(credential_file_path, 'r') as fobj: for line in fobj: key_val = line.strip().split(separator) if len(key_val) == 2: key = key_val[0] value = key_val[1] _parse_key_and_update_config(config, service_name, key, value) return config def _parse_key_and_update_config(config, service_name, key, value): service_name = service_name.replace(' ', '_').replace('-', '_').upper() if key.startswith(service_name): config[key[len(service_name) + 1:]] = value def __read_from_vcap_services(service_name: str) -> dict: """Return a config object based on the vcap services environment variable. Args: service_name: The name of the service to look for in the vcap. Returns: A set of service configuration key-value pairs. """ vcap_services = getenv('VCAP_SERVICES') vcap_service_credentials = {} if vcap_services: services = json_import.loads(vcap_services) for key in services.keys(): for i in range(len(services[key])): if vcap_service_credentials and isinstance(vcap_service_credentials, dict): break if services[key][i].get('name') == service_name: vcap_service_credentials = services[key][i].get('credentials', {}) if not vcap_service_credentials: if service_name in services.keys(): service = services.get(service_name) if service: vcap_service_credentials = service[0].get('credentials', {}) if vcap_service_credentials and isinstance(vcap_service_credentials, dict): new_vcap_creds = {} if vcap_service_credentials.get('username') and vcap_service_credentials.get('password'): # cf new_vcap_creds['AUTH_TYPE'] = 'basic' new_vcap_creds['USERNAME'] = vcap_service_credentials.get('username') new_vcap_creds['PASSWORD'] = vcap_service_credentials.get('password') vcap_service_credentials = new_vcap_creds elif vcap_service_credentials.get('iam_apikey'): new_vcap_creds['AUTH_TYPE'] = 'iam' new_vcap_creds['APIKEY'] = vcap_service_credentials.get('iam_apikey') vcap_service_credentials = new_vcap_creds elif vcap_service_credentials.get('apikey'): new_vcap_creds['AUTH_TYPE'] = 'iam' new_vcap_creds['APIKEY'] = vcap_service_credentials.get('apikey') vcap_service_credentials = new_vcap_creds return vcap_service_credentials ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/version.py0000664000372000037200000000002613621341247023610 0ustar travistravis00000000000000__version__ = '1.6.2' ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/detailed_response.py0000664000372000037200000000520713621341247025622 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Dict, Optional import json import requests class DetailedResponse: """Custom class for detailed response returned from APIs. Keyword Args: response: The response to the service request, defaults to None. headers: The headers of the response, defaults to None. status_code: The status code of there response, defaults to None. Attributes: response (requests.Response): The response to the service request. headers (dict): The headers of the response. status_code (int): The status code of the response. """ def __init__(self, response: Optional[requests.Response] = None, headers: Optional[Dict[str, str]] = None, status_code: Optional[int] = None): self.result = response self.headers = headers self.status_code = status_code def get_result(self) -> requests.Response: """Get the HTTP response of the service request. Returns: The response to the service request """ return self.result def get_headers(self) -> dict: """The HTTP response headers of the service request. Returns: A dictionary of response headers """ return self.headers def get_status_code(self) -> int: """The HTTP status code of the service request. Returns: The status code of the service request. """ return self.status_code def _to_dict(self): _dict = {} if hasattr(self, 'result') and self.result is not None: _dict['result'] = self.result if isinstance(self.result, (dict, list)) else 'HTTP response' if hasattr(self, 'headers') and self.headers is not None: _dict['headers'] = self.headers if hasattr(self, 'status_code') and self.status_code is not None: _dict['status_code'] = self.status_code return _dict def __str__(self): return json.dumps(self._to_dict(), indent=4, default=lambda o: o.__dict__) ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/__init__.py0000664000372000037200000000432213621341247023665 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Classes and helper functions used by generated SDKs. classes: BaseService: Abstract class for common functionality between each service. DetailedResponse: The object returned from successful service operations. IAMTokenManager: Requests and refreshes IAM tokens using an apikey, and optionally a client_id and client_secret. JWTTokenManager: Abstract class for common functionality between each JWT token manager. CP4DTokenManager: Requests and refreshes CP4D tokens given a username and password. ApiException: Custom exception class for errors returned from service operations. functions: datetime_to_string: Serializes a datetime to a string. string_to_datetime: De-serializes a string to a datetime. date_to_string: Serializes a date to a string. string_to_date: De-serializes a string to a date. convert_model: Convert a model object into an equivalent dict. convert_list: Convert a list of strings into comma-separated string. read_external_sources: Get config object from external sources. get_authenticator_from_environment: Get authenticator from external sources. """ from .base_service import BaseService from .detailed_response import DetailedResponse from .iam_token_manager import IAMTokenManager from .jwt_token_manager import JWTTokenManager from .cp4d_token_manager import CP4DTokenManager from .api_exception import ApiException from .utils import datetime_to_string, string_to_datetime, read_external_sources from .utils import date_to_string, string_to_date from .utils import convert_model, convert_list from .get_authenticator import get_authenticator_from_environment ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core/api_exception.py0000664000372000037200000000554313621341247024763 0ustar travistravis00000000000000# coding: utf-8 # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Optional from requests import Response class ApiException(Exception): """Custom exception class for errors returned from operations. Args: code: HTTP status code of the error response. message: The error response body. Defaults to None. http_response: The HTTP response of the failed request. Defaults to None. Attributes: code (int): HTTP status code of the error response. message (str): The error response body. http_response (requests.Response): The HTTP response of the failed request. global_transaction_id (str, optional): Globally unique id the service endpoint has given a transaction. """ def __init__(self, code: int, message: Optional[str] = None, http_response: Optional[Response] = None): # Call the base class constructor with the parameters it needs super(ApiException, self).__init__(message) self.message = message self.code = code self.http_response = http_response self.global_transaction_id = None if http_response is not None: self.global_transaction_id = http_response.headers.get('X-Global-Transaction-ID') self.message = self.message if self.message else self._get_error_message(http_response) def __str__(self): msg = 'Error: ' + str(self.message) + ', Code: ' + str(self.code) if self.global_transaction_id is not None: msg += ' , X-global-transaction-id: ' + str(self.global_transaction_id) return msg @staticmethod def _get_error_message(response: Response): error_message = 'Unknown error' try: error_json = response.json() if 'errors' in error_json: if isinstance(error_json['errors'], list): err = error_json['errors'][0] error_message = err.get('message') elif 'error' in error_json: error_message = error_json['error'] elif 'message' in error_json: error_message = error_json['message'] elif 'errorMessage' in error_json: error_message = error_json['errorMessage'] return error_message except: return response.text or error_message ibm-cloud-sdk-core-1.6.2/PKG-INFO0000664000372000037200000001226313621341546017050 0ustar travistravis00000000000000Metadata-Version: 1.1 Name: ibm-cloud-sdk-core Version: 1.6.2 Summary: Client library for the IBM Cloud services Home-page: https://github.com/IBM/python-sdk-core Author: Erika Dsouza Author-email: erika.dsouza@ibm.com License: Apache 2.0 Description: |Build Status| |codecov| |Latest Stable Version| |CLA assistant| python-sdk-core =============== This project contains the core functionality used by Python SDK's generated by the IBM OpenAPI 3 SDK Generator (openapi-sdkgen). Python code generated by openapi-sdkgen will depend on the function contained in this project. Notice ====== Support for Python versions 2.x and versions <= 3.4 is deprecated and will be officially dropped in the next major release, which is expected to be end of December, 2019. Installation ------------ To install, use ``pip`` or ``easy_install``: .. code:: bash pip install --upgrade ibm-cloud-sdk-core or .. code:: bash easy_install --upgrade ibm-cloud-sdk-core Authentication -------------- The python-sdk-core project supports the following types of authentication: - Basic Authentication - Bearer Token - Identity and Access Management (IAM) - Cloud Pak for Data - No Authentication For more information about the various authentication types and how to use them with your services, click `here `__ Issues ------ If you encounter an issue with this project, you are welcome to submit a `bug report `__. Before opening a new issue, please search for similar issues. It's possible that someone has already reported it. Logging ------- Enable logging ~~~~~~~~~~~~~~ .. code:: python import logging logging.basicConfig(level=logging.DEBUG) This would show output of the form: :: DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): iam.cloud.ibm.com:443 DEBUG:urllib3.connectionpool:https://iam.cloud.ibm.com:443 "POST /identity/token HTTP/1.1" 200 1809 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "POST /assistant/api/v1/workspaces?version=2018-07-10 HTTP/1.1" 201 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "GET /assistant/api/v1/workspaces/883a2a44-eb5f-4b1a-96b0-32a90b475ea8?version=2018-07-10&export=true HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "DELETE /assistant/api/v1/workspaces/883a2a44-eb5f-4b1a-96b0-32a90b475ea8?version=2018-07-10 HTTP/1.1" 200 28 Low level request and response dump ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To get low level information of the requests/ responses: .. code:: python from http.client import HTTPConnection HTTPConnection.debuglevel = 1 Open source @ IBM ----------------- Find more open source projects on the `IBM Github Page `__ License ------- This library is licensed under Apache 2.0. Full license text is available in `LICENSE `__. Contributing ------------ See `CONTRIBUTING.md `__. .. |Build Status| image:: https://travis-ci.com/IBM/python-sdk-core.svg?branch=master :target: https://travis-ci.com/IBM/python-sdk-core .. |codecov| image:: https://codecov.io/gh/IBM/python-sdk-core/branch/master/graph/badge.svg :target: https://codecov.io/gh/IBM/python-sdk-core .. |Latest Stable Version| image:: https://img.shields.io/pypi/v/ibm-cloud-sdk-core.svg :target: https://pypi.python.org/pypi/ibm-cloud-sdk-core .. |CLA assistant| image:: https://cla-assistant.io/readme/badge/ibm/python-sdk-core :target: https://cla-assistant.io/ibm/python-sdk-core Keywords: watson,ibm,cloud Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Libraries :: Application Frameworks ibm-cloud-sdk-core-1.6.2/README.md0000664000372000037200000000632513621341247017232 0ustar travistravis00000000000000[![Build Status](https://travis-ci.com/IBM/python-sdk-core.svg?branch=master)](https://travis-ci.com/IBM/python-sdk-core) [![codecov](https://codecov.io/gh/IBM/python-sdk-core/branch/master/graph/badge.svg)](https://codecov.io/gh/IBM/python-sdk-core) [![Latest Stable Version](https://img.shields.io/pypi/v/ibm-cloud-sdk-core.svg)](https://pypi.python.org/pypi/ibm-cloud-sdk-core) [![CLA assistant](https://cla-assistant.io/readme/badge/ibm/python-sdk-core)](https://cla-assistant.io/ibm/python-sdk-core) # python-sdk-core This project contains the core functionality used by Python SDK's generated by the IBM OpenAPI 3 SDK Generator (openapi-sdkgen). Python code generated by openapi-sdkgen will depend on the function contained in this project. # Notice Support for Python versions 2.x and versions <= 3.4 is deprecated and will be officially dropped in the next major release, which is expected to be end of December, 2019. ## Installation To install, use `pip` or `easy_install`: ```bash pip install --upgrade ibm-cloud-sdk-core ``` or ```bash easy_install --upgrade ibm-cloud-sdk-core ``` ## Authentication The python-sdk-core project supports the following types of authentication: - Basic Authentication - Bearer Token - Identity and Access Management (IAM) - Cloud Pak for Data - No Authentication For more information about the various authentication types and how to use them with your services, click [here](Authentication.md) ## Issues If you encounter an issue with this project, you are welcome to submit a [bug report](https://github.com/IBM/python-sdk-core/issues). Before opening a new issue, please search for similar issues. It's possible that someone has already reported it. ## Logging ### Enable logging ```python import logging logging.basicConfig(level=logging.DEBUG) ``` This would show output of the form: ``` DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): iam.cloud.ibm.com:443 DEBUG:urllib3.connectionpool:https://iam.cloud.ibm.com:443 "POST /identity/token HTTP/1.1" 200 1809 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "POST /assistant/api/v1/workspaces?version=2018-07-10 HTTP/1.1" 201 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "GET /assistant/api/v1/workspaces/883a2a44-eb5f-4b1a-96b0-32a90b475ea8?version=2018-07-10&export=true HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "DELETE /assistant/api/v1/workspaces/883a2a44-eb5f-4b1a-96b0-32a90b475ea8?version=2018-07-10 HTTP/1.1" 200 28 ``` ### Low level request and response dump To get low level information of the requests/ responses: ```python from http.client import HTTPConnection HTTPConnection.debuglevel = 1 ``` ## Open source @ IBM Find more open source projects on the [IBM Github Page](http://github.com/IBM) ## License This library is licensed under Apache 2.0. Full license text is available in [LICENSE](LICENSE). ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md).ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core.egg-info/0000775000372000037200000000000013621341546023247 5ustar travistravis00000000000000ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core.egg-info/PKG-INFO0000664000372000037200000001226313621341546024350 0ustar travistravis00000000000000Metadata-Version: 1.1 Name: ibm-cloud-sdk-core Version: 1.6.2 Summary: Client library for the IBM Cloud services Home-page: https://github.com/IBM/python-sdk-core Author: Erika Dsouza Author-email: erika.dsouza@ibm.com License: Apache 2.0 Description: |Build Status| |codecov| |Latest Stable Version| |CLA assistant| python-sdk-core =============== This project contains the core functionality used by Python SDK's generated by the IBM OpenAPI 3 SDK Generator (openapi-sdkgen). Python code generated by openapi-sdkgen will depend on the function contained in this project. Notice ====== Support for Python versions 2.x and versions <= 3.4 is deprecated and will be officially dropped in the next major release, which is expected to be end of December, 2019. Installation ------------ To install, use ``pip`` or ``easy_install``: .. code:: bash pip install --upgrade ibm-cloud-sdk-core or .. code:: bash easy_install --upgrade ibm-cloud-sdk-core Authentication -------------- The python-sdk-core project supports the following types of authentication: - Basic Authentication - Bearer Token - Identity and Access Management (IAM) - Cloud Pak for Data - No Authentication For more information about the various authentication types and how to use them with your services, click `here `__ Issues ------ If you encounter an issue with this project, you are welcome to submit a `bug report `__. Before opening a new issue, please search for similar issues. It's possible that someone has already reported it. Logging ------- Enable logging ~~~~~~~~~~~~~~ .. code:: python import logging logging.basicConfig(level=logging.DEBUG) This would show output of the form: :: DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): iam.cloud.ibm.com:443 DEBUG:urllib3.connectionpool:https://iam.cloud.ibm.com:443 "POST /identity/token HTTP/1.1" 200 1809 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "POST /assistant/api/v1/workspaces?version=2018-07-10 HTTP/1.1" 201 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "GET /assistant/api/v1/workspaces/883a2a44-eb5f-4b1a-96b0-32a90b475ea8?version=2018-07-10&export=true HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): gateway.watsonplatform.net:443 DEBUG:urllib3.connectionpool:https://gateway.watsonplatform.net:443 "DELETE /assistant/api/v1/workspaces/883a2a44-eb5f-4b1a-96b0-32a90b475ea8?version=2018-07-10 HTTP/1.1" 200 28 Low level request and response dump ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To get low level information of the requests/ responses: .. code:: python from http.client import HTTPConnection HTTPConnection.debuglevel = 1 Open source @ IBM ----------------- Find more open source projects on the `IBM Github Page `__ License ------- This library is licensed under Apache 2.0. Full license text is available in `LICENSE `__. Contributing ------------ See `CONTRIBUTING.md `__. .. |Build Status| image:: https://travis-ci.com/IBM/python-sdk-core.svg?branch=master :target: https://travis-ci.com/IBM/python-sdk-core .. |codecov| image:: https://codecov.io/gh/IBM/python-sdk-core/branch/master/graph/badge.svg :target: https://codecov.io/gh/IBM/python-sdk-core .. |Latest Stable Version| image:: https://img.shields.io/pypi/v/ibm-cloud-sdk-core.svg :target: https://pypi.python.org/pypi/ibm-cloud-sdk-core .. |CLA assistant| image:: https://cla-assistant.io/readme/badge/ibm/python-sdk-core :target: https://cla-assistant.io/ibm/python-sdk-core Keywords: watson,ibm,cloud Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Libraries :: Application Frameworks ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core.egg-info/zip-safe0000664000372000037200000000000113621341304024667 0ustar travistravis00000000000000 ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core.egg-info/dependency_links.txt0000664000372000037200000000000113621341546027315 0ustar travistravis00000000000000 ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core.egg-info/top_level.txt0000664000372000037200000000003013621341546025772 0ustar travistravis00000000000000ibm_cloud_sdk_core test ibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core.egg-info/SOURCES.txt0000664000372000037200000000256013621341546025136 0ustar travistravis00000000000000README.md setup.py ibm_cloud_sdk_core/__init__.py ibm_cloud_sdk_core/api_exception.py ibm_cloud_sdk_core/base_service.py ibm_cloud_sdk_core/cp4d_token_manager.py ibm_cloud_sdk_core/detailed_response.py ibm_cloud_sdk_core/get_authenticator.py ibm_cloud_sdk_core/iam_token_manager.py ibm_cloud_sdk_core/jwt_token_manager.py ibm_cloud_sdk_core/utils.py ibm_cloud_sdk_core/version.py ibm_cloud_sdk_core.egg-info/PKG-INFO ibm_cloud_sdk_core.egg-info/SOURCES.txt ibm_cloud_sdk_core.egg-info/dependency_links.txt ibm_cloud_sdk_core.egg-info/requires.txt ibm_cloud_sdk_core.egg-info/top_level.txt ibm_cloud_sdk_core.egg-info/zip-safe ibm_cloud_sdk_core/authenticators/__init__.py ibm_cloud_sdk_core/authenticators/authenticator.py ibm_cloud_sdk_core/authenticators/basic_authenticator.py ibm_cloud_sdk_core/authenticators/bearer_token_authenticator.py ibm_cloud_sdk_core/authenticators/cp4d_authenticator.py ibm_cloud_sdk_core/authenticators/iam_authenticator.py ibm_cloud_sdk_core/authenticators/no_auth_authenticator.py test/__init__.py test/test_api_exception.py test/test_base_service.py test/test_basic_authenticator.py test/test_bearer_authenticator.py test/test_cp4d_authenticator.py test/test_cp4d_token_manager.py test/test_detailed_response.py test/test_iam_authenticator.py test/test_iam_token_manager.py test/test_jwt_token_manager.py test/test_no_auth_authenticator.py test/test_utils.pyibm-cloud-sdk-core-1.6.2/ibm_cloud_sdk_core.egg-info/requires.txt0000664000372000037200000000006713621341546025652 0ustar travistravis00000000000000requests<3.0,>=2.0 python_dateutil>=2.5.3 PyJWT>=1.7.1 ibm-cloud-sdk-core-1.6.2/setup.cfg0000664000372000037200000000004613621341546017570 0ustar travistravis00000000000000[egg_info] tag_build = tag_date = 0 ibm-cloud-sdk-core-1.6.2/test/0000775000372000037200000000000013621341546016726 5ustar travistravis00000000000000ibm-cloud-sdk-core-1.6.2/test/test_basic_authenticator.py0000664000372000037200000000274413621341247024357 0ustar travistravis00000000000000# pylint: disable=missing-docstring import pytest from ibm_cloud_sdk_core.authenticators import BasicAuthenticator def test_basic_authenticator(): authenticator = BasicAuthenticator('my_username', 'my_password') assert authenticator is not None assert authenticator.username == 'my_username' assert authenticator.password == 'my_password' request = {'headers': {}} authenticator.authenticate(request) assert request['headers']['Authorization'] == 'Basic bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=' def test_basic_authenticator_validate_failed(): with pytest.raises(ValueError) as err: BasicAuthenticator('my_username', None) assert str(err.value) == 'The username and password shouldn\'t be None.' with pytest.raises(ValueError) as err: BasicAuthenticator(None, 'my_password') assert str(err.value) == 'The username and password shouldn\'t be None.' with pytest.raises(ValueError) as err: BasicAuthenticator('{my_username}', 'my_password') assert str(err.value) == 'The username and password shouldn\'t start or end with curly brackets or quotes. '\ 'Please remove any surrounding {, }, or \" characters.' with pytest.raises(ValueError) as err: BasicAuthenticator('my_username', '{my_password}') assert str(err.value) == 'The username and password shouldn\'t start or end with curly brackets or quotes. '\ 'Please remove any surrounding {, }, or \" characters.' ibm-cloud-sdk-core-1.6.2/test/test_cp4d_authenticator.py0000664000372000037200000000775713621341247024141 0ustar travistravis00000000000000# pylint: disable=missing-docstring import json import pytest import responses import jwt from ibm_cloud_sdk_core.authenticators import CloudPakForDataAuthenticator def test_iam_authenticator(): authenticator = CloudPakForDataAuthenticator( 'my_username', 'my_password', 'http://my_url') assert authenticator is not None assert authenticator.token_manager.url == 'http://my_url/v1/preauth/validateAuth' assert authenticator.token_manager.username == 'my_username' assert authenticator.token_manager.password == 'my_password' assert authenticator.token_manager.disable_ssl_verification is False assert authenticator.token_manager.headers is None assert authenticator.token_manager.proxies is None authenticator.set_disable_ssl_verification(True) assert authenticator.token_manager.disable_ssl_verification is True with pytest.raises(TypeError) as err: authenticator.set_headers('dummy') assert str(err.value) == 'headers must be a dictionary' authenticator.set_headers({'dummy': 'headers'}) assert authenticator.token_manager.headers == {'dummy': 'headers'} with pytest.raises(TypeError) as err: authenticator.set_proxies('dummy') assert str(err.value) == 'proxies must be a dictionary' authenticator.set_proxies({'dummy': 'proxies'}) assert authenticator.token_manager.proxies == {'dummy': 'proxies'} def test_iam_authenticator_validate_failed(): with pytest.raises(ValueError) as err: CloudPakForDataAuthenticator('my_username', None, 'my_url') assert str(err.value) == 'The username and password shouldn\'t be None.' with pytest.raises(ValueError) as err: CloudPakForDataAuthenticator(None, 'my_password', 'my_url') assert str(err.value) == 'The username and password shouldn\'t be None.' with pytest.raises(ValueError) as err: CloudPakForDataAuthenticator('my_username', 'my_password', None) assert str(err.value) == 'The url shouldn\'t be None.' with pytest.raises(ValueError) as err: CloudPakForDataAuthenticator('{my_username}', 'my_password', 'my_url') assert str(err.value) == 'The username and password shouldn\'t start or end with curly brackets or quotes. '\ 'Please remove any surrounding {, }, or \" characters.' with pytest.raises(ValueError) as err: CloudPakForDataAuthenticator('my_username', '{my_password}', 'my_url') assert str(err.value) == 'The username and password shouldn\'t start or end with curly brackets or quotes. '\ 'Please remove any surrounding {, }, or \" characters.' with pytest.raises(ValueError) as err: CloudPakForDataAuthenticator('my_username', 'my_password', '{my_url}') assert str(err.value) == 'The url shouldn\'t start or end with curly brackets or quotes. '\ 'Please remove any surrounding {, }, or \" characters.' @responses.activate def test_get_token(): url = "https://test" access_token_layout = { "username": "dummy", "role": "Admin", "permissions": [ "administrator", "manage_catalog" ], "sub": "admin", "iss": "sss", "aud": "sss", "uid": "sss", "iat": 1559324664, "exp": 1559324664 } access_token = jwt.encode(access_token_layout, 'secret', algorithm='HS256', headers={'kid': '230498151c214b788dd97f22b85410a5'}).decode('utf-8') response = { "accessToken": access_token, "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" } responses.add(responses.GET, url + '/v1/preauth/validateAuth', body=json.dumps(response), status=200) authenticator = CloudPakForDataAuthenticator( 'my_username', 'my_password', url) request = {'headers': {}} authenticator.authenticate(request) assert request['headers']['Authorization'] is not None ibm-cloud-sdk-core-1.6.2/test/test_base_service.py0000664000372000037200000004770713621341247023006 0ustar travistravis00000000000000# coding=utf-8 # pylint: disable=missing-docstring,protected-access,too-few-public-methods import json import time import os from shutil import copyfile import pytest import responses import jwt from ibm_cloud_sdk_core import BaseService from ibm_cloud_sdk_core import ApiException from ibm_cloud_sdk_core import CP4DTokenManager from ibm_cloud_sdk_core.authenticators import (IAMAuthenticator, NoAuthAuthenticator, Authenticator, BasicAuthenticator, CloudPakForDataAuthenticator) from ibm_cloud_sdk_core import get_authenticator_from_environment class IncludeExternalConfigService(BaseService): default_service_url = 'https://servicesthatincludeexternalconfig.com/api' def __init__( self, api_version, authenticator=None, trace_id=None ): BaseService.__init__( self, service_url=self.default_service_url, authenticator=authenticator, disable_ssl_verification=False ) self.api_version = api_version self.trace_id = trace_id self.configure_service('include-external-config') class AnyServiceV1(BaseService): default_url = 'https://gateway.watsonplatform.net/test/api' def __init__( self, version, service_url=default_url, authenticator=None, disable_ssl_verification=False ): BaseService.__init__( self, service_url=service_url, authenticator=authenticator, disable_ssl_verification=disable_ssl_verification) self.version = version def op_with_path_params(self, path0, path1): if path0 is None: raise ValueError('path0 must be provided') if path1 is None: raise ValueError('path1 must be provided') params = {'version': self.version} url = '/v1/foo/{0}/bar/{1}/baz'.format( *self._encode_path_vars(path0, path1)) request = self.prepare_request(method='GET', url=url, params=params) response = self.send(request) return response def with_http_config(self, http_config): self.set_http_config(http_config) request = self.prepare_request(method='GET', url='') response = self.send(request) return response def any_service_call(self): request = self.prepare_request(method='GET', url='') response = self.send(request) return response def head_request(self): request = self.prepare_request(method='HEAD', url='') response = self.send(request) return response def get_access_token(): access_token_layout = { "username": "dummy", "role": "Admin", "permissions": ["administrator", "manage_catalog"], "sub": "admin", "iss": "sss", "aud": "sss", "uid": "sss", "iat": 3600, "exp": int(time.time()) } access_token = jwt.encode( access_token_layout, 'secret', algorithm='HS256', headers={ 'kid': '230498151c214b788dd97f22b85410a5' }) return access_token.decode('utf-8') @responses.activate def test_url_encoding(): service = AnyServiceV1('2017-07-07', authenticator=NoAuthAuthenticator()) # All characters in path0 _must_ be encoded in path segments path0 = ' \"<>^`{}|/\\?#%[]' path0_encoded = '%20%22%3C%3E%5E%60%7B%7D%7C%2F%5C%3F%23%25%5B%5D' # All non-ASCII chars _must_ be encoded in path segments path1 = u'比萨浇头'.encode('utf8') # "pizza toppings" path1_encoded = '%E6%AF%94%E8%90%A8%E6%B5%87%E5%A4%B4' path_encoded = '/v1/foo/' + path0_encoded + '/bar/' + path1_encoded + '/baz' test_url = service.default_url + path_encoded responses.add( responses.GET, test_url, status=200, body=json.dumps({ "foobar": "baz" }), content_type='application/json') response = service.op_with_path_params(path0, path1) assert response is not None assert len(responses.calls) == 1 assert path_encoded in responses.calls[0].request.url assert 'version=2017-07-07' in responses.calls[0].request.url @responses.activate def test_http_config(): service = AnyServiceV1('2017-07-07', authenticator=NoAuthAuthenticator()) responses.add( responses.GET, service.default_url, status=200, body=json.dumps({ "foobar": "baz" }), content_type='application/json') response = service.with_http_config({'timeout': 100}) assert response is not None assert len(responses.calls) == 1 def test_fail_http_config(): service = AnyServiceV1('2017-07-07', authenticator=NoAuthAuthenticator()) with pytest.raises(TypeError): service.with_http_config(None) @responses.activate def test_cwd(): file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials.env') # Try changing working directories to test getting creds from cwd cwd = os.getcwd() os.chdir(os.path.dirname(file_path)) iam_authenticator = get_authenticator_from_environment('ibm_watson') service = AnyServiceV1('2017-07-07', authenticator=iam_authenticator) service.configure_service('ibm_watson') os.chdir(cwd) assert service.service_url == 'https://cwdserviceurl' assert service.authenticator is not None # Copy credentials file to cwd to test loading from current working directory temp_env_path = os.getcwd() + '/ibm-credentials.env' copyfile(file_path, temp_env_path) iam_authenticator = get_authenticator_from_environment('ibm_watson') service = AnyServiceV1('2017-07-07', authenticator=iam_authenticator) service.configure_service('ibm_watson') os.remove(temp_env_path) assert service.service_url == 'https://cwdserviceurl' assert service.authenticator is not None @responses.activate def test_iam(): file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-iam.env') os.environ['IBM_CREDENTIALS_FILE'] = file_path iam_authenticator = get_authenticator_from_environment('ibm-watson') service = AnyServiceV1('2017-07-07', authenticator=iam_authenticator) assert service.service_url == 'https://gateway.watsonplatform.net/test/api' del os.environ['IBM_CREDENTIALS_FILE'] assert service.authenticator is not None response = { "access_token": get_access_token(), "token_type": "Bearer", "expires_in": 3600, "expiration": int(time.time()), "refresh_token": "jy4gl91BQ" } responses.add( responses.POST, url='https://iam.cloud.ibm.com/identity/token', body=json.dumps(response), status=200) responses.add( responses.GET, url='https://gateway.watsonplatform.net/test/api', body=json.dumps({ "foobar": "baz" }), content_type='application/json') service.any_service_call() assert "grant-type%3Aapikey" in responses.calls[0].request.body def test_no_auth(): class MadeUp: def __init__(self): self.lazy = 'made up' with pytest.raises(ValueError) as err: service = AnyServiceV1('2017-07-07', authenticator=MadeUp()) service.prepare_request( responses.GET, url='https://gateway.watsonplatform.net/test/api', ) assert str(err.value) == 'authenticator should be of type Authenticator' service = AnyServiceV1('2017-07-07', authenticator=NoAuthAuthenticator()) service.prepare_request( responses.GET, url='https://gateway.watsonplatform.net/test/api', ) assert service.authenticator is not None assert isinstance(service.authenticator, Authenticator) def test_for_cp4d(): cp4d_authenticator = CloudPakForDataAuthenticator('my_username', 'my_password', 'my_url') service = AnyServiceV1('2017-07-07', authenticator=cp4d_authenticator) assert service.authenticator.token_manager is not None assert service.authenticator.token_manager.username == 'my_username' assert service.authenticator.token_manager.password == 'my_password' assert service.authenticator.token_manager.url == 'my_url/v1/preauth/validateAuth' assert isinstance(service.authenticator.token_manager, CP4DTokenManager) def test_disable_ssl_verification(): service1 = AnyServiceV1('2017-07-07', authenticator=NoAuthAuthenticator(), disable_ssl_verification=True) assert service1.disable_ssl_verification is True service1.set_disable_ssl_verification(False) assert service1.disable_ssl_verification is False cp4d_authenticator = CloudPakForDataAuthenticator('my_username', 'my_password', 'my_url') service2 = AnyServiceV1('2017-07-07', authenticator=cp4d_authenticator) assert service2.disable_ssl_verification is False cp4d_authenticator.set_disable_ssl_verification(True) assert service2.authenticator.token_manager.disable_ssl_verification is True @responses.activate def test_http_head(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) expected_headers = {'Test-Header1': 'value1', 'Test-Header2': 'value2'} responses.add( responses.HEAD, service.default_url, status=200, headers=expected_headers, content_type=None) response = service.head_request() assert response is not None assert len(responses.calls) == 1 assert response.headers is not None assert response.headers == expected_headers @responses.activate def test_response_with_no_body(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) responses.add(responses.GET, service.default_url, status=200, body=None) response = service.any_service_call() assert response is not None assert len(responses.calls) == 1 assert response.get_result() is None def test_has_bad_first_or_last_char(): with pytest.raises(ValueError) as err: basic_authenticator = BasicAuthenticator('{my_username}', 'my_password') AnyServiceV1('2018-11-20', authenticator=basic_authenticator).prepare_request( responses.GET, 'https://gateway.watsonplatform.net/test/api' ) assert str( err.value ) == 'The username and password shouldn\'t start or end with curly brackets or quotes. '\ 'Please remove any surrounding {, }, or \" characters.' @responses.activate def test_request_server_error(): responses.add( responses.GET, 'https://gateway.watsonplatform.net/test/api', status=500, body=json.dumps({ 'error': 'internal server error' }), content_type='application/json') service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) try: prepped = service.prepare_request('GET', url='') service.send(prepped) except ApiException as err: assert err.message == 'internal server error' @responses.activate def test_request_success_json(): responses.add( responses.GET, 'https://gateway.watsonplatform.net/test/api', status=200, body=json.dumps({ 'foo': 'bar' }), content_type='application/json') service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) prepped = service.prepare_request('GET', url='') detailed_response = service.send(prepped) assert detailed_response.get_result() == {'foo': 'bar'} service = AnyServiceV1('2018-11-20', authenticator=BasicAuthenticator('my_username', 'my_password')) service.set_default_headers({'test': 'header'}) service.set_disable_ssl_verification(True) prepped = service.prepare_request('GET', url='') detailed_response = service.send(prepped) assert detailed_response.get_result() == {'foo': 'bar'} @responses.activate def test_request_success_response(): responses.add( responses.GET, 'https://gateway.watsonplatform.net/test/api', status=200, body=json.dumps({ 'foo': 'bar' }), content_type='application/json') service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) prepped = service.prepare_request('GET', url='') detailed_response = service.send(prepped) assert detailed_response.get_result() == {"foo": "bar"} @responses.activate def test_request_fail_401(): responses.add( responses.GET, 'https://gateway.watsonplatform.net/test/api', status=401, body=json.dumps({ 'foo': 'bar' }), content_type='application/json') service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) try: prepped = service.prepare_request('GET', url='') service.send(prepped) except ApiException as err: assert err.message == 'Unauthorized: Access is denied due to invalid credentials' def test_misc_methods(): class MockModel: def __init__(self, xyz=None): self.xyz = xyz def _to_dict(self): _dict = {} if hasattr(self, 'xyz') and self.xyz is not None: _dict['xyz'] = self.xyz return _dict @classmethod def _from_dict(cls, _dict): args = {} if 'xyz' in _dict: args['xyz'] = _dict.get('xyz') return cls(**args) mock = MockModel('foo') service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) model1 = service._convert_model(mock) assert model1 == {'xyz': 'foo'} model2 = service._convert_model("{\"xyz\": \"foo\"}") assert model2 is not None assert model2['xyz'] == 'foo' temp = ['default', '123'] res_str = service._convert_list(temp) assert res_str == 'default,123' def test_default_headers(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) service.set_default_headers({'xxx': 'yyy'}) assert service.default_headers == {'xxx': 'yyy'} with pytest.raises(TypeError): service.set_default_headers('xxx') def test_set_service_url(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) with pytest.raises(ValueError) as err: service.set_service_url('{url}') assert str(err.value) == 'The service url shouldn\'t start or end with curly brackets or quotes. '\ 'Be sure to remove any {} and \" characters surrounding your service url' service.set_service_url('my_url') def test_get_authenticator(): auth = BasicAuthenticator('my_username', 'my_password') service = AnyServiceV1('2018-11-20', authenticator=auth) assert service.get_authenticator() is not None @responses.activate def test_user_agent_header(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) user_agent_header = service.user_agent_header assert user_agent_header is not None assert user_agent_header['User-Agent'] is not None responses.add( responses.GET, 'https://gateway.watsonplatform.net/test/api', status=200, body='some text') prepped = service.prepare_request('GET', url='', headers={ 'user-agent': 'my_user_agent' }) response = service.send(prepped) assert response.get_result().request.headers.__getitem__( 'user-agent') == 'my_user_agent' prepped = service.prepare_request('GET', url='', headers=None) response = service.send(prepped) assert response.get_result().request.headers.__getitem__( 'user-agent') == user_agent_header['User-Agent'] def test_files_dict(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) form_data = {} file = open( os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-iam.env'), 'r') form_data['file1'] = (None, file, 'application/octet-stream') form_data['string1'] = (None, 'hello', 'text/plain') request = service.prepare_request('GET', url='', headers={'X-opt-out': True}, files=form_data) files = request['files'] assert isinstance(files, list) assert len(files) == 2 files_dict = dict(files) file1 = files_dict['file1'] assert file1[0] == 'ibm-credentials-iam.env' string1 = files_dict['string1'] assert string1[0] is None def test_files_list(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) form_data = [] file = open( os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-iam.env'), 'r') form_data.append(('file1', (None, file, 'application/octet-stream'))) form_data.append(('string1', (None, 'hello', 'text/plain'))) request = service.prepare_request('GET', url='', headers={'X-opt-out': True}, files=form_data) files = request['files'] assert isinstance(files, list) assert len(files) == 2 files_dict = dict(files) file1 = files_dict['file1'] assert file1[0] == 'ibm-credentials-iam.env' string1 = files_dict['string1'] assert string1[0] is None def test_files_duplicate_parts(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) form_data = [] file = open( os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-iam.env'), 'r') form_data.append(('creds_file', (None, file, 'application/octet-stream'))) file = open( os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-basic.env'), 'r') form_data.append(('creds_file', (None, file, 'application/octet-stream'))) file = open( os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-bearer.env'), 'r') form_data.append(('creds_file', (None, file, 'application/octet-stream'))) request = service.prepare_request('GET', url='', headers={'X-opt-out': True}, files=form_data) files = request['files'] assert isinstance(files, list) assert len(files) == 3 for part_name, file_tuple in files: assert part_name == 'creds_file' assert file_tuple[0] is not None def test_json(): service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator()) req = service.prepare_request('POST', url='', headers={'X-opt-out': True}, data={'hello': 'world'}) assert req.get('data') == "{\"hello\": \"world\"}" def test_service_url_not_set(): service = BaseService(service_url='', authenticator=NoAuthAuthenticator()) with pytest.raises(ValueError) as err: service.prepare_request('POST', url='') assert str(err.value) == 'The service_url is required' def test_setting_proxy(): service = BaseService('test', authenticator=IAMAuthenticator('wonder woman')) assert service.authenticator is not None assert service.authenticator.token_manager.http_config == {} http_config = { "proxies": { "http": "user:password@host:port" } } service.set_http_config(http_config) assert service.authenticator.token_manager.http_config == http_config service2 = BaseService('test', authenticator=BasicAuthenticator('marvellous', 'mrs maisel')) service2.set_http_config(http_config) assert service2.authenticator is not None def test_configure_service(): file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-external.env') os.environ['IBM_CREDENTIALS_FILE'] = file_path service = IncludeExternalConfigService('v1', authenticator=NoAuthAuthenticator()) assert service.service_url == 'https://externallyconfigured.com/api' assert service.disable_ssl_verification is True # The authenticator should not be changed as a result of configure_service() assert isinstance(service.get_authenticator(), NoAuthAuthenticator) def test_configure_service_error(): service = BaseService('v1', authenticator=NoAuthAuthenticator()) with pytest.raises(ValueError) as err: service.configure_service(None) assert str(err.value) == 'Service_name must be of type string.' ibm-cloud-sdk-core-1.6.2/test/test_bearer_authenticator.py0000664000372000037200000000170713621341247024534 0ustar travistravis00000000000000# pylint: disable=missing-docstring import pytest from ibm_cloud_sdk_core.authenticators import BearerTokenAuthenticator def test_bearer_authenticator(): authenticator = BearerTokenAuthenticator('my_bearer_token') assert authenticator is not None assert authenticator.bearer_token == 'my_bearer_token' authenticator.set_bearer_token('james bond') assert authenticator.bearer_token == 'james bond' request = {'headers': {}} authenticator.authenticate(request) assert request['headers']['Authorization'] == 'Bearer james bond' def test_bearer_validate_failed(): with pytest.raises(ValueError) as err: BearerTokenAuthenticator(None) assert str(err.value) == 'The bearer token shouldn\'t be None.' authenticator = BearerTokenAuthenticator('my_bearer_token') with pytest.raises(ValueError) as err: authenticator.set_bearer_token(None) assert str(err.value) == 'The bearer token shouldn\'t be None.' ibm-cloud-sdk-core-1.6.2/test/test_detailed_response.py0000664000372000037200000000426313621341247024033 0ustar travistravis00000000000000# coding=utf-8 # pylint: disable=missing-docstring import json import responses import requests from ibm_cloud_sdk_core import DetailedResponse def clean(val): """Eliminate all whitespace and convert single to double quotes""" return val.translate(str.maketrans('', '', ' \n\t\r')).replace("'", "\"") @responses.activate def test_detailed_response_dict(): responses.add(responses.GET, 'https://test.com', status=200, body=json.dumps({'foobar': 'baz'}), content_type='application/json') mock_response = requests.get('https://test.com') detailed_response = DetailedResponse(mock_response.json(), mock_response.headers, mock_response.status_code) assert detailed_response is not None assert detailed_response.get_result() == {'foobar': 'baz'} assert detailed_response.get_headers() == {u'Content-Type': 'application/json'} assert detailed_response.get_status_code() == 200 response_str = clean(detailed_response.__str__()) assert clean(detailed_response.get_result().__str__()) in response_str #assert clean(detailed_response.get_headers().__str__()) in response_str assert clean(detailed_response.get_status_code().__str__()) in response_str @responses.activate def test_detailed_response_list(): responses.add(responses.GET, 'https://test.com', status=200, body=json.dumps(['foobar', 'baz']), content_type='application/json') mock_response = requests.get('https://test.com') detailed_response = DetailedResponse(mock_response.json(), mock_response.headers, mock_response.status_code) assert detailed_response is not None assert detailed_response.get_result() == ['foobar', 'baz'] assert detailed_response.get_headers() == {u'Content-Type': 'application/json'} assert detailed_response.get_status_code() == 200 response_str = clean(detailed_response.__str__()) assert clean(detailed_response.get_result().__str__()) in response_str #assert clean(detailed_response.get_headers().__str__()) in response_str assert clean(detailed_response.get_status_code().__str__()) in response_str ibm-cloud-sdk-core-1.6.2/test/test_iam_token_manager.py0000664000372000037200000001624413621341247024004 0ustar travistravis00000000000000# pylint: disable=missing-docstring import time import responses import jwt import pytest from ibm_cloud_sdk_core import IAMTokenManager, ApiException def get_access_token(): access_token_layout = { "username": "dummy", "role": "Admin", "permissions": [ "administrator", "manage_catalog" ], "sub": "admin", "iss": "sss", "aud": "sss", "uid": "sss", "iat": 3600, "exp": int(time.time()) } access_token = jwt.encode(access_token_layout, 'secret', algorithm='HS256', headers={'kid': '230498151c214b788dd97f22b85410a5'}) return access_token.decode('utf-8') @responses.activate def test_request_token_auth_default(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "access_token": "oAeisG8yqPY7sFR_x66Z15", "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" }""" responses.add(responses.POST, url=iam_url, body=response, status=200) token_manager = IAMTokenManager("apikey") token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].request.headers.get('Authorization') is None assert responses.calls[0].response.text == response @responses.activate def test_request_token_auth_in_ctor(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "access_token": "oAeisG8yqPY7sFR_x66Z15", "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" }""" default_auth_header = 'Basic Yng6Yng=' responses.add(responses.POST, url=iam_url, body=response, status=200) token_manager = IAMTokenManager("apikey", iam_url, 'foo', 'bar') token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].request.headers['Authorization'] != default_auth_header assert responses.calls[0].response.text == response @responses.activate def test_request_token_unsuccessful(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "context": { "requestId": "38a0e9c226d94764820d92aa623eb0f6", "requestType": "incoming.Identity_Token", "userAgent": "ibm-python-sdk-core-1.0.0", "url": "https://iam.cloud.ibm.com", "instanceId": "iamid-4.5-6788-90b137c-75f48695b5-kl4wx", "threadId": "169de5", "host": "iamid-4.5-6788-90b137c-75f48695b5-kl4wx", "startTime": "29.10.2019 12:31:00:300 GMT", "endTime": "29.10.2019 12:31:00:381 GMT", "elapsedTime": "81", "locale": "en_US", "clusterName": "iam-id-prdal12-8brn" }, "errorCode": "BXNIM0415E", "errorMessage": "Provided API key could not be found" } """ responses.add(responses.POST, url=iam_url, body=response, status=400) token_manager = IAMTokenManager("apikey") with pytest.raises(ApiException): token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].response.text == response @responses.activate def test_request_token_auth_in_ctor_client_id_only(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "access_token": "oAeisG8yqPY7sFR_x66Z15", "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" }""" responses.add(responses.POST, url=iam_url, body=response, status=200) token_manager = IAMTokenManager("iam_apikey", iam_url, 'foo') token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].request.headers.get('Authorization') is None assert responses.calls[0].response.text == response @responses.activate def test_request_token_auth_in_ctor_secret_only(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "access_token": "oAeisG8yqPY7sFR_x66Z15", "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" }""" responses.add(responses.POST, url=iam_url, body=response, status=200) token_manager = IAMTokenManager("iam_apikey", iam_url, None, 'bar') token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].request.headers.get('Authorization') is None assert responses.calls[0].response.text == response @responses.activate def test_request_token_auth_in_setter(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "access_token": "oAeisG8yqPY7sFR_x66Z15", "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" }""" default_auth_header = 'Basic Yng6Yng=' responses.add(responses.POST, url=iam_url, body=response, status=200) token_manager = IAMTokenManager("iam_apikey") token_manager.set_client_id_and_secret('foo', 'bar') token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].request.headers['Authorization'] != default_auth_header assert responses.calls[0].response.text == response @responses.activate def test_request_token_auth_in_setter_client_id_only(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "access_token": "oAeisG8yqPY7sFR_x66Z15", "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" }""" responses.add(responses.POST, url=iam_url, body=response, status=200) token_manager = IAMTokenManager("iam_apikey") token_manager.set_client_id_and_secret('foo', None) token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].request.headers.get('Authorization') is None assert responses.calls[0].response.text == response @responses.activate def test_request_token_auth_in_setter_secret_only(): iam_url = "https://iam.cloud.ibm.com/identity/token" response = """{ "access_token": "oAeisG8yqPY7sFR_x66Z15", "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" }""" responses.add(responses.POST, url=iam_url, body=response, status=200) token_manager = IAMTokenManager("iam_apikey") token_manager.set_client_id_and_secret(None, 'bar') token_manager.set_headers({'user':'header'}) token_manager.request_token() assert len(responses.calls) == 1 assert responses.calls[0].request.url == iam_url assert responses.calls[0].request.headers.get('Authorization') is None assert responses.calls[0].response.text == response ibm-cloud-sdk-core-1.6.2/test/test_iam_authenticator.py0000664000372000037200000000650213621341247024040 0ustar travistravis00000000000000# pylint: disable=missing-docstring import json import pytest import responses import jwt from ibm_cloud_sdk_core.authenticators import IAMAuthenticator def test_iam_authenticator(): authenticator = IAMAuthenticator('my_apikey') assert authenticator is not None assert authenticator.token_manager.url == 'https://iam.cloud.ibm.com/identity/token' assert authenticator.token_manager.client_id is None assert authenticator.token_manager.client_secret is None assert authenticator.token_manager.disable_ssl_verification is False assert authenticator.token_manager.headers is None assert authenticator.token_manager.proxies is None assert authenticator.token_manager.apikey == 'my_apikey' authenticator.set_client_id_and_secret('tom', 'jerry') assert authenticator.token_manager.client_id == 'tom' assert authenticator.token_manager.client_secret == 'jerry' with pytest.raises(TypeError) as err: authenticator.set_headers('dummy') assert str(err.value) == 'headers must be a dictionary' authenticator.set_headers({'dummy': 'headers'}) assert authenticator.token_manager.headers == {'dummy': 'headers'} with pytest.raises(TypeError) as err: authenticator.set_proxies('dummy') assert str(err.value) == 'proxies must be a dictionary' authenticator.set_proxies({'dummy': 'proxies'}) assert authenticator.token_manager.proxies == {'dummy': 'proxies'} def test_iam_authenticator_validate_failed(): with pytest.raises(ValueError) as err: IAMAuthenticator(None) assert str(err.value) == 'The apikey shouldn\'t be None.' with pytest.raises(ValueError) as err: IAMAuthenticator('{apikey}') assert str( err.value ) == 'The apikey shouldn\'t start or end with curly brackets or quotes. '\ 'Please remove any surrounding {, }, or \" characters.' with pytest.raises(ValueError) as err: IAMAuthenticator('my_apikey', client_id='my_client_id') assert str( err.value) == 'Both client_id and client_secret should be initialized.' with pytest.raises(ValueError) as err: IAMAuthenticator('my_apikey', client_secret='my_client_secret') assert str( err.value) == 'Both client_id and client_secret should be initialized.' @responses.activate def test_get_token(): url = "https://iam.cloud.ibm.com/identity/token" access_token_layout = { "username": "dummy", "role": "Admin", "permissions": ["administrator", "manage_catalog"], "sub": "admin", "iss": "sss", "aud": "sss", "uid": "sss", "iat": 1559324664, "exp": 1559324664 } access_token = jwt.encode( access_token_layout, 'secret', algorithm='HS256', headers={ 'kid': '230498151c214b788dd97f22b85410a5' }) access_token = access_token.decode('utf-8') response = { "access_token": access_token, "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" } responses.add( responses.POST, url=url, body=json.dumps(response), status=200) authenticator = IAMAuthenticator('my_apikey') request = {'headers': {}} authenticator.authenticate(request) assert request['headers']['Authorization'] is not None ibm-cloud-sdk-core-1.6.2/test/test_cp4d_token_manager.py0000664000372000037200000000305513621341247024064 0ustar travistravis00000000000000# pylint: disable=missing-docstring import json import responses import jwt from ibm_cloud_sdk_core import CP4DTokenManager @responses.activate def test_request_token(): url = "https://test" access_token_layout = { "username": "dummy", "role": "Admin", "permissions": [ "administrator", "manage_catalog" ], "sub": "admin", "iss": "sss", "aud": "sss", "uid": "sss", "iat": 1559324664, "exp": 1559324664 } access_token = jwt.encode(access_token_layout, 'secret', algorithm='HS256', headers={'kid': '230498151c214b788dd97f22b85410a5'}).decode('utf-8') response = { "accessToken": access_token, "token_type": "Bearer", "expires_in": 3600, "expiration": 1524167011, "refresh_token": "jy4gl91BQ" } responses.add(responses.GET, url + '/v1/preauth/validateAuth', body=json.dumps(response), status=200) token_manager = CP4DTokenManager("username", "password", url) token_manager.set_disable_ssl_verification(True) token = token_manager.get_token() assert responses.calls[0].request.url == url + '/v1/preauth/validateAuth' assert token == access_token token_manager = CP4DTokenManager("username", "password", url + '/v1/preauth/validateAuth') token = token_manager.get_token() assert responses.calls[0].request.url == url + '/v1/preauth/validateAuth' assert token == access_token assert len(responses.calls) == 2 ibm-cloud-sdk-core-1.6.2/test/__init__.py0000664000372000037200000000000013621341247021023 0ustar travistravis00000000000000ibm-cloud-sdk-core-1.6.2/test/test_no_auth_authenticator.py0000664000372000037200000000063313621341247024726 0ustar travistravis00000000000000# pylint: disable=missing-docstring from ibm_cloud_sdk_core.authenticators import NoAuthAuthenticator def test_no_auth_authenticator(): authenticator = NoAuthAuthenticator() assert authenticator is not None assert authenticator.authentication_type == 'noAuth' authenticator.validate() request = {'headers': {}} authenticator.authenticate(request) assert request['headers'] == {} ibm-cloud-sdk-core-1.6.2/test/test_jwt_token_manager.py0000664000372000037200000000630413621341247024036 0ustar travistravis00000000000000# pylint: disable=missing-docstring,protected-access import time import jwt import pytest from ibm_cloud_sdk_core import JWTTokenManager class JWTTokenManagerMockImpl(JWTTokenManager): def __init__(self, url=None, access_token=None): self.url = url self.access_token = access_token self.request_count = 0 # just for tests to see how many times request was called super(JWTTokenManagerMockImpl, self).__init__(url, access_token, 'access_token') def request_token(self): self.request_count += 1 current_time = int(time.time()) token_layout = { "username": "dummy", "role": "Admin", "permissions": [ "administrator", "manage_catalog" ], "sub": "admin", "iss": "sss", "aud": "sss", "uid": "sss", "iat": current_time, "exp": current_time + 3600 } access_token = jwt.encode(token_layout, 'secret', algorithm='HS256', headers={'kid': '230498151c214b788dd97f22b85410a5'}) response = {"access_token": access_token, "token_type": "Bearer", "expires_in": 3600, "expiration": current_time, "refresh_token": "jy4gl91BQ", "from_token_manager": True } return response def _get_current_time(): return int(time.time()) def test_get_token(): url = "https://iam.cloud.ibm.com/identity/token" token_manager = JWTTokenManagerMockImpl(url) token = token_manager.get_token() assert token_manager.token_info.get('expires_in') == 3600 assert token_manager._is_token_expired() is False token_manager.token_info = {"access_token": "old_dummy", "token_type": "Bearer", "expires_in": 3600, "expiration": time.time(), "refresh_token": "jy4gl91BQ" } token = token_manager.get_token() assert token == "old_dummy" # expired token: token_manager.time_for_new_token = _get_current_time() - 300 token = token_manager.get_token() assert token != "old_dummy" assert token_manager.request_count == 2 def test_is_token_expired(): token_manager = JWTTokenManagerMockImpl(None, None) assert token_manager._is_token_expired() is True token_manager.time_for_new_token = _get_current_time() + 3600 assert token_manager._is_token_expired() is False token_manager.time_for_new_token = _get_current_time() - 3600 assert token_manager._is_token_expired() def test_not_implemented_error(): with pytest.raises(NotImplementedError) as err: token_manager = JWTTokenManager(None, None) token_manager.request_token() assert str(err.value) == 'request_token MUST be overridden by a subclass of JWTTokenManager.' def test_disable_ssl_verification(): token_manager = JWTTokenManagerMockImpl('https://iam.cloud.ibm.com/identity/token') token_manager.set_disable_ssl_verification(True) assert token_manager.disable_ssl_verification is True ibm-cloud-sdk-core-1.6.2/test/test_utils.py0000664000372000037200000002405413621341247021502 0ustar travistravis00000000000000# pylint: disable=missing-docstring import datetime import os from ibm_cloud_sdk_core import string_to_datetime, datetime_to_string, get_authenticator_from_environment from ibm_cloud_sdk_core import string_to_date, date_to_string from ibm_cloud_sdk_core import convert_model, convert_list from ibm_cloud_sdk_core.authenticators import BasicAuthenticator, IAMAuthenticator def test_string_to_datetime(): # If the specified string does not include a timezone, it is assumed to be UTC date = string_to_datetime('2017-03-06 16:00:04.159338') assert date.day == 6 assert date.hour == 16 assert date.tzinfo.utcoffset(None) == datetime.timezone.utc.utcoffset(None) # Test date string with TZ specified as '+xxxx' date = string_to_datetime('2017-03-06 16:00:04.159338+0600') assert date.day == 6 assert date.hour == 16 assert date.tzinfo.utcoffset(None).total_seconds() == 6*60*60 # Test date string with TZ specified as 'Z' date = string_to_datetime('2017-03-06 16:00:04.159338Z') assert date.day == 6 assert date.hour == 16 assert date.tzinfo.utcoffset(None) == datetime.timezone.utc.utcoffset(None) def test_datetime_to_string(): # If specified date is None, return None assert datetime_to_string(None) is None # If the specified date is "naive", it is interpreted as a UTC date date = datetime.datetime(2017, 3, 6, 16, 0, 4, 159338) res = datetime_to_string(date) assert res == '2017-03-06T16:00:04.159338Z' # Test date with UTC timezone date = datetime.datetime(2017, 3, 6, 16, 0, 4, 159338, datetime.timezone.utc) res = datetime_to_string(date) assert res == '2017-03-06T16:00:04.159338Z' # Test date with non-UTC timezone tzn = datetime.timezone(datetime.timedelta(hours=-6)) date = datetime.datetime(2017, 3, 6, 10, 0, 4, 159338, tzn) res = datetime_to_string(date) assert res == '2017-03-06T16:00:04.159338Z' def test_date_conversion(): date = string_to_date('2017-03-06') assert date.day == 6 res = date_to_string(date) assert res == '2017-03-06' assert date_to_string(None) is None def test_convert_model(): class MockModel: def __init__(self, xyz=None): self.xyz = xyz def to_dict(self): _dict = {} if hasattr(self, 'xyz') and self.xyz is not None: _dict['xyz'] = self.xyz return _dict @classmethod def from_dict(cls, _dict): pass mock1 = MockModel('foo') mock1_dict = convert_model(mock1) assert mock1_dict == {'xyz': 'foo'} mock2 = {'foo': 'bar', 'baz': 'qux'} mock2_dict = convert_model(mock2) assert mock2_dict == mock2 mock3 = 'this is not a model' mock3_dict = convert_model(mock3) assert mock3_dict == mock3 def test_convert_list(): temp = ['default', '123'] res_str = convert_list(temp) assert res_str == 'default,123' mock2 = 'default,123' mock2_str = convert_list(mock2) assert mock2_str == mock2 mock3 = {'not': 'a list'} mock3_str = convert_list(mock3) assert mock3_str == mock3 mock4 = ['not', 0, 'list of str'] mock4_str = convert_list(mock4) assert mock4_str == mock4 def test_get_authenticator_from_credential_file(): file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-iam.env') os.environ['IBM_CREDENTIALS_FILE'] = file_path authenticator = get_authenticator_from_environment('ibm watson') assert authenticator is not None assert authenticator.token_manager.apikey == '5678efgh' del os.environ['IBM_CREDENTIALS_FILE'] file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-basic.env') os.environ['IBM_CREDENTIALS_FILE'] = file_path authenticator = get_authenticator_from_environment('watson') assert authenticator is not None assert authenticator.username == 'my_username' del os.environ['IBM_CREDENTIALS_FILE'] file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-cp4d.env') os.environ['IBM_CREDENTIALS_FILE'] = file_path authenticator = get_authenticator_from_environment('watson') assert authenticator is not None assert authenticator.token_manager.username == 'my_username' assert authenticator.token_manager.password == 'my_password' del os.environ['IBM_CREDENTIALS_FILE'] file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-no-auth.env') os.environ['IBM_CREDENTIALS_FILE'] = file_path authenticator = get_authenticator_from_environment('watson') assert authenticator is not None del os.environ['IBM_CREDENTIALS_FILE'] file_path = os.path.join( os.path.dirname(__file__), '../resources/ibm-credentials-bearer.env') os.environ['IBM_CREDENTIALS_FILE'] = file_path authenticator = get_authenticator_from_environment('watson') assert authenticator is not None assert authenticator.bearer_token is not None del os.environ['IBM_CREDENTIALS_FILE'] def test_get_authenticator_from_env_variables(): os.environ['TEST_APIKEY'] = '5678efgh' authenticator = get_authenticator_from_environment('test') assert authenticator is not None assert authenticator.token_manager.apikey == '5678efgh' del os.environ['TEST_APIKEY'] def test_vcap_credentials(): vcap_services = '{"test":[{"credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username", \ "password":"bogus password"}}]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('test') assert isinstance(authenticator, BasicAuthenticator) assert authenticator.username == 'bogus username' assert authenticator.password == 'bogus password' del os.environ['VCAP_SERVICES'] vcap_services = '{"test":[{"credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "apikey":"bogus apikey"}}]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('test') assert isinstance(authenticator, IAMAuthenticator) assert authenticator.token_manager.apikey == 'bogus apikey' del os.environ['VCAP_SERVICES'] vcap_services = '{"test":[{"credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "iam_apikey":"bogus apikey"}}]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('test') assert isinstance(authenticator, IAMAuthenticator) assert authenticator.token_manager.apikey == 'bogus apikey' del os.environ['VCAP_SERVICES'] vcap_services = '{"test":[{"name": "testname",\ "credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username", \ "password":"bogus password"}}]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('testname') assert isinstance(authenticator, BasicAuthenticator) assert authenticator.username == 'bogus username' assert authenticator.password == 'bogus password' del os.environ['VCAP_SERVICES'] def test_vcap_credentials_2(): vcap_services = '{\ "test":[{"name": "testname",\ "credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username2", \ "password":"bogus password2"}},\ {"name": "othertestname",\ "credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username3", \ "password":"bogus password3"}}],\ "testname":[{"name": "nottestname",\ "credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username", \ "password":"bogus password"}}]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('testname') assert isinstance(authenticator, BasicAuthenticator) assert authenticator.username == 'bogus username2' assert authenticator.password == 'bogus password2' del os.environ['VCAP_SERVICES'] vcap_services = '{"test":[{\ "credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username", \ "password":"bogus password"}},\ {"credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username2", \ "password":"bogus password2"}}\ ]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('test') assert isinstance(authenticator, BasicAuthenticator) assert authenticator.username == 'bogus username' assert authenticator.password == 'bogus password' del os.environ['VCAP_SERVICES'] vcap_services = '{"first":[],\ "test":[{"credentials":{ \ "url":"https://gateway.watsonplatform.net/compare-comply/api",\ "username":"bogus username", \ "password":"bogus password"}}],\ "last":[]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('test') assert isinstance(authenticator, BasicAuthenticator) assert authenticator.username == 'bogus username' assert authenticator.password == 'bogus password' del os.environ['VCAP_SERVICES'] vcap_services = '{"test":[],\ "last":[]}' os.environ['VCAP_SERVICES'] = vcap_services authenticator = get_authenticator_from_environment('test') assert authenticator is None del os.environ['VCAP_SERVICES'] def test_multi_word_service_name(): os.environ['PERSONALITY_INSIGHTS_APIKEY'] = '5678efgh' authenticator = get_authenticator_from_environment('personality-insights') assert authenticator is not None assert authenticator.token_manager.apikey == '5678efgh' del os.environ['PERSONALITY_INSIGHTS_APIKEY'] ibm-cloud-sdk-core-1.6.2/test/test_api_exception.py0000664000372000037200000000543413621341247023172 0ustar travistravis00000000000000# coding=utf-8 import json import responses import requests from ibm_cloud_sdk_core import ApiException @responses.activate def test_api_exception(): """Test APIException class""" responses.add(responses.GET, 'https://test.com', status=500, body=json.dumps({'error': 'sorry', 'msg': 'serious error'}), content_type='application/json') mock_response = requests.get('https://test.com') exception = ApiException(500, http_response=mock_response) assert exception is not None assert exception.message == 'sorry' responses.add(responses.GET, 'https://test-again.com', status=500, body=json.dumps({ "errors": [ { "message": "sorry again", }], }), content_type='application/json') mock_response = requests.get('https://test-again.com') exception = ApiException(500, http_response=mock_response) assert exception.message == 'sorry again' responses.add(responses.GET, 'https://test-once-more.com', status=500, body=json.dumps({'message': 'sorry once more'}), content_type='application/json') mock_response = requests.get('https://test-once-more.com') exception = ApiException(500, http_response=mock_response) assert exception.message == 'sorry once more' responses.add(responses.GET, 'https://test-msg.com', status=500, body=json.dumps({'msg': 'serious error'}), content_type='application/json') mock_response = requests.get('https://test-msg.com') exception = ApiException(500, http_response=mock_response) assert exception.message == 'Unknown error' responses.add(responses.GET, 'https://test-errormessage.com', status=500, body=json.dumps({'errorMessage': 'IAM error message'}), content_type='application/json') mock_response = requests.get('https://test-errormessage.com') exception = ApiException(500, http_response=mock_response) assert exception.message == 'IAM error message' responses.add(responses.GET, 'https://test-for-text.com', status=500, headers={'X-Global-Transaction-ID': 'xx'}, body="plain text error") mock_response = requests.get('https://test-for-text.com') exception = ApiException(500, http_response=mock_response) assert exception.message == 'plain text error' assert exception.__str__() == 'Error: plain text error, Code: 500 , X-global-transaction-id: xx' ibm-cloud-sdk-core-1.6.2/setup.py0000664000372000037200000000557113621341247017467 0ustar travistravis00000000000000#!/usr/bin/env python # Copyright 2019 IBM All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand __version__ = '1.6.2' if sys.argv[-1] == 'publish': # test server os.system('python setup.py register -r pypitest') os.system('python setup.py sdist upload -r pypitest') # production server os.system('python setup.py register -r pypi') os.system('python setup.py sdist upload -r pypi') sys.exit() # Convert README.md to README.rst for pypi try: from pypandoc import convert_file def read_md(f): return convert_file(f, 'rst') except: print('warning: pypandoc module not found, ' 'could not convert Markdown to RST') def read_md(f): return open(f, 'rb').read().decode(encoding='utf-8') class PyTest(TestCommand): def finalize_options(self): TestCommand.finalize_options(self) self.test_args = ['--strict', '--verbose', '--tb=long', 'test'] self.test_suite = True def run_tests(self): import pytest errcode = pytest.main(self.test_args) sys.exit(errcode) setup(name='ibm-cloud-sdk-core', version=__version__, description='Client library for the IBM Cloud services', license='Apache 2.0', install_requires=['requests>=2.0, <3.0', 'python_dateutil>=2.5.3', 'PyJWT >=1.7.1'], tests_require=['responses', 'pytest', 'pytest-rerunfailures', 'tox', 'pylint', 'bumpversion'], cmdclass={'test': PyTest}, author='Erika Dsouza', author_email='erika.dsouza@ibm.com', long_description=read_md('README.md'), url='https://github.com/IBM/python-sdk-core', packages=find_packages(), include_package_data=True, keywords='watson, ibm, cloud', classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Application ' 'Frameworks', ], zip_safe=True )