diff --git a/api/api.py b/api/api.py index 3a5f40e..6752d7e 100644 --- a/api/api.py +++ b/api/api.py @@ -7,10 +7,11 @@ import logging import sys +from globals import env from internal import logger_init, check_env_vars, set_log_level from routers.configuration.operations import router as configuration_router -# check if python version is what this is written with: +# python version sanity check if sys.version_info < (3, 10): print('ERROR: Python 3.10+ required.') @@ -31,7 +32,8 @@ logger_init() # check env and use defaults if not present -env = check_env_vars() +# this populates env dict global +check_env_vars() # set logger level based on what we got back set_log_level(env['LOG_LEVEL']) diff --git a/api/internal/globals.py b/api/globals.py similarity index 100% rename from api/internal/globals.py rename to api/globals.py diff --git a/api/internal/__init__.py b/api/internal/__init__.py index e7c5182..9199506 100644 --- a/api/internal/__init__.py +++ b/api/internal/__init__.py @@ -3,4 +3,5 @@ from .logger_init import logger_init from .check_env_vars import check_env_vars from .set_log_level import set_log_level +from .check_conf_server import check_conf_server diff --git a/api/internal/check_conf_server.py b/api/internal/check_conf_server.py new file mode 100644 index 0000000..dc4658f --- /dev/null +++ b/api/internal/check_conf_server.py @@ -0,0 +1,37 @@ + +from typing import Tuple +from xmlrpc.client import Boolean +import redis +from globals import env + + +def check_conf_server( + host_param: str = None, + port_param: str = None) -> Tuple: + """Check if the Redis configuration endpoint is alive. + If host and/or port aren't provided, pull values from REDIS_HOST and REDIS_port + environment variables + + Arguments: + host_param (Optional): the Redis endpoint (.e.g, 'redis://localhost' or '127.0.0.1') + port_param (Optional): the Redis port + + Return: + (status_code, r) tuple where status_code is boolean for the check success, + and r is the Redis handle if successful + """ + + if not host_param: + host_param = env['REDIS_HOST'] + if not port_param: + port_param = env['REDIS_PORT'] + + try: + r = redis.StrictRedis(host=host_param, port=port_param, decode_responses=True ) + if r.ping(): + return True, r + return False, None + + except redis.ConnectionError: + return False, None + diff --git a/api/internal/check_env_vars.py b/api/internal/check_env_vars.py index c2824b9..5305045 100644 --- a/api/internal/check_env_vars.py +++ b/api/internal/check_env_vars.py @@ -1,18 +1,18 @@ -from ast import Dict import os import logging +from typing import Dict +from globals import env -def check_env_vars() -> Dict: - """ - Checks if environment variables we care about are present. +def check_env_vars(): + """Checks if environment variables we care about are present. If so, accept them. If not, substitute a reasonable default. - - Returns: - Dict: returns a dictionary of environment variables and their assigned - values. NOT typesafe. Consumer is resonsible for typecasting as needed. + + Sets globals.env Dict of environment variables and their assigned + values. + + NOT typesafe. Consumer is resonsible for typecasting as needed. """ - # access the 'global' logger logger = logging.getLogger('api') @@ -36,8 +36,6 @@ def check_env_vars() -> Dict: all_env_vars = dict(os.environ) - env = {} - logger.debug(f'all_env_vars: {all_env_vars}') logger.debug(f'env = {env}') @@ -50,5 +48,3 @@ def check_env_vars() -> Dict: logger.debug(f'env: {var}={env[var]} (default)') logger.debug(f'env = {env}') - - return env diff --git a/api/routers/configuration/delete.py b/api/routers/configuration/delete.py index c845213..9ac9ff8 100644 --- a/api/routers/configuration/delete.py +++ b/api/routers/configuration/delete.py @@ -1,8 +1,10 @@ # Configuration DELETE from ast import Dict +from fastapi import Response, status def delete( + response: Response, instance_param: str = None, section_param: str = None ) -> Dict: diff --git a/api/routers/configuration/get.py b/api/routers/configuration/get.py index b31a3d0..48bee47 100644 --- a/api/routers/configuration/get.py +++ b/api/routers/configuration/get.py @@ -1,14 +1,42 @@ # Configuration GET +from fastapi import Response, status +from globals import env from ast import Dict +from internal.check_conf_server import check_conf_server def get( + response: Response, instance_param: str = None, section_param: str = None ) -> Dict: + """Implement GET method for + /v1/configuration + /v1/configuration/{instance} + + /v1/configuration/{instance}/{section} + + Args: + response (Response): pass through for setting specific status + instance_param (str, optional): Name of the instance. Defaults to None. + section_param (str, optional): Name of the configuration section. Defaults to None. + + Returns: + Dict: response from the operation + + HTTP Status Codes: + 424: Redis failure + """ return_dict = {'instance': instance_param, 'section': section_param} -# redis code goes here + # do we have a working Redis connection? + redis_status, r = check_conf_server() + if not redis_status: + response.status_code = status.HTTP_424_FAILED_DEPENDENCY + return { 'message': 'redis connection failed' } + # we have a working redis connection + + # do something useful with redis return return_dict diff --git a/api/routers/configuration/operations.py b/api/routers/configuration/operations.py index 8094ebe..7a4ab83 100644 --- a/api/routers/configuration/operations.py +++ b/api/routers/configuration/operations.py @@ -1,7 +1,7 @@ # Configuration operations from enum import Enum -from fastapi import APIRouter, Body +from fastapi import APIRouter, Body, Response from .get import get as configuration_get from .put import put as configuration_put @@ -27,10 +27,22 @@ class SectionName(str, Enum): @router.get('/configuration/{instance}') @router.get('/configuration/{instance}/{section}') async def get_config( + response: Response, instance: str = None, section: SectionName = None ): + """Handler for GET operation on configuration + + Args: + response (Response): HTTP status code pass-through + instance (str, optional): Name of the instance. Defaults to None. + section (SectionName, optional): Name of the configuration section. Defaults to None. + + Returns: + Dict: HTTP operation response + """ return configuration_get( + response=response, instance_param=instance, section_param=section ) @@ -43,12 +55,14 @@ async def get_config( @router.put('/configuration/{instance}') @router.put('/configuration/{instance}/{section}') async def put_config( + response: Response, instance: str = None, section: SectionName = None, body: dict = Body (...) ): return configuration_put( + response=response, instance_param=instance, section_param=section ) diff --git a/api/routers/configuration/patch.py b/api/routers/configuration/patch.py index 872ffc0..a537377 100644 --- a/api/routers/configuration/patch.py +++ b/api/routers/configuration/patch.py @@ -1,8 +1,10 @@ # Configuration PATCH from ast import Dict +from fastapi import Response, status def patch( + response: Response, instance_param: str = None, section_param: str = None ) -> Dict: diff --git a/api/routers/configuration/put.py b/api/routers/configuration/put.py index 2eb85da..18b6a7f 100644 --- a/api/routers/configuration/put.py +++ b/api/routers/configuration/put.py @@ -1,8 +1,10 @@ # Configuration PUT from ast import Dict +from fastapi import Response, status def put( + response: Response, instance_param: str = None, section_param: str = None ) -> Dict: