Skip to content

Commit

Permalink
chore: fix lint errors
Browse files Browse the repository at this point in the history
  • Loading branch information
smotornyuk committed Oct 28, 2024
1 parent 6518a22 commit c576732
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 97 deletions.
3 changes: 2 additions & 1 deletion ckanext/event_audit/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
CONF_ACTIVE_REPO = "ckanext.event_audit.active_repo"


def get_active_repo() -> str:
def active_repo() -> str:
"""The active repository to store the audit logs."""
return tk.config[CONF_ACTIVE_REPO]
10 changes: 7 additions & 3 deletions ckanext/event_audit/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ class IEventAudit(Interface):
def register_repository(self) -> dict[str, type[repos.AbstractRepository]]:
"""Return the repositories provided by this plugin.
Return a dictionary mapping repository names (strings) to
repository classes. For example:
Example:
```
def register_repository(self):
return {RedisRepository.get_name(): RedisRepository}
```
{RedisRepository.get_name(): RedisRepository}
Returns:
mapping of repository names to repository classes
"""
return {}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Create Event table
"""Create Event table.
Revision ID: 9256fa265b84
Revises:
Create Date: 2024-10-23 12:03:33.876737
"""

import sqlalchemy as sa
from alembic import op

Expand All @@ -27,8 +28,8 @@ def upgrade():
sa.Column("target_type", sa.String()),
sa.Column("target_id", sa.String()),
sa.Column("timestamp", sa.TIMESTAMP(timezone=True), nullable=False),
sa.Column("result", sa.JSON(), default={}),
sa.Column("payload", sa.JSON(), default={}),
sa.Column("result", sa.JSON(), server_default="{}"),
sa.Column("payload", sa.JSON(), server_default="{}"),
)

op.create_index("ix_event_category", "event_audit_event", ["category"])
Expand Down
47 changes: 32 additions & 15 deletions ckanext/event_audit/model.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
from __future__ import annotations

from sqlalchemy import TIMESTAMP, Column, Index, String
from datetime import datetime
from typing import Any

from sqlalchemy import TIMESTAMP, Column, Index, String, Table
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.orm import Mapped

import ckan.plugins.toolkit as tk
from ckan import model


class EventModel(tk.BaseModel):
__tablename__ = "event_audit_event"

id = Column(String, primary_key=True)
category = Column(String, nullable=False, index=True)
action = Column(String, nullable=False, index=True)
actor = Column(String, index=True)
action_object = Column(String, index=True)
action_object_id = Column(String, index=True)
target_type = Column(String, index=True)
target_id = Column(String, index=True)
timestamp = Column(TIMESTAMP(timezone=True), nullable=False, index=True)
result = Column(MutableDict.as_mutable(JSONB), default="{}")
payload = Column(MutableDict.as_mutable(JSONB), default="{}")
__table__ = Table(
"event_audit_event",
tk.BaseModel.metadata,
Column("id", String, primary_key=True),
Column("category", String, nullable=False, index=True),
Column("action", String, nullable=False, index=True),
Column("actor", String, index=True),
Column("action_object", String, index=True),
Column("action_object_id", String, index=True),
Column("target_type", String, index=True),
Column("target_id", String, index=True),
Column("timestamp", TIMESTAMP(timezone=True), nullable=False, index=True),
Column("result", MutableDict.as_mutable(JSONB), default="{}"), # type: ignore
Column("payload", MutableDict.as_mutable(JSONB), default="{}"), # type: ignore
Index("ix_event_actor_action", "actor", "action"),
)

__table_args__ = (Index("ix_event_actor_action", "actor", "action"),)
id: Mapped[str]
category: Mapped[str]
action: Mapped[str]
actor: Mapped[str | None]
action_object: Mapped[str | None]
action_object_id: Mapped[str | None]
target_type: Mapped[str | None]
target_id: Mapped[str | None]
timestamp: Mapped[datetime]
result: Mapped[dict[str, Any]]
payload: Mapped[dict[str, Any]]

def save(self) -> None:
model.Session.add(self)
Expand Down
18 changes: 1 addition & 17 deletions ckanext/event_audit/plugin.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
from __future__ import annotations

import os

import yaml

import ckan.plugins.toolkit as tk
from ckan import plugins as p
from ckan.config.declaration import Declaration, Key
from ckan.logic import clear_validators_cache


@tk.blanket.config_declarations
@tk.blanket.validators
class EventAuditPlugin(p.SingletonPlugin):
p.implements(p.IConfigDeclaration)

# IConfigDeclaration

def declare_config_options(self, declaration: Declaration, key: Key):
# this call allows using custom validators in config declarations
clear_validators_cache()

here = os.path.dirname(__file__)
with open(os.path.join(here, "config_declaration.yaml"), "rb") as src:
declaration.load_dict(yaml.safe_load(src))
pass
3 changes: 2 additions & 1 deletion ckanext/event_audit/repositories/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Any

from ckanext.event_audit import types

Expand All @@ -25,7 +26,7 @@ def build_event(self, event_data: types.EventData) -> types.Event:
return types.Event(**event_data)

@abstractmethod
def get_event(self, event_id: str) -> types.Event | None:
def get_event(self, event_id: Any) -> types.Event | None:
"""Get a single event by its ID."""

@abstractmethod
Expand Down
5 changes: 3 additions & 2 deletions ckanext/event_audit/repositories/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from datetime import datetime as dt
from datetime import timezone as tz
from typing import Any

from ckan.lib.redis import connect_to_redis

Expand Down Expand Up @@ -51,7 +52,7 @@ def get_event(self, event_id: float) -> types.Event | None:
for event_data in result.values():
return types.Event.model_validate_json(event_data)

def filter_events(self, filters: types.Filters) -> list[types.Event]:
def filter_events(self, filters: types.Filters | Any) -> list[types.Event]:
"""Filters events based on patterns generated from the provided filters."""
if not isinstance(filters, types.Filters):
raise TypeError(
Expand All @@ -67,7 +68,7 @@ def filter_events(self, filters: types.Filters) -> list[types.Event]:
break

cursor, result = self.conn.hscan(
REDIS_SET_KEY, cursor=cursor, match=pattern # type: ignore
REDIS_SET_KEY, cursor=cursor, match=pattern, # type: ignore
)

matching_events.extend(
Expand Down
2 changes: 1 addition & 1 deletion ckanext/event_audit/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
@pytest.mark.usefixtures("with_plugins")
class TestEventAuditConfig:
def test_get_active_repo_default(self):
assert config.get_active_repo() == "redis"
assert config.active_repo() == "redis"
10 changes: 5 additions & 5 deletions ckanext/event_audit/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import uuid
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any, Dict, Literal, Optional, TypedDict, Union
from typing import Any, Dict, Optional, TypedDict, Union

from pydantic import BaseModel, ConfigDict, Field, validator

Expand Down Expand Up @@ -83,10 +83,10 @@ def validate_actor(cls, v: str) -> str:
@validator("timestamp")
@classmethod
def validate_timestamp(cls, v: Union[str, datetime]) -> str:
if v and isinstance(v, datetime):
if isinstance(v, datetime):
return v.isoformat()

if not v or not isinstance(v, str):
if not v:
raise ValueError("The `timestamp` field must be a non-empty string.")

try:
Expand All @@ -100,13 +100,13 @@ def validate_timestamp(cls, v: Union[str, datetime]) -> str:
class ModelEvent(Event):
"""TODO: do we need it?"""

category: Literal["model"] = "model"
category: str = "model"


class ApiEvent(Event):
"""TODO: do we need it?"""

category: Literal["api"] = "api"
category: str = "api"


class Filters(BaseModel):
Expand Down
13 changes: 6 additions & 7 deletions ckanext/event_audit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@

import ckan.plugins as p

import ckanext.event_audit.config as audit_config
import ckanext.event_audit.repositories as repos
from ckanext.event_audit import config
from ckanext.event_audit import repositories as repos
from ckanext.event_audit.interfaces import IEventAudit


def get_available_repos() -> dict[str, type[repos.AbstractRepository]]:
"""Get the available repositories.
Returns:
dict[str, type[repos.AbstractRepository]]: The available repositories
available repositories
"""
plugin_repos: dict[str, type[repos.AbstractRepository]] = {
repos.RedisRepository.get_name(): repos.RedisRepository,
}

for plugin in reversed(list(p.PluginImplementations(IEventAudit))):
for name, repo in plugin.register_repository().items():
plugin_repos[name] = repo
plugin_repos.update(plugin.register_repository())

return plugin_repos

Expand All @@ -28,9 +27,9 @@ def get_active_repo() -> type[repos.AbstractRepository]:
"""Get the active repository.
Returns:
Type[repos.AbstractRepository]: The active repository
the active repository
"""
repos = get_available_repos()
active_repo_name = audit_config.get_active_repo()
active_repo_name = config.active_repo()

return repos[active_repo_name]
47 changes: 47 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
[build-system]
requires = [ "setuptools>=61.2"]
build-backend = "setuptools.build_meta"

[project]
name = "ckanext-event-audit"
version = "0.1.0"
description = ""
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
]
keywords = [ "CKAN" ]
dependencies = [
"pydantic>=2.3.0,<2.4.0",
]
authors = [
{name = "DataShades", email = "[email protected]"},
{name = "LD"},
]

[project.readme]
file = "README.md"
content-type = "text/markdown"

[project.license]
text = "AGPL"

[project.urls]
Homepage = "https://github.com/DataShades/ckanext-event-audit"
Documentation = "https://datashades.github.io/ckanext-event-audit/"

[project.optional-dependencies]

[project.entry-points."ckan.plugins"]
event_audit = "ckanext.event_audit.plugin:EventAuditPlugin"
test_event_audit = "ckanext.event_audit.tests.test_interface:TestEventAuditPlugin"

[project.entry-points."babel.extractors"]
ckan = "ckan.lib.extract:extract_ckan"

[tool.setuptools.packages]
find = {}

[tool.ruff]
target-version = "py38"
exclude = [
Expand Down
42 changes: 0 additions & 42 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
[metadata]
name = ckanext-event-audit
version = 0.1.0
description =
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/DataShades/ckanext-event-audit
author = LD
author_email =
license = AGPL
classifiers =
Development Status :: 4 - Beta
License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
keywords = CKAN

[options]
packages = find:
namespace_packages = ckanext
install_requires =
pydantic>=2.3.0,<2.4.0
include_package_data = True

[options.entry_points]
ckan.plugins =
event_audit = ckanext.event_audit.plugin:EventAuditPlugin
test_event_audit = ckanext.event_audit.tests.test_interface:TestEventAuditPlugin

babel.extractors =
ckan = ckan.lib.extract:extract_ckan

[options.extras_require]

[extract_messages]
keywords = translate isPlural
add_comments = TRANSLATORS:
Expand All @@ -54,10 +19,3 @@ previous = true
domain = ckanext-event_audit
directory = ckanext/event_audit/i18n
statistics = true

[tool:pytest]
filterwarnings =
ignore::sqlalchemy.exc.SADeprecationWarning
ignore::sqlalchemy.exc.SAWarning
ignore::DeprecationWarning
addopts = --ckan-ini test.ini

0 comments on commit c576732

Please sign in to comment.