Skip to content

Commit

Permalink
Allow loading client configuration from env (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarosław Wygoda authored and BenRKarl committed Jun 12, 2019
1 parent c7a6c07 commit e2d774d
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 82 deletions.
45 changes: 43 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,53 @@ please refer to `this page of the wiki`_.
Create a GoogleAdsClient
########################

Using YAML file
***************

You can run the following to retrieve a `GoogleAdsClient` instance using a
configuration file named **google-ads.yaml** stored in your home directory:

.. code-block:: python
client = google.ads.google_ads.client.GoogleAdsClient.load_from_storage()
Using environment variables
***************************

You can also retrieve it exporting environment variables.

* Required:

.. code-block:: bash
export GOOGLE_ADS_CLIENT_ID=INSERT_OAUTH2_CLIENT_ID_HERE
export GOOGLE_ADS_CLIENT_SECRET=INSERT_OAUTH2_CLIENT_SECRET_HERE
export GOOGLE_ADS_REFRESH_TOKEN=INSERT_REFRESH_TOKEN_HERE
export GOOGLE_ADS_DEVELOPER_TOKEN=INSERT_DEVELOPER_TOKEN_HERE
* Optional:

.. code-block:: bash
export GOOGLE_ADS_LOGIN_CUSTOMER_ID=INSERT_LOGIN_CUSTOMER_ID_HERE
export GOOGLE_ADS_LOGGING=INSERT_GOOGLE_ADS_LOGGING
.. _GOOGLE_ADS_LOGGING:

GOOGLE_ADS_LOGGING should be a JSON with logging configuration. Example:

.. code-block:: json
{"version": 1, "disable_existing_loggers": false, "formatters": {"default_fmt": {"format": "[%(asctime)s - %(levelname)s] %(message).5000s", "datefmt": "%Y-%m-%d %H:%M:%S"}}, "handlers": {"default_handler": {"class": "logging.StreamHandler", "formatter": "default_fmt"}}, "loggers": {"": {"handlers": ["default_handler"], "level": "INFO"}}}
Then run the following to retrieve a GoogleAdsClient instance:

.. code-block:: python
client = google.ads.google_ads.client.GoogleAdsClient.load_from_env()
Get types and service clients
#############################
You can use a `GoogleAdsClient` instance to retrieve any type or service used
Expand Down Expand Up @@ -94,8 +134,9 @@ The currently available list of versions is:
Enabling and Configuring logging
################################
The library uses Python's built in logging framework. You can specify your
configuration via the configuration file; see `google-ads.yaml`_
for an example. The library logs to ``stderr`` by default. You can easily pipe
configuration via the configuration file (see `google-ads.yaml`_
for an example) or GOOGLE_ADS_LOGGING_ environment variable.
The library logs to ``stderr`` by default. You can easily pipe
log messages to a file; when running an example:

.. code-block:: bash
Expand Down
90 changes: 82 additions & 8 deletions google/ads/google_ads/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,24 @@
_VALID_API_VERSIONS = ['v1']
_DEFAULT_VERSION = _VALID_API_VERSIONS[0]
_REQUEST_ID_KEY = 'request-id'
_ENV_PREFIX = 'GOOGLE_ADS_'
_KEYS_ENV_VARIABLES_MAP = {
key: _ENV_PREFIX + key.upper() for key in
list(_REQUIRED_KEYS) + ['login_customer_id', 'endpoint', 'logging']
}

class GoogleAdsClient(object):
"""Google Ads client used to configure settings and fetch services."""

@classmethod
def _get_client_kwargs_from_yaml(cls, yaml_str):
"""Utility function used to load client kwargs from YAML string.
def _get_client_kwargs(cls, config_data, error_message):
"""Utility function used to load client kwargs from configuration data
dictionary.
Args:
yaml_str: a str containing client configuration in YAML format.
config_data: a dict containing client configuration.
error_message: error message raised when config_data lacks a
required key
Returns:
A dict containing configuration data that will be provided to the
Expand All @@ -56,8 +64,6 @@ def _get_client_kwargs_from_yaml(cls, yaml_str):
Raises:
ValueError: If the configuration lacks a required field.
"""
config_data = yaml.safe_load(yaml_str) or {}

if all(required_key in config_data for required_key in _REQUIRED_KEYS):
credentials = Credentials(
None,
Expand All @@ -77,9 +83,64 @@ def _get_client_kwargs_from_yaml(cls, yaml_str):
'login_customer_id': login_customer_id,
'logging_config': config_data.get('logging')}
else:
raise ValueError('A required field in the configuration data was'
'not found. The required fields are: %s'
% str(_REQUIRED_KEYS))
raise ValueError(error_message)

@classmethod
def _get_client_kwargs_from_env(cls):
"""Utility function used to load client kwargs from env variables.
Returns:
A dict containing configuration data that will be provided to the
GoogleAdsClient initializer as keyword arguments.
Raises:
ValueError: If the environment lacks a required field or
GOOGLE_ADS_LOGGING env variable is not in JSON format.
"""
config_data = {
key: os.environ[env_variable]
for key, env_variable in _KEYS_ENV_VARIABLES_MAP.items()
if env_variable in os.environ
}
if 'logging' in config_data.keys():
try:
config_data['logging'] = json.loads(config_data['logging'])
except json.JSONDecodeError:
raise ValueError(
'GOOGLE_ADS_LOGGING env variable should be in JSON format.'
)
return cls._get_client_kwargs(
config_data,
'A required variable was not found in the environment. The '
'required environment variables are: %s'
% str(
tuple(
v for k, v in _KEYS_ENV_VARIABLES_MAP.items()
if k in _REQUIRED_KEYS
)
)
)

@classmethod
def _get_client_kwargs_from_yaml(cls, yaml_str):
"""Utility function used to load client kwargs from YAML string.
Args:
yaml_str: a str containing client configuration in YAML format.
Returns:
A dict containing configuration data that will be provided to the
GoogleAdsClient initializer as keyword arguments.
Raises:
ValueError: If the configuration lacks a required field.
"""
config_data = yaml.safe_load(yaml_str) or {}
return cls._get_client_kwargs(
config_data,
'A required field in the configuration data was not found. The '
'required fields are: %s' % str(_REQUIRED_KEYS)
)

@classmethod
def get_type(cls, name, version=_DEFAULT_VERSION):
Expand All @@ -106,6 +167,19 @@ def get_type(cls, name, version=_DEFAULT_VERSION):
'API %s.' % (name, version))
return message_type()

@classmethod
def load_from_env(cls):
"""Creates a GoogleAdsClient with data stored in the env variables.
Returns:
A GoogleAdsClient initialized with the values specified in the
env variables.
Raises:
ValueError: If the configuration lacks a required field.
"""
return cls(**cls._get_client_kwargs_from_env())

@classmethod
def load_from_string(cls, yaml_str):
"""Creates a GoogleAdsClient with data stored in the YAML string.
Expand Down
Loading

0 comments on commit e2d774d

Please sign in to comment.