feat: domain, use cases and JSON repository with file error handling
Task: MIL-002#1 Task: MIL-002#2 Task: MIL-002#3 Task: MIL-002#4
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""@brief Package marker."""
|
||||
@@ -0,0 +1,31 @@
|
||||
"""@brief Ports the use cases need from the outside.
|
||||
|
||||
@file ports.py
|
||||
"""
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from password_manager.domain.password_entry import PasswordEntry
|
||||
|
||||
|
||||
class PasswordRepository(Protocol):
|
||||
"""@brief Storage of password entries."""
|
||||
|
||||
def add(self, entry: PasswordEntry) -> None:
|
||||
"""@brief Store an entry, keeping every other entry.
|
||||
|
||||
@param entry The entry to store; replaces one for the same website.
|
||||
@throws InvalidDataFileError The stored data has an unexpected format.
|
||||
"""
|
||||
...
|
||||
|
||||
def find(self, website: str) -> PasswordEntry:
|
||||
"""@brief Look an entry up.
|
||||
|
||||
@param website The website to look for.
|
||||
@return The stored entry.
|
||||
@throws DataFileNotFoundError There is no storage yet.
|
||||
@throws EntryNotFoundError The website has no entry.
|
||||
@throws InvalidDataFileError The stored data has an unexpected format.
|
||||
"""
|
||||
...
|
||||
@@ -0,0 +1,76 @@
|
||||
"""@brief Use cases of the password manager.
|
||||
|
||||
@file use_cases.py
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
from password_manager.application.ports import PasswordRepository
|
||||
from password_manager.domain.errors import EmptyFieldError
|
||||
from password_manager.domain.password_entry import PasswordEntry
|
||||
from password_manager.domain.password_generator import generate_password
|
||||
|
||||
|
||||
class SavePasswordEntry:
|
||||
"""@brief Validate and store a password entry."""
|
||||
|
||||
def __init__(self, repository: PasswordRepository) -> None:
|
||||
"""@brief Create the use case.
|
||||
|
||||
@param repository Where entries are stored.
|
||||
"""
|
||||
self._repository = repository
|
||||
|
||||
def execute(self, website: str, email: str, password: str) -> None:
|
||||
"""@brief Store the entry.
|
||||
|
||||
@param website Website of the entry.
|
||||
@param email E-mail address or user name.
|
||||
@param password The password.
|
||||
@throws EmptyFieldError A field is empty.
|
||||
"""
|
||||
if not (website.strip() and email.strip() and password.strip()):
|
||||
raise EmptyFieldError
|
||||
self._repository.add(PasswordEntry(website.strip(), email, password))
|
||||
|
||||
|
||||
class FindPasswordEntry:
|
||||
"""@brief Look up the entry of a website."""
|
||||
|
||||
def __init__(self, repository: PasswordRepository) -> None:
|
||||
"""@brief Create the use case.
|
||||
|
||||
@param repository Where entries are stored.
|
||||
"""
|
||||
self._repository = repository
|
||||
|
||||
def execute(self, website: str) -> PasswordEntry:
|
||||
"""@brief Find the entry.
|
||||
|
||||
@param website Website to look for.
|
||||
@return The stored entry.
|
||||
@throws EmptyFieldError The website is empty.
|
||||
@throws DataFileNotFoundError There is no storage yet.
|
||||
@throws EntryNotFoundError The website has no entry.
|
||||
"""
|
||||
if not website.strip():
|
||||
raise EmptyFieldError
|
||||
return self._repository.find(website.strip())
|
||||
|
||||
|
||||
class GeneratePassword:
|
||||
"""@brief Produce a new random password."""
|
||||
|
||||
def __init__(self, rng: random.Random | None = None) -> None:
|
||||
"""@brief Create the use case.
|
||||
|
||||
@param rng Random source; secure by default.
|
||||
"""
|
||||
self._rng = rng
|
||||
|
||||
def execute(self) -> str:
|
||||
"""@brief Generate a password.
|
||||
|
||||
@return The new password.
|
||||
"""
|
||||
return generate_password(self._rng)
|
||||
Reference in New Issue
Block a user