Coverage for app / repositories / link_repository.py: 100%
34 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-21 22:41 +0300
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-21 22:41 +0300
1from datetime import datetime
3from sqlalchemy import select, update
4from sqlalchemy.ext.asyncio import AsyncSession
6from app.models import Link
9class LinkRepository:
10 @staticmethod
11 async def get_by_short_code(db: AsyncSession, short_code: str) -> Link | None:
12 """
13 Ищет ссылку по короткому коду.
14 """
15 query = select(Link).where(Link.short_code == short_code)
16 result = await db.execute(query)
17 return result.scalar_one_or_none()
19 @staticmethod
20 async def get_active_by_short_code(
21 db: AsyncSession, short_code: str, now: datetime
22 ) -> Link | None:
23 """
24 По короткому коду ищет ссылку, время жизни которой ещё не истекло.
25 """
26 query = select(Link).where(
27 (Link.short_code == short_code) & (Link.expires_at > now)
28 )
29 result = await db.execute(query)
30 return result.scalar_one_or_none()
32 @staticmethod
33 async def get_active_by_long_url(
34 db: AsyncSession, long_url: str, now: datetime, user_id: int | None = None
35 ) -> list[Link] | None:
36 """
37 По оригинальному URL ищет активные ссылки, принадлежащие пользователю
38 с указанным идентификатором.
39 """
40 query = select(Link).where(
41 (
42 (Link.long_url == long_url)
43 & (Link.expires_at > now)
44 & (Link.user_id == user_id)
45 )
46 )
47 result = await db.execute(query)
48 return result.scalars().all()
50 @staticmethod
51 async def update_short_code(
52 db: AsyncSession, current_short_code: str, new_short_code: str
53 ) -> None:
54 """
55 Обновляет уникальный короткий код ссылки.
56 """
57 query = (
58 update(Link)
59 .where(Link.short_code == current_short_code)
60 .values(short_code=new_short_code)
61 )
62 await db.execute(query)
64 @staticmethod
65 async def update_on_redirect(
66 db: AsyncSession,
67 short_code: str,
68 last_visited_at: datetime,
69 expires_at: datetime | None = None,
70 ) -> None:
71 """
72 Увеличивает количество переходов по ссылке на единицу, обновляет
73 дату последнего перехода по ссылке и опционально устанавливает
74 новую дату истечения действия ссылки.
75 """
76 values = {
77 "visits_counter": Link.visits_counter + 1,
78 "last_visited_at": last_visited_at,
79 }
80 if expires_at:
81 values["expires_at"] = expires_at
83 query = update(Link).where(Link.short_code == short_code).values(**values)
84 await db.execute(query)
86 @staticmethod
87 async def delete(db: AsyncSession, link: Link) -> None:
88 """
89 Отмечает объект ссылки `Link` как удалённый.
90 """
91 await db.delete(link)