mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-05-05 12:43:49 +00:00
refactor: allow event to receive keyword arguments
This change also replaces the dataclass implementation of the class to custom one
This commit is contained in:
parent
a9003993fa
commit
48ee19e825
@ -48,13 +48,8 @@ class EventOperations(Operations):
|
||||
|
||||
def run(connection: Connection) -> list[Event]:
|
||||
return [
|
||||
Event(
|
||||
event=row["event"],
|
||||
object_id=row["object_id"],
|
||||
message=row["message"],
|
||||
data=row["data"],
|
||||
created=row["created"],
|
||||
) for row in connection.execute(
|
||||
Event.from_json(row)
|
||||
for row in connection.execute(
|
||||
"""
|
||||
select created, event, object_id, message, data from auditlog
|
||||
where (:event is null or event = :event)
|
||||
|
@ -17,11 +17,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from dataclasses import dataclass, field, fields
|
||||
from enum import StrEnum
|
||||
from typing import Any, Self
|
||||
|
||||
from ahriman.core.utils import dataclass_view, filter_json, utcnow
|
||||
from ahriman.core.utils import utcnow
|
||||
|
||||
|
||||
class EventType(StrEnum):
|
||||
@ -41,7 +40,6 @@ class EventType(StrEnum):
|
||||
PackageUpdated = "package-updated"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Event:
|
||||
"""
|
||||
audit log event
|
||||
@ -54,18 +52,24 @@ class Event:
|
||||
object_id(str): object identifier
|
||||
"""
|
||||
|
||||
event: str | EventType
|
||||
object_id: str
|
||||
message: str | None = None
|
||||
data: dict[str, Any] = field(default_factory=dict)
|
||||
created: int = field(default_factory=lambda: int(utcnow().timestamp()))
|
||||
def __init__(self, event: str | EventType, object_id: str, message: str | None = None, created: int | None = None,
|
||||
**kwargs: Any):
|
||||
"""
|
||||
default constructor
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
Args:
|
||||
event(str | EventType): event type
|
||||
object_id(str): object identifier
|
||||
message(str | None): event message if available
|
||||
created(int | None, optional): event timestamp (Default value = None)
|
||||
**kwargs(Any): event metadata
|
||||
"""
|
||||
convert event type to enum if it is a well-known event type
|
||||
"""
|
||||
if self.event in EventType:
|
||||
object.__setattr__(self, "event", EventType(self.event))
|
||||
self.event = EventType(event) if event in EventType else event
|
||||
self.object_id = object_id
|
||||
self.created = created or int(utcnow().timestamp())
|
||||
|
||||
self.message = message
|
||||
self.data = kwargs
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||
@ -78,9 +82,13 @@ class Event:
|
||||
Returns:
|
||||
Self: dependencies object
|
||||
"""
|
||||
# filter to only known fields
|
||||
known_fields = [pair.name for pair in fields(cls)]
|
||||
return cls(**filter_json(dump, known_fields))
|
||||
return cls(
|
||||
event=dump["event"],
|
||||
object_id=dump["object_id"],
|
||||
message=dump.get("message"),
|
||||
created=dump.get("created"),
|
||||
**dump.get("data", {}),
|
||||
)
|
||||
|
||||
def view(self) -> dict[str, Any]:
|
||||
"""
|
||||
@ -89,4 +97,32 @@ class Event:
|
||||
Returns:
|
||||
dict[str, Any]: json-friendly dictionary
|
||||
"""
|
||||
return dataclass_view(self)
|
||||
dump = {
|
||||
"event": self.event,
|
||||
"object_id": self.object_id,
|
||||
"created": self.created,
|
||||
}
|
||||
if self.message is not None:
|
||||
dump["message"] = self.message
|
||||
if self.data:
|
||||
dump["data"] = self.data
|
||||
|
||||
return dump
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
"""
|
||||
check if other is the same object
|
||||
|
||||
Args:
|
||||
other(Any): other object instance
|
||||
|
||||
Returns:
|
||||
bool: ``True`` if the other object is the same and ``False`` otherwise
|
||||
"""
|
||||
if not isinstance(other, Event):
|
||||
return False
|
||||
return self.event == other.event \
|
||||
and self.object_id == other.object_id \
|
||||
and self.message == other.message \
|
||||
and self.created == other.created \
|
||||
and self.data == other.data
|
||||
|
@ -8,7 +8,7 @@ def test_event_insert_get(database: SQLite, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must insert and get event
|
||||
"""
|
||||
event = Event(EventType.PackageUpdated, package_ahriman.base, "Updated", {"key": "value"})
|
||||
event = Event(EventType.PackageUpdated, package_ahriman.base, "Updated", key="value")
|
||||
database.event_insert(event)
|
||||
assert database.event_get() == [event]
|
||||
|
||||
|
@ -1,17 +1,49 @@
|
||||
from ahriman.models.event import Event, EventType
|
||||
|
||||
|
||||
def test_post_init() -> None:
|
||||
def test_init() -> None:
|
||||
"""
|
||||
must replace event type for known types
|
||||
"""
|
||||
assert Event("random", "")
|
||||
assert isinstance(Event(str(EventType.PackageUpdated), "").event, EventType)
|
||||
|
||||
assert Event("", "", key="value").data == {"key": "value"}
|
||||
|
||||
assert Event("", "").created > 0
|
||||
|
||||
|
||||
def test_from_json_view() -> None:
|
||||
"""
|
||||
must construct and serialize event to json
|
||||
"""
|
||||
event = Event("event", "object", "message", {"key": "value"})
|
||||
event = Event("event", "object", "message", key="value")
|
||||
assert Event.from_json(event.view()) == event
|
||||
|
||||
|
||||
def test_view_empty() -> None:
|
||||
"""
|
||||
must skip empty fields during (de-)serialization
|
||||
"""
|
||||
event = Event("event", "object")
|
||||
assert Event.from_json(event.view()) == event
|
||||
assert "message" not in event.view()
|
||||
assert "data" not in event.view()
|
||||
|
||||
|
||||
def test_eq() -> None:
|
||||
"""
|
||||
must compare two events
|
||||
"""
|
||||
event1 = Event("1", "1", "1", 1, key="value")
|
||||
assert event1 == event1
|
||||
|
||||
event2 = Event("2", "2", "2", 2, key="value")
|
||||
assert event1 != event2
|
||||
|
||||
|
||||
def test_eq_other() -> None:
|
||||
"""
|
||||
must return False in case if object is not an instance of event
|
||||
"""
|
||||
assert Event("1", "1") != 42
|
||||
|
@ -27,7 +27,7 @@ async def test_get(client: TestClient) -> None:
|
||||
"""
|
||||
must return all events
|
||||
"""
|
||||
event1 = Event("event1", "object1", "message", {"key": "value"})
|
||||
event1 = Event("event1", "object1", "message", key="value")
|
||||
event2 = Event("event2", "object2")
|
||||
await client.post("/api/v1/events", json=event1.view())
|
||||
await client.post("/api/v1/events", json=event2.view())
|
||||
@ -46,7 +46,7 @@ async def test_get_with_pagination(client: TestClient) -> None:
|
||||
"""
|
||||
must get events with pagination
|
||||
"""
|
||||
event1 = Event("event1", "object1", "message", {"key": "value"})
|
||||
event1 = Event("event1", "object1", "message", key="value")
|
||||
event2 = Event("event2", "object2")
|
||||
await client.post("/api/v1/events", json=event1.view())
|
||||
await client.post("/api/v1/events", json=event2.view())
|
||||
@ -83,7 +83,7 @@ async def test_post(client: TestClient) -> None:
|
||||
"""
|
||||
must create event
|
||||
"""
|
||||
event = Event("event1", "object1", "message", {"key": "value"})
|
||||
event = Event("event1", "object1", "message", key="value")
|
||||
request_schema = pytest.helpers.schema_request(EventsView.post)
|
||||
|
||||
payload = event.view()
|
||||
|
Loading…
x
Reference in New Issue
Block a user