Skip to content

Commit

Permalink
WIP: Add schema and from_file classmethod
Browse files Browse the repository at this point in the history
  • Loading branch information
JimMadge committed Oct 27, 2023
1 parent 5e6726d commit 3c2ad43
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 31 deletions.
83 changes: 52 additions & 31 deletions data_safe_haven/config/backend_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import appdirs
import yaml
from schema import Schema, SchemaError
from yaml.parser import ParserError

from data_safe_haven.exceptions import (
Expand All @@ -14,6 +15,12 @@
from data_safe_haven.utility import LoggingSingleton


config_directory = pathlib.Path(
appdirs.user_config_dir(appname="data_safe_haven")
).resolve()
config_file_path = config_directory / "config.yaml"


@dataclass
class Context:
admin_group_id: str
Expand All @@ -35,43 +42,39 @@ class ContextSettings:
...
"""

def __init__(self) -> None:
def __init__(self, settings_dict: dict[Any, Any]) -> None:
self.logger = LoggingSingleton()

self._settings: dict[Any, Any] | None = None

config_directory = pathlib.Path(
appdirs.user_config_dir(appname="data_safe_haven")
).resolve()
self.config_file_path = config_directory / "config.yaml"
context_schema = Schema({
"name": str,
"admin_group_id": str,
"location": str,
"subscription_name": str,
})

schema = Schema({
"selected": str,
"contexts": Schema({
str: context_schema,
}),
})

try:
self._settings: schema.validate(settings_dict)
except SchemaError as exc:
msg = f"Invalid context configuration file.\n{exc}"
raise DataSafeHavenParameterError(msg)

@property
def settings(self) -> dict[Any, Any]:
if not self._settings:
try:
with open(self.config_file_path, encoding="utf-8") as f_yaml:
settings = yaml.safe_load(f_yaml)
if isinstance(settings, dict):
self.logger.info(
f"Reading project settings from '[green]{self.config_file_path}[/]'."
)
self._settings = settings
self._context = Context(**settings.get("contexts").get(self._selected))
except FileNotFoundError as exc:
msg = f"Could not find file {self.config_file_path}.\n{exc}"
raise DataSafeHavenConfigError(msg) from exc
except ParserError as exc:
msg = f"Could not load settings from {self.config_file_path}.\n{exc}"
raise DataSafeHavenConfigError(msg) from exc

return self._settings

@property
def selected(self) -> str:
if selected := self.settings.get("selected"):
return selected
else:
msg = f"Selected context is not defined in {self.config_file_path}."
msg = "Selected context is not defined."
raise DataSafeHavenParameterError(msg)

@selected.setter
Expand All @@ -80,15 +83,15 @@ def selected(self, context_name: str) -> None:
self.settings["selected"] = context_name
self.logger.info(f"Switched context to {context_name}.")
else:
msg = f"Context {context_name} is not defined in {self.config_file_path}."
msg = f"Context {context_name} is not defined."
raise DataSafeHavenParameterError(msg)

@property
def context(self) -> Context:
if context_dict := self.settings.get("contexts").get(self.selected):
return Context(**context_dict)
else:
msg = f"Context {self.selected} is not defined in {self.config_file_path}."
msg = f"Context {self.selected} is not defined."
raise DataSafeHavenParameterError(msg)

def update(
Expand Down Expand Up @@ -118,13 +121,31 @@ def update(
)
context_dict["subscription_name"] = subscription_name

def write(self) -> None:
def write(self, config_file_path: str = config_file_path) -> None:
"""Write settings to YAML file"""
# Create the parent directory if it does not exist then write YAML
self.config_file_path.parent.mkdir(parents=True, exist_ok=True)
config_file_path.parent.mkdir(parents=True, exist_ok=True)

with open(self.config_file_path, "w", encoding="utf-8") as f_yaml:
with open(config_file_path, "w", encoding="utf-8") as f_yaml:
yaml.dump(self.settings, f_yaml, indent=2)
self.logger.info(
f"Saved context settings to '[green]{self.config_file_path}[/]'."
f"Saved context settings to '[green]{config_file_path}[/]'."
)

@classmethod
def from_file(cls, config_file_path: str = config_file_path) -> None:
logger = LoggingSingleton()
try:
with open(config_file_path, encoding="utf-8") as f_yaml:
settings = yaml.safe_load(f_yaml)
if isinstance(settings, dict):
logger.info(
f"Reading project settings from '[green]{config_file_path}[/]'."
)
return cls(settings)
except FileNotFoundError as exc:
msg = f"Could not find file {config_file_path}.\n{exc}"
raise DataSafeHavenConfigError(msg) from exc
except ParserError as exc:
msg = f"Could not load settings from {config_file_path}.\n{exc}"
raise DataSafeHavenConfigError(msg) from exc
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies = [
"pytz~=2022.7.0",
"PyYAML~=6.0",
"rich~=13.4.2",
"schema~=0.7.0",
"simple-acme-dns~=1.2.0",
"typer~=0.9.0",
"websocket-client~=1.5.0",
Expand Down

0 comments on commit 3c2ad43

Please sign in to comment.