-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Chore: Refactor Config Module #87
base: main
Are you sure you want to change the base?
Conversation
0840424
to
ecec2ff
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The current Config service does not seems to be similar as the one that we are using in mern-boilerplate We should expose just two methods from this class e.g:
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
24b2764
to
73b87ea
Compare
This comment has been minimized.
This comment has been minimized.
73b87ea
to
38a23b5
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
removed unused settings and config_manager modified config_service to be the class that handles configuration modified all instances of config_manager to use config_service
- Overridden optionxform in ConfigParser to preserve the case of keys - Ensured environment variables are matched with original case from .ini files
fix: improved file readablity
refactor: created a config file which simulates node-config refactor: modified config_service to extend config so that it reflects mern-boilerplate
…lues function to improve code smell
5d5b7e4
to
817c48f
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
import yaml | ||
|
||
|
||
def get_parent_directory(directory: str, levels: int) -> Path: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid creating methods outside class
|
||
class Config: | ||
config_dict: dict[str, Any] | ||
config_path: Path = get_parent_directory(__file__, 6) / "config" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid magic numbers like 6
which are not readable.
return parent_dir | ||
|
||
|
||
class Config: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is not supposed to be file consumed by other modules directly, this should live inside internals
folder
value = Config.get(key) | ||
if value is None: | ||
raise MissingKeyError(missing_key=key, error_code=ErrorCode.MISSING_KEY) | ||
return value # type: ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have this?
How are we enforcing type safety? For example if some asked for bool
but found a string, that should throw.
return yaml_content | ||
|
||
@staticmethod | ||
def load_config() -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- If this is the only public method, move to top of the class.
- Mark all internal methods to start with
_
- Consider improving readability of the code overall from code organization, naming and others
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Deployment (frm-boilerplate) is available at - https://f55a0c2629bdc00.preview.platform.jalantechnologies.com |
Minimum allowed line rate is |
|
||
T = TypeVar('T') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to bound the values of T
?
def has_key(key: str) -> bool: | ||
return key in ConfigManager.config | ||
@classmethod | ||
def get_value(cls,key: str) -> T: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: add space after ,
def load_config() -> None: | ||
Config._intialize_config() | ||
Config._process_custom_environment_variables() | ||
print(yaml.dump(Config._config_dict)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we please avoid this?
|
||
@staticmethod | ||
def get(key: str, default: Optional[Any] = None) -> Any: | ||
keys = key.split(".") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using magic string or number. You can make this code readable by storing .
in a constant variable, something of the form - CONFIG_KEY_SEPARATOR
or something to that effect.
|
||
@staticmethod | ||
def get(key: str, default: Optional[Any] = None) -> Any: | ||
keys = key.split(".") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recommend to use better naming convention here, and below. What you have fully qualified namespace for the key.
for k in keys: | ||
value = value[k] | ||
return value | ||
except (KeyError, TypeError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add comments in the code when system would throw these errors (under what circumstances)?
keys = key.split(".") | ||
value = Config._config_dict | ||
try: | ||
for k in keys: | ||
value = value[k] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First 5 lines is repeating here, and in get
method. Consider refactoring.
Config._config_dict = merge_content | ||
|
||
@staticmethod | ||
def _parse_value(value:Optional[str], value_format:str)-> Any: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: add space after :
Kindly scan your code to ensure we are using spacing properly.
Config._config_dict = merge_content | ||
|
||
@staticmethod | ||
def _parse_value(value:Optional[str], value_format:str)-> Any: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this need be converted to use generics?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is still a lot of code in this file which is hard to follow and its flow. There are opportunity to think through this and break into appropriate entities, encapsulate behaviour, possibly using OOPS. This is largely created using functional programming and its hard to follow all the functions and how and when they are called, which is making them hard to read.
Description
This PR refactors the
Config
module of the boilerplate to improve its readability, maintainability, and functionality. It removes the existing service manager and introduces enhancements inspired by the MERN boilerplate.Changes
pyaml
for reading.yml
configuration files.ConfigService
to closely align with the structure and functionality of the MERN boilerplate.Custom Environment Variables
Some deployment scenarios require secrets and settings to be configured via environment variables instead of hardcoding them in the codebase. The
custom-environment-variables.yml
file introduces a YAML-based approach for mapping environment variables into your configuration.To enable custom environment variables, create a configuration file at
config/custom-environment-variables.yml
, mapping environment variable names to your configuration structure. Example:This example checks for the environment variables
WEB_APP_HOST
andINSPECTLET_KEY
to override corresponding config values. ThePAPERTRAIL_PORT
variable is parsed using the specified__format
type (number
in this case).Available
__format
types:boolean
number
Precedence: Custom environment variables override all other configuration files, including
default.yml
and{app_env}.yml
.Database Schema Changes
N/A
Tests
Automated Tests
Manual Test Cases
User Creation
Login
OTP Errors
code
:OTP_ERR_01
, "invalid otp").code
:OTP_ERR_02
, "expired otp").Account Errors
code
:ACCOUNT_ERR_02
, "account not found").code
:ACCOUNT_ERR_02
, "invalid password").code
:ACCOUNT_ERR_02
, "account not found").code
:ACCOUNT_ERR_01
, "account already exists").