Skip to content

Commit 2679cb9

Browse files
author
Ran Isenberg
committed
added abstract schema fetcher class
1 parent b0c9daa commit 2679cb9

File tree

7 files changed

+98
-33
lines changed

7 files changed

+98
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
"""Advanced feature toggles utility
22
"""
3+
from .appconfig_fetcher import AppConfigFetcher
34
from .configuration_store import ConfigurationStore
45
from .exceptions import ConfigurationException
56
from .schema import ACTION, SchemaValidator
7+
from .schema_fetcher import SchemaFetcher
68

79
__all__ = [
810
"ConfigurationException",
911
"ConfigurationStore",
1012
"ACTION",
1113
"SchemaValidator",
14+
"AppConfigFetcher",
15+
"SchemaFetcher",
1216
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import logging
2+
from typing import Any, Dict, Optional
3+
4+
from botocore.config import Config
5+
6+
from aws_lambda_powertools.utilities.parameters import AppConfigProvider, GetParameterError, TransformParameterError
7+
8+
from .exceptions import ConfigurationException
9+
from .schema_fetcher import SchemaFetcher
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
TRANSFORM_TYPE = "json"
15+
16+
17+
class AppConfigFetcher(SchemaFetcher):
18+
def __init__(
19+
self,
20+
environment: str,
21+
service: str,
22+
configuration_name: str,
23+
cache_seconds: int,
24+
config: Optional[Config] = None,
25+
):
26+
"""This class fetches JSON schemas from AWS AppConfig
27+
28+
Args:
29+
environment (str): what appconfig environment to use 'dev/test' etc.
30+
service (str): what service name to use from the supplied environment
31+
configuration_name (str): what configuration to take from the environment & service combination
32+
cache_seconds (int): cache expiration time, how often to call AppConfig to fetch latest configuration
33+
config (Optional[Config]): boto3 client configuration
34+
"""
35+
super().__init__(configuration_name, cache_seconds)
36+
self._logger = logger
37+
self._conf_store = AppConfigProvider(environment=environment, application=service, config=config)
38+
39+
def get_json_configuration(self) -> Dict[str, Any]:
40+
"""Get configuration string from AWs AppConfig and return the parsed JSON dictionary
41+
42+
Raises:
43+
ConfigurationException: Any validation error or appconfig error that can occur
44+
45+
Returns:
46+
Dict[str, Any]: parsed JSON dictionary
47+
"""
48+
try:
49+
return self._conf_store.get(
50+
name=self.configuration_name,
51+
transform=TRANSFORM_TYPE,
52+
max_age=self._cache_seconds,
53+
) # parse result conf as JSON, keep in cache for self.max_age seconds
54+
except (GetParameterError, TransformParameterError) as exc:
55+
error_str = f"unable to get AWS AppConfig configuration file, exception={str(exc)}"
56+
self._logger.error(error_str)
57+
raise ConfigurationException(error_str)

aws_lambda_powertools/utilities/feature_toggles/configuration_store.py

+9-28
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,23 @@
1-
# pylint: disable=no-name-in-module,line-too-long
21
import logging
32
from typing import Any, Dict, List, Optional
43

5-
from botocore.config import Config
6-
7-
from aws_lambda_powertools.utilities.parameters import AppConfigProvider, GetParameterError, TransformParameterError
8-
94
from . import schema
105
from .exceptions import ConfigurationException
11-
12-
TRANSFORM_TYPE = "json"
6+
from .schema_fetcher import SchemaFetcher
137

148
logger = logging.getLogger(__name__)
159

1610

1711
class ConfigurationStore:
18-
def __init__(
19-
self, environment: str, service: str, conf_name: str, cache_seconds: int, config: Optional[Config] = None
20-
):
12+
def __init__(self, schema_fetcher: SchemaFetcher):
2113
"""constructor
2214
2315
Args:
24-
environment (str): what appconfig environment to use 'dev/test' etc.
25-
service (str): what service name to use from the supplied environment
26-
conf_name (str): what configuration to take from the environment & service combination
27-
cache_seconds (int): cache expiration time, how often to call AppConfig to fetch latest configuration
16+
schema_fetcher (SchemaFetcher): A schema JSON fetcher, can be AWS AppConfig, Hashicorp Consul etc.
2817
"""
29-
self._cache_seconds = cache_seconds
3018
self._logger = logger
31-
self._conf_name = conf_name
19+
self._schema_fetcher = schema_fetcher
3220
self._schema_validator = schema.SchemaValidator(self._logger)
33-
self._conf_store = AppConfigProvider(environment=environment, application=service, config=config)
3421

3522
def _match_by_action(self, action: str, condition_value: Any, context_value: Any) -> bool:
3623
if not context_value:
@@ -99,17 +86,11 @@ def get_configuration(self) -> Dict[str, Any]:
9986
Returns:
10087
Dict[str, Any]: parsed JSON dictionary
10188
"""
102-
try:
103-
schema = self._conf_store.get(
104-
name=self._conf_name,
105-
transform=TRANSFORM_TYPE,
106-
max_age=self._cache_seconds,
107-
) # parse result conf as JSON, keep in cache for self.max_age seconds
108-
except (GetParameterError, TransformParameterError) as exc:
109-
error_str = f"unable to get AWS AppConfig configuration file, exception={str(exc)}"
110-
self._logger.error(error_str)
111-
raise ConfigurationException(error_str)
112-
89+
schema: Dict[
90+
str, Any
91+
] = (
92+
self._schema_fetcher.get_json_configuration()
93+
) # parse result conf as JSON, keep in cache for self.max_age seconds
11394
# validate schema
11495
self._schema_validator.validate_json_schema(schema)
11596
return schema
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
class ConfigurationException(Exception):
2-
"""When a a configuration store raises an exception on schema retrieval or parsing"""
2+
"""When a a configuration store raises an exception on config retrieval or parsing"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from abc import ABC, abstractclassmethod
2+
from typing import Any, Dict
3+
4+
5+
class SchemaFetcher(ABC):
6+
def __init__(self, configuration_name: str, cache_seconds: int):
7+
self.configuration_name = configuration_name
8+
self._cache_seconds = cache_seconds
9+
10+
@abstractclassmethod
11+
def get_json_configuration(self) -> Dict[str, Any]:
12+
"""Get configuration string from any configuration storing service and return the parsed JSON dictionary
13+
14+
Raises:
15+
ConfigurationException: Any error that can occur during schema fetch or JSON parse
16+
17+
Returns:
18+
Dict[str, Any]: parsed JSON dictionary
19+
"""
20+
return None

tests/functional/feature_toggles/test_feature_toggles.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest # noqa: F401
44
from botocore.config import Config
55

6+
from aws_lambda_powertools.utilities.feature_toggles.appconfig_fetcher import AppConfigFetcher
67
from aws_lambda_powertools.utilities.feature_toggles.configuration_store import ConfigurationStore
78
from aws_lambda_powertools.utilities.feature_toggles.schema import ACTION
89

@@ -15,13 +16,15 @@ def config():
1516
def init_configuration_store(mocker, mock_schema: Dict, config: Config) -> ConfigurationStore:
1617
mocked_get_conf = mocker.patch("aws_lambda_powertools.utilities.parameters.AppConfigProvider.get")
1718
mocked_get_conf.return_value = mock_schema
18-
conf_store: ConfigurationStore = ConfigurationStore(
19+
20+
app_conf_fetcher = AppConfigFetcher(
1921
environment="test_env",
2022
service="test_app",
21-
conf_name="test_conf_name",
23+
configuration_name="test_conf_name",
2224
cache_seconds=600,
2325
config=config,
2426
)
27+
conf_store: ConfigurationStore = ConfigurationStore(schema_fetcher=app_conf_fetcher)
2528
return conf_store
2629

2730

tests/functional/feature_toggles/test_schema_validation.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
from aws_lambda_powertools.utilities.feature_toggles.exceptions import ConfigurationException
66
from aws_lambda_powertools.utilities.feature_toggles.schema import (
77
ACTION,
8-
FEATURE_DEFAULT_VAL_KEY,
9-
FEATURES_KEY,
108
CONDITION_ACTION,
119
CONDITION_KEY,
1210
CONDITION_VALUE,
1311
CONDITIONS_KEY,
12+
FEATURE_DEFAULT_VAL_KEY,
13+
FEATURES_KEY,
1414
RULE_DEFAULT_VALUE,
1515
RULE_NAME_KEY,
1616
RULES_KEY,

0 commit comments

Comments
 (0)