Skip to content

Commit

Permalink
add configuration option for datagateway and panosc mode #256
Browse files Browse the repository at this point in the history
BREAKING CHANGE: extend configuration to allow for different API modes
  • Loading branch information
Viktor Bozhinov committed Nov 9, 2021
1 parent b57c127 commit be64faf
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 158 deletions.
128 changes: 79 additions & 49 deletions datagateway_api/common/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,67 +17,22 @@
log = logging.getLogger()


class TestUserCredentials(BaseModel):
username: StrictStr
password: StrictStr


class APIConfig(BaseModel):
class DatagatewayAPI(BaseModel):
"""
Configuration model class that implements pydantic's BaseModel class to allow for
validation of the API config data using Python type annotations. It ensures that
all config options exist before getting too far into the setup of the API. It takes
the backend into account, meaning only the config options for the backend used is
validation of the DatagatewayAPI config data using Python type annotations. It takes
the backend into account, meaning only the config options for the backend used are
required.
If a mandatory config option is missing or misspelled, or has a wrong value type,
Pydantic raises a validation error with a breakdown of what was wrong and the
application is exited.
Config options used for testing are not checked here as they should only be used
during tests, not in the typical running of the API.
Some options used when running the API (host, debug_mode etc.) aren't mandatory
when running the API in production (these options aren't used in the `wsgi.py`
entrypoint). As a result, they're not present in `config_keys`. However, they
are required when using `main.py` as an entrypoint. In any case of these
specific missing config options when using that entrypoint, they are checked at
API startup so any missing options will be caught quickly.
"""

backend: StrictStr
client_cache_size: Optional[StrictInt]
client_pool_init_size: Optional[StrictInt]
client_pool_max_size: Optional[StrictInt]
db_url: Optional[StrictStr]
debug_mode: StrictBool
flask_reloader: StrictBool
generate_swagger: StrictBool
host: StrictStr
extension: StrictStr
icat_check_cert: Optional[StrictBool]
icat_url: Optional[StrictStr]
log_level: StrictStr
log_location: StrictStr
port: StrictStr
test_mechanism: StrictStr
test_user_credentials: TestUserCredentials

@classmethod
def load(cls, path=Path(__file__).parent.parent / "config.json"):
"""
Loads the config data from the JSON file and returns it as a APIConfig pydantic
model. Exits the application if it fails to locate the JSON config file or
the APIConfig model validation fails.
:param path: path to the configuration file
:return: APIConfig model object that contains the config data
"""
try:
with open(path, encoding="utf-8") as target:
data = json.load(target)
return cls(**data)
except (IOError, ValidationError) as error:
sys.exit(f"An error occurred while trying to load the config data: {error}")

@validator("db_url", always=True)
def require_db_config_value(cls, value, values): # noqa: B902, N805
Expand Down Expand Up @@ -136,7 +91,82 @@ def set_backend_type(self, backend_type):
self.backend = backend_type

class Config:
"""
The behaviour of the BaseModel class can be controlled via this class.
"""

# Enables assignment validation on the BaseModel fields. Useful for when the
# backend type is changed using the set_backend_type function.
validate_assignment = True


class SearchAPI(BaseModel):
"""
Configuration model class that implements pydantic's BaseModel class to allow for
validation of the SearchAPI config data using Python type annotations.
"""

client_pool_init_size: StrictInt
client_pool_max_size: StrictInt
extension: StrictStr
icat_check_cert: StrictBool
icat_url: StrictStr


class TestUserCredentials(BaseModel):
username: StrictStr
password: StrictStr


class APIConfig(BaseModel):
"""
Configuration model class that implements pydantic's BaseModel class to allow for
validation of the API config data using Python type annotations. It ensures that
all required config options exist before getting too far into the setup of the API.
If a mandatory config option is missing or misspelled, or has a wrong value type,
Pydantic raises a validation error with a breakdown of what was wrong and the
application is exited.
Config options used for testing are not checked here as they should only be used
during tests, not in the typical running of the API.
Some options used when running the API (host, debug_mode etc.) aren't mandatory
when running the API in production (these options aren't used in the `wsgi.py`
entrypoint). As a result, they're not present in `config_keys`. However, they
are required when using `main.py` as an entrypoint. In any case of these
specific missing config options when using that entrypoint, they are checked at
API startup so any missing options will be caught quickly.
"""

datagateway_api: Optional[DatagatewayAPI]
debug_mode: StrictBool
flask_reloader: StrictBool
generate_swagger: StrictBool
host: StrictStr
log_level: StrictStr
log_location: StrictStr
port: StrictStr
search_api: Optional[SearchAPI]
test_mechanism: StrictStr
test_user_credentials: TestUserCredentials

@classmethod
def load(cls, path=Path(__file__).parent.parent / "config.json"):
"""
Loads the config data from the JSON file and returns it as a APIConfig pydantic
model. Exits the application if it fails to locate the JSON config file or
the APIConfig model validation fails.
:param path: path to the configuration file
:return: APIConfig model object that contains the config data
"""
try:
with open(path, encoding="utf-8") as target:
data = json.load(target)
return cls(**data)
except (IOError, ValidationError) as error:
sys.exit(f"An error occurred while trying to load the config data: {error}")


config = APIConfig.load()
4 changes: 3 additions & 1 deletion datagateway_api/common/datagateway_api/icat/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ def __init__(self, skip_value):
super().__init__(skip_value)

def apply_filter(self, query):
icat_properties = get_icat_properties(config.icat_url, config.icat_check_cert)
icat_properties = get_icat_properties(
config.datagateway_api.icat_url, config.datagateway_api.icat_check_cert,
)
icat_set_limit(query, self.skip_value, icat_properties["maxEntities"])


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ class ICATClient(Client):

def __init__(self):
super().__init__(
config.icat_url, checkCert=config.icat_check_cert,
config.datagateway_api.icat_url,
checkCert=config.datagateway_api.icat_check_cert,
)
# When clients are cleaned up, sessions won't be logged out
self.autoLogout = False
Expand All @@ -35,8 +36,8 @@ def create_client_pool():

return ObjectPool(
ICATClient,
min_init=config.client_pool_init_size,
max_capacity=config.client_pool_max_size,
min_init=config.datagateway_api.client_pool_init_size,
max_capacity=config.datagateway_api.client_pool_max_size,
max_reusable=0,
expires=0,
)
2 changes: 1 addition & 1 deletion datagateway_api/common/datagateway_api/icat/lru_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ExtendedLRUCache(LRUCache):
"""

def __init__(self):
super().__init__(maxsize=config.client_cache_size)
super().__init__(maxsize=config.datagateway_api.client_cache_size)

def popitem(self):
key, client = super().popitem()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def get_query_filter(request_filter):
:raises FilterError: If the filter name is not recognised
"""

backend_type = config.backend
backend_type = config.datagateway_api.backend
if backend_type == "db":
from datagateway_api.common.datagateway_api.database.filters import (
DatabaseDistinctFieldFilter as DistinctFieldFilter,
Expand Down
42 changes: 26 additions & 16 deletions datagateway_api/config.json.example
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
{
"backend": "db",
"client_cache_size": 5,
"client_pool_init_size": 2,
"client_pool_max_size": 5,
"db_url": "mysql+pymysql://icatdbuser:icatdbuserpw@localhost:3306/icatdb",
"flask_reloader": false,
"icat_url": "https://localhost:8181",
"icat_check_cert": false,
"log_level": "WARN",
"log_location": "/home/runner/work/datagateway-api/datagateway-api/logs.log",
"debug_mode": false,
"generate_swagger": false,
"host": "127.0.0.1",
"port": "5000",
"test_user_credentials": {"username": "root", "password": "pw"},
"test_mechanism": "simple"
"datagateway_api": {
"extension": "/datagateway-api",
"backend": "db",
"client_cache_size": 5,
"client_pool_init_size": 2,
"client_pool_max_size": 5,
"db_url": "mysql+pymysql://icatdbuser:icatdbuserpw@localhost:3306/icatdb",
"icat_url": "https://localhost:8181",
"icat_check_cert": false
},
"search_api": {
"extension": "/search-api",
"icat_url": "https://localhost:8181",
"icat_check_cert": false,
"client_pool_init_size": 2,
"client_pool_max_size": 5
},
"flask_reloader": false,
"log_level": "WARN",
"log_location": "/home/runner/work/datagateway-api/datagateway-api/logs.log",
"debug_mode": false,
"generate_swagger": false,
"host": "127.0.0.1",
"port": "5000",
"test_user_credentials": {"username": "root", "password": "pw"},
"test_mechanism": "simple"
}
Loading

0 comments on commit be64faf

Please sign in to comment.