147 lines
5.2 KiB
Python
147 lines
5.2 KiB
Python
import logging
|
|
from abc import abstractmethod
|
|
from datetime import datetime
|
|
from typing import Union, List, Optional, Any, Dict
|
|
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.brm.brn import BRN
|
|
from app.extensions import db
|
|
|
|
|
|
class AbstractConfiguration(db.Model): # type: ignore
|
|
__abstract__ = True
|
|
|
|
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
|
description: Mapped[str]
|
|
added: Mapped[datetime]
|
|
updated: Mapped[datetime]
|
|
destroyed: Mapped[Optional[datetime]]
|
|
|
|
@property
|
|
@abstractmethod
|
|
def brn(self) -> BRN:
|
|
raise NotImplementedError()
|
|
|
|
def destroy(self) -> None:
|
|
self.destroyed = datetime.utcnow()
|
|
self.updated = datetime.utcnow()
|
|
|
|
@classmethod
|
|
def csv_header(cls) -> List[str]:
|
|
return [
|
|
"id", "description", "added", "updated", "destroyed"
|
|
]
|
|
|
|
def csv_row(self) -> List[Any]:
|
|
return [
|
|
getattr(self, x) for x in self.csv_header()
|
|
]
|
|
|
|
|
|
class Deprecation(db.Model): # type: ignore[name-defined,misc]
|
|
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
|
resource_type: Mapped[str] = mapped_column(db.String(50))
|
|
resource_id: Mapped[int] = mapped_column(db.Integer)
|
|
deprecated_at: Mapped[datetime] = mapped_column(db.DateTime(), default=datetime.utcnow, nullable=False)
|
|
meta: Mapped[Optional[Dict[str, Any]]] = mapped_column(db.JSON())
|
|
reason: Mapped[str] = mapped_column(db.String(), nullable=False)
|
|
|
|
@property
|
|
def resource(self) -> "AbstractResource":
|
|
from app.models.mirrors import Proxy
|
|
model = {'Proxy': Proxy}[self.resource_type]
|
|
return model.query.get(self.resource_id) # type: ignore[no-any-return]
|
|
|
|
|
|
class AbstractResource(db.Model): # type: ignore
|
|
__abstract__ = True
|
|
|
|
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
|
added: Mapped[datetime] = mapped_column(db.DateTime(), default=datetime.utcnow, nullable=False)
|
|
updated: Mapped[datetime] = mapped_column(db.DateTime(), default=datetime.utcnow, nullable=False)
|
|
deprecated: Mapped[Optional[datetime]] = mapped_column(db.DateTime())
|
|
deprecation_reason: Mapped[Optional[str]] = mapped_column(db.String())
|
|
destroyed: Mapped[Optional[datetime]] = mapped_column(db.DateTime())
|
|
|
|
def __init__(self, *,
|
|
id: Optional[int] = None,
|
|
added: Optional[datetime] = None,
|
|
updated: Optional[datetime] = None,
|
|
deprecated: Optional[datetime] = None,
|
|
deprecation_reason: Optional[str] = None,
|
|
destroyed: Optional[datetime] = None,
|
|
**kwargs: Any) -> None:
|
|
if added is None:
|
|
added = datetime.utcnow()
|
|
if updated is None:
|
|
updated = datetime.utcnow()
|
|
super().__init__(id=id,
|
|
added=added,
|
|
updated=updated,
|
|
deprecated=deprecated,
|
|
deprecation_reason=deprecation_reason,
|
|
destroyed=destroyed,
|
|
**kwargs)
|
|
|
|
@property
|
|
@abstractmethod
|
|
def brn(self) -> BRN:
|
|
raise NotImplementedError()
|
|
|
|
def deprecate(self, *, reason: str, meta: Optional[Dict[str, Any]] = None) -> bool:
|
|
"""
|
|
Marks the resource as deprecated. In the event that the resource was already
|
|
deprecated, no change will be recorded and the function will return False.
|
|
|
|
:param reason: an opaque string that records the deprecation reason
|
|
:param meta: metadata associated with the deprecation reason, such as the circumstances in which censorship was
|
|
detected
|
|
:return: if the proxy was deprecated
|
|
"""
|
|
if self.deprecated is None:
|
|
logging.info("Deprecating %s (reason=%s)", self.brn, reason)
|
|
self.deprecated = datetime.utcnow()
|
|
self.deprecation_reason = reason
|
|
self.updated = datetime.utcnow()
|
|
if reason not in [d.reason for d in self.deprecations]:
|
|
new_deprecation = Deprecation(
|
|
resource_type=type(self).__name__,
|
|
resource_id=self.id,
|
|
reason=reason,
|
|
meta=meta
|
|
)
|
|
db.session.add(new_deprecation)
|
|
return True
|
|
logging.info("Not deprecating %s (reason=%s) because it's already deprecated with that reason.",
|
|
self.brn, reason)
|
|
return False
|
|
|
|
@property
|
|
def deprecations(self) -> List[Deprecation]:
|
|
return Deprecation.query.filter_by( # type: ignore[no-any-return]
|
|
resource_type='Proxy',
|
|
resource_id=self.id
|
|
).all()
|
|
|
|
def destroy(self) -> None:
|
|
"""
|
|
Marks the resource for destruction.
|
|
|
|
:return: None
|
|
"""
|
|
if self.deprecated is None:
|
|
self.deprecate(reason="destroyed")
|
|
self.destroyed = datetime.utcnow()
|
|
self.updated = datetime.utcnow()
|
|
|
|
@classmethod
|
|
def csv_header(cls) -> List[str]:
|
|
return [
|
|
"id", "added", "updated", "deprecated", "deprecation_reason", "destroyed"
|
|
]
|
|
|
|
def csv_row(self) -> List[Union[datetime, bool, int, str]]:
|
|
return [
|
|
getattr(self, x) for x in self.csv_header()
|
|
]
|