def test_missing_from_config_file_throws(self, _, missing_attributes, token): """ Tests that if some required fields are missing from the ApiConfiguration an error is thrown :return: """ # Create an ApiConfiguration with all values populated proxy_config = ProxyConfig(**{ key.replace("proxy_", ""): value for key, value in source_config_details.items() if value is not None and "proxy" in key }) if source_config_details["proxy_address"] is not None else None api_config_kwargs = {key: value for key, value in source_config_details.items() if value is not None and "proxy" not in key} api_config_kwargs["proxy_config"] = proxy_config api_configuration = ApiConfiguration(**api_config_kwargs) # Pop off the missing attributes [setattr(api_configuration, missing_attribute, None) for missing_attribute in missing_attributes] # Ensure that there are no environment variables which can be used to fill the missing Api Url with patch.dict('os.environ', clear=True), self.assertRaises(ValueError) as ex: ApiClientBuilder.build(api_configuration=api_configuration, token=token) self.assertEqual( ex.exception.args[0], f"The fields {str(missing_attributes)} on the ApiConfiguration are set to None, " f"please ensure that you have provided them directly, via a secrets file or environment " f"variables")
def test_get_token_with_proxy(self): secrets = { "api": { config_keys[key]["config"]: value for key, value in source_config_details.items() if value is not None and "proxy" not in key }, "proxy": { config_keys[key]["config"]: value for key, value in source_config_details.items() if value is not None and "proxy" in key } } secrets["api"].pop("clientCertificate", None) if secrets["proxy"].get("address", None) is None: self.skipTest(f"missing proxy configuration") secrets_file = TempFileManager.create_temp_file(secrets) original_token, refresh_token = tu.get_okta_tokens(secrets_file.name) proxy_config = ProxyConfig( address=secrets["proxy"]["address"], username=secrets["proxy"]["username"], password=secrets["proxy"]["password"] ) proxies = proxy_config.format_proxy_schema() with patch.dict('os.environ', {"HTTPS_PROXY": proxies["https"]}, clear=True): proxy_url = os.getenv("HTTPS_PROXY", None) if proxy_url is not None: refreshed_token = RefreshingToken(token_url=self.config.token_url, client_id=self.config.client_id, client_secret=self.config.client_secret, initial_access_token=original_token, initial_token_expiry=1, # 1s expiry refresh_token=refresh_token, expiry_offset=3599, # set to 1s expiry proxies={}) self.assertIsNotNone(refreshed_token)
def test_get_token_with_proxy(self): secrets = { "api": { config_keys[key]["config"]: value for key, value in source_config_details.items() if value is not None and "proxy" not in key }, "proxy": { config_keys[key]["config"]: value for key, value in source_config_details.items() if value is not None and "proxy" in key } } secrets["api"].pop("clientCertificate", None) if secrets["proxy"].get("address", None) is None: self.skipTest(f"missing proxy configuration") TempFileManager.create_temp_file(secrets) proxy_config = ProxyConfig(address=secrets["proxy"]["address"], username=secrets["proxy"]["username"], password=secrets["proxy"]["password"]) proxies = proxy_config.format_proxy_schema() with patch.dict('os.environ', {"HTTPS_PROXY": proxies["https"]}, clear=True): proxy_url = os.getenv("HTTPS_PROXY", None) if proxy_url is not None: refreshed_token = RefreshingToken(api_configuration=self.config, expiry_offset=3599) self.assertIsNotNone(refreshed_token)
def __init__(self, **kwargs): """ Iniitalise an ApiClientFactory by passing the token, api_url and app_name, or by passing in the api_secrets_filename :param str token: Bearer token used to initialise the API :param str api_secrets_filename: Name of secrets file (including full path) :param str api_url: LUSID API url :param str app_name: Application name (optional) :param str certificate_filename: Name of the certificate file (.pem, .cer or .crt) :param str proxy_url: The url of the proxy to use including the port e.g. http://myproxy.com:8888 :param str proxy_username: The username for the proxy to use :param str proxy_password: The password for the proxy to use :param str correlation_id: Correlation id for all calls made from the returned LUSID API instances :param bool tcp_keep_alive: A flag for controlling if the API client uses TCP keep-alive probes """ builder_kwargs = {} if "token" in kwargs and str(kwargs["token"]) != "None": # If there is a token use it along with the specified proxy details if specified config = ApiConfiguration( api_url=kwargs.get("api_url", None), certificate_filename=kwargs.get("certificate_filename", None), proxy_config=ProxyConfig( address=kwargs.get("proxy_url", None), username=kwargs.get("proxy_username", None), password=kwargs.get("proxy_password", None), ) if kwargs.get("proxy_url", None) is not None else None, app_name=kwargs.get("app_name", None)) builder_kwargs["api_configuration"] = config builder_kwargs["token"] = kwargs["token"] # Otherwise use a secrets file if it exists builder_kwargs["api_secrets_filename"] = kwargs.get( "api_secrets_filename", None) # add the correlation id if specified builder_kwargs["correlation_id"] = kwargs.get("correlation_id", None) # add the id provider response handler if specified builder_kwargs["id_provider_response_handler"] = kwargs.get( "id_provider_response_handler", None) builder_kwargs["tcp_keep_alive"] = kwargs.get("tcp_keep_alive", False) # Call the client builder, this will result in using either a token, secrets file or environment variables self.api_client = ApiClientBuilder.build(**builder_kwargs)
def __init__(self, **kwargs): """ Iniitalise an ApiClientFactory by passing the token, api_url and app_name, or by passing in the api_secrets_filename :param str token: Bearer token used to initialise the API :param str api_secrets_filename: Name of secrets file (including full path) :param str api_url: LUSID API url :param str app_name: Application name (optional) :param str certificate_filename: Name of the certificate file (.pem, .cer or .crt) :param str proxy_url: The url of the proxy to use including the port e.g. http://myproxy.com:8888 :param str proxy_username: The username for the proxy to use :param str proxy_password: The password for the proxy to use """ builder_kwargs = {} if "token" in kwargs and str(kwargs["token"]) != "None": # If there is a token use it along with the specified proxy details if specified config = ApiConfiguration( api_url=kwargs.get("api_url", None), certificate_filename=kwargs.get("certificate_filename", None), proxy_config=ProxyConfig( address=kwargs.get("proxy_url", None), username=kwargs.get("proxy_username", None), password=kwargs.get("proxy_password", None), ) if kwargs.get("proxy_url", None) is not None else None, app_name=kwargs.get("app_name", None)) builder_kwargs["api_configuration"] = config builder_kwargs["token"] = kwargs["token"] # Otherwise use a secrets file if it exists builder_kwargs["api_secrets_filename"] = kwargs.get( "api_secrets_filename", None) # Call the client builder, this will result in using either a token, secrets file or environment variables self.api_client = ApiClientBuilder.build(**builder_kwargs)
def load(api_secrets_filename=None): """ :param str api_secrets_filename: The full path to the JSON file containing the API credentials and optional proxy details :return: lusid.utilities.ApiConfiguration: The populated ApiConfiguration """ # Get the config keys which contain the mapping between the ApiConfiguration attributes and the variable names # in the secrets.json file and environment variables e.g. token_url is tokenUrl (secrets.json) and # FBN_TOKEN_URL (env variable) with open(Path(__file__).parent.joinpath( 'config_keys.json')) as json_file: config_keys = json.load(json_file) # The secrets file is a nested dictionary, set the names of the top level keys api_config_key = "api" proxy_config_key = "proxy" def _load_config_from_secrets_file(): # If there is a secrets file specified and it exists get the details from it if api_secrets_filename is not None and os.path.exists( api_secrets_filename) and os.path.isfile( api_secrets_filename): with open(api_secrets_filename, "r") as secrets: config = json.load(secrets) # If there is a secrets file specified and it does not exist log a warning to indicate that the specified file # could not be found and create an empty config elif api_secrets_filename is not None and ( not os.path.exists(api_secrets_filename) or not os.path.isfile(api_secrets_filename)): logging.debug( f"Provided secrets file of {api_secrets_filename} can not be found, please ensure you " f"have correctly specified the full path to the file or don't provide a secrets file to use " f"environment variables instead.") config = {} # If no secrets file is specified just create an empty config else: config = {} return config def _get_access_api_config(): config = _load_config_from_secrets_file() # Populate the values for the api configuration preferring the secrets file over the environment variables populated_api_config_values = { key: config.get(api_config_key, {}).get(value["config"], os.getenv(value["env"], None)) for key, value in config_keys.items() if "proxy" not in key } return populated_api_config_values def _get_proxy_api_config(): config = _load_config_from_secrets_file() # Populate the values for the proxy preferring the secrets file over the environment variables populated_proxy_values = { key.replace("proxy_", ""): config.get(proxy_config_key, {}).get(value["config"], os.getenv(value["env"], None)) for key, value in config_keys.items() if "proxy" in key } return populated_proxy_values populated_api_config_values = _get_access_api_config() populated_proxy_values = _get_proxy_api_config() # If the proxy address is missing ensure that no proxy is used in the ApiConfiguration if populated_proxy_values.get("address", None) is None: populated_api_config_values["proxy_config"] = None # Otherwise create a ProxyConfig to use else: populated_api_config_values["proxy_config"] = ProxyConfig( **populated_proxy_values) # Create and return the ApiConfiguration return ApiConfiguration(**populated_api_config_values)