basis/components/users.py
apeters 3f3a7ba2d5 -
Signed-off-by: apeters <apeters@korves.net>
2025-05-27 07:25:27 +00:00

243 lines
7.3 KiB
Python

from components.models.users import (
AddCredential,
CredentialPatch,
Credential,
User,
UserAdd,
UserPatch,
UserProfile,
UserProfilePatch,
UserSession,
constr,
validate_call,
UUID,
)
from components.utils import merge_models
from components.database import *
from components.cache import buster
def _create_credentials_mapping(credentials: dict):
user_credentials = dict()
for c in credentials:
user_credentials.update({c["id"]: Credential.model_validate(c)})
return user_credentials
@validate_call
async def what_id(login: str):
db_params = evaluate_db_params()
async with TinyDB(**db_params) as db:
user = db.table("users").get(Query().login == login)
if user:
return user["id"]
else:
raise ValueError("login", "The provided login name is unknown")
@validate_call
async def create(data: dict):
db_params = evaluate_db_params()
create_user = UserAdd.model_validate(data)
async with TinyDB(**db_params) as db:
if db.table("users").search(Query().login == create_user.login):
raise ValueError("name", "The provided login name exists")
insert_data = create_user.model_dump(mode="json")
db.table("users").insert(insert_data)
for user_id in IN_MEMORY_DB["CACHE"]["FORMS"].copy():
if "users" in IN_MEMORY_DB["CACHE"]["FORMS"][user_id]:
del IN_MEMORY_DB["CACHE"]["FORMS"][user_id]["users"]
return insert_data["id"]
@validate_call
async def get(user_id: UUID, join_credentials: bool = True):
db_params = evaluate_db_params()
system_id = "00000000-0000-0000-0000-000000000000"
if not IN_MEMORY_DB["CACHE"]["MODELS"].get(system_id):
IN_MEMORY_DB["CACHE"]["MODELS"][system_id] = dict()
async with TinyDB(**db_params) as db:
if not str(user_id) in IN_MEMORY_DB["CACHE"]["MODELS"][system_id]:
print(
User.model_validate(db.table("users").get(Query().id == str(user_id)))
)
IN_MEMORY_DB["CACHE"]["MODELS"][system_id][
str(user_id)
] = User.model_validate(db.table("users").get(Query().id == str(user_id)))
user = IN_MEMORY_DB["CACHE"]["MODELS"][system_id][str(user_id)].copy()
credentials = db.table("credentials").search(
(Query().id.one_of(user.credentials))
)
if join_credentials:
user.credentials = _create_credentials_mapping(credentials)
return user
@validate_call
async def delete(user_id: UUID):
db_params = evaluate_db_params()
user = await get(user_id=user_id, join_credentials=False)
if not user:
raise ValueError("name", "The provided user does not exist")
async with TinyDB(**db_params) as db:
if len(db.table("users").all()) == 1:
raise ValueError("name", "Cannot delete last user")
db.table("credentials").remove(Query().id.one_of(user.credentials))
deleted = db.table("users").remove(Query().id == str(user_id))
buster(user.id)
return user.id
@validate_call
async def create_credential(user_id: UUID, data: dict):
db_params = evaluate_db_params()
credential = AddCredential.model_validate(data)
user = await get(user_id=user_id, join_credentials=False)
if not user:
raise ValueError("name", "The provided user does not exist")
async with TinyDB(**db_params) as db:
db.table("credentials").insert(credential.model_dump(mode="json"))
user.credentials.append(credential.id)
db.table("users").update(
{"credentials": user.credentials},
Query().id == str(user_id),
)
return credential.id
@validate_call
async def delete_credential(
user_id: UUID, hex_id: constr(pattern=r"^[0-9a-fA-F]+$", min_length=2)
):
db_params = evaluate_db_params()
user = await get(user_id=user_id, join_credentials=False)
if not user:
raise ValueError("name", "The provided user does not exist")
async with TinyDB(**db_params) as db:
if hex_id in user.credentials:
user.credentials.remove(hex_id)
db.table("credentials").remove(Query().id == hex_id)
db.table("users").update(
{"credentials": user.credentials}, Query().id == str(user_id)
)
return hex_id
@validate_call
async def patch(user_id: UUID, data: dict):
db_params = evaluate_db_params()
user = await get(user_id=user_id, join_credentials=False)
if not user:
raise ValueError("name", "The provided user does not exist")
patch_data = UserPatch.model_validate(data)
patched_user = merge_models(
user,
patch_data,
exclude_strategies=["exclude_override_none"],
)
async with TinyDB(**db_params) as db:
if db.table("users").get(
(Query().login == patched_user.login) & (Query().id != str(user_id))
):
raise ValueError("login", "The provided login name exists")
orphaned_credentials = [
c for c in user.credentials if c not in patched_user.credentials
]
db.table("users").update(
patched_user.model_dump(mode="json"),
Query().id == str(user_id),
)
db.table("credentials").remove(Query().id.one_of(orphaned_credentials))
buster(user.id)
return user.id
@validate_call
async def patch_profile(user_id: UUID, data: dict):
db_params = evaluate_db_params()
user = await get(user_id=user_id, join_credentials=False)
if not user:
raise ValueError("name", "The provided user does not exist")
patch_data = UserProfilePatch.model_validate(data)
patched_user_profile = merge_models(
user.profile, patch_data, exclude_strategies=["exclude_override_none"]
)
async with TinyDB(**db_params) as db:
db.table("users").update(
{"profile": patched_user_profile.model_dump(mode="json")},
Query().id == str(user_id),
)
return user_id
@validate_call
async def patch_credential(
user_id: UUID, hex_id: constr(pattern=r"^[0-9a-fA-F]+$", min_length=2), data: dict
):
db_params = evaluate_db_params()
user = await get(user_id=user_id, join_credentials=True)
if not user:
raise ValueError("name", "The provided user does not exist")
if hex_id not in user.credentials:
raise ValueError(
"hex_id",
"The provided credential ID was not found in user context",
)
patch_data = CredentialPatch.model_validate(data)
patched_credential = merge_models(
user.credentials[hex_id],
patch_data,
exclude_strategies=["exclude_override_none"],
)
async with TinyDB(**db_params) as db:
db.table("credentials").update(
patched_credential.model_dump(mode="json"), Query().id == hex_id
)
return hex_id
@validate_call
async def search(
name: constr(strip_whitespace=True, min_length=0), join_credentials: bool = True
):
db_params = evaluate_db_params()
def search_name(s):
return name in s
async with TinyDB(**db_params) as db:
matches = db.table("users").search(Query().login.test(search_name))
return [
await get(user["id"], join_credentials=join_credentials) for user in matches
]