flask-postgresql-audit 1.0.6__tar.gz → 1.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/PKG-INFO +1 -1
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/__init__.py +1 -1
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/entities.py +9 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/setup.py +1 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/core.py +60 -31
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/get_pk_values.sql +1 -1
- flask_postgresql_audit-1.0.8/flask_postgresql_audit/templates/get_table_relid.sql +4 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/app.py +1 -4
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/.gitignore +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/CHANGES.md +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/LICENSE +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/README.md +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/__init__.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/schema.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/base.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/descriptors.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/__init__.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/alembic_utils/__init__.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/alembic_utils/pg_aggregate.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/__init__.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/enum.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/listener.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/model.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/models.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/audit_trigger_delete.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/audit_trigger_insert.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/audit_trigger_update.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/create_activity.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/create_schema.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/drop_schema.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/get_setting.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/jsonb_subtract.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/row_audit_trigger.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/row_create_activity.sql +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/typing.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/utils.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/pyproject.toml +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/__init__.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/conftest.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/migrations/README +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/migrations/alembic.ini +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/migrations/env.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/migrations/script.py.mako +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/test_defaults.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/test_operators.py +0 -0
- {flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flask-postgresql_audit
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: Versioning and auditing extension for PostgreSQL and SQLAlchemy.
|
|
5
5
|
Project-URL: Homepage, https://github.com/higamigu/flask-postgresql-audit
|
|
6
6
|
Author-email: Higamigu <higamigu@gmail.com>, Konsta Vesterinen <konsta@fastmonkeys.com>
|
|
@@ -55,6 +55,15 @@ def get_pk_values(**context):
|
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def get_table_relid(**context):
|
|
59
|
+
sql = parse_template("get_table_relid.sql", **context)
|
|
60
|
+
return PGFunction(
|
|
61
|
+
schema=context["schema_name"],
|
|
62
|
+
signature="get_table_relid(tablename text)",
|
|
63
|
+
definition=sql["definition"],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
58
67
|
def get_setting_factory(**context):
|
|
59
68
|
sql = parse_template("get_setting.sql", **context)
|
|
60
69
|
return PGFunction(
|
|
@@ -63,6 +63,7 @@ def setup_db(audit: "PostgreSQLAudit"):
|
|
|
63
63
|
def register_core_entities(audit: "PostgreSQLAudit"):
|
|
64
64
|
audit.pg_audit_entities.add(entities.btree_gist)
|
|
65
65
|
audit.pg_audit_entities.add(entities.get_pk_values(**audit.context))
|
|
66
|
+
audit.pg_audit_entities.add(entities.get_table_relid(**audit.context))
|
|
66
67
|
audit.pg_audit_entities.add(entities.get_setting_factory(**audit.context))
|
|
67
68
|
audit.pg_audit_entities.add(entities.jsonb_subtract_factory(**audit.context))
|
|
68
69
|
audit.pg_audit_entities.add(entities.create_activity_factory(**audit.context))
|
{flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/core.py
RENAMED
|
@@ -27,6 +27,12 @@ class ImproperlyConfigured(Exception):
|
|
|
27
27
|
|
|
28
28
|
class Audit:
|
|
29
29
|
__audit_args__: dict = {}
|
|
30
|
+
__audit_schema__: str = ""
|
|
31
|
+
|
|
32
|
+
__audit_relid__: orm.Mapped[int]
|
|
33
|
+
__audit_rowkey__: orm.Mapped[list[str]]
|
|
34
|
+
|
|
35
|
+
__audit_configured__: t.ClassVar[bool]
|
|
30
36
|
|
|
31
37
|
if t.TYPE_CHECKING:
|
|
32
38
|
__table__: sa.Table
|
|
@@ -34,10 +40,33 @@ class Audit:
|
|
|
34
40
|
|
|
35
41
|
def __init__(self, **kw: t.Any): ...
|
|
36
42
|
|
|
37
|
-
def __init_subclass__(cls) -> None:
|
|
43
|
+
def __init_subclass__(cls, *args, **kwargs) -> None:
|
|
38
44
|
if not hasattr(cls, "__audit_args__"):
|
|
39
45
|
cls.__audit_args__ = {}
|
|
40
46
|
|
|
47
|
+
event.listen(orm.Mapper, "mapper_configured", cls.__init_audit__)
|
|
48
|
+
super().__init_subclass__(*args, **kwargs)
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def __init_audit__(cls, mapper: orm.Mapper[t.Self], class_: type[t.Self]):
|
|
52
|
+
is_configured = getattr(class_, "__audit_configured__", False)
|
|
53
|
+
has_schema = hasattr(class_, "__audit_schema__")
|
|
54
|
+
if not is_configured and has_schema:
|
|
55
|
+
fn_ = sa.func
|
|
56
|
+
if class_.__audit_schema__ and class_.__audit_schema__ != "public":
|
|
57
|
+
fn_ = getattr(sa.func, class_.__audit_schema__)
|
|
58
|
+
|
|
59
|
+
table = class_.__table__
|
|
60
|
+
jsonb = sa.func.to_jsonb(table.table_valued())
|
|
61
|
+
|
|
62
|
+
relid = orm.column_property(fn_.get_table_relid(class_.__tablename__))
|
|
63
|
+
rowkey = orm.column_property(fn_.get_pk_values(relid, jsonb))
|
|
64
|
+
|
|
65
|
+
mapper.add_property("__audit_relid__", relid)
|
|
66
|
+
mapper.add_property("__audit_rowkey__", rowkey)
|
|
67
|
+
|
|
68
|
+
class_.__audit_configured__ = True
|
|
69
|
+
|
|
41
70
|
|
|
42
71
|
def get_modified_columns(obj: orm.DeclarativeBase):
|
|
43
72
|
modified: set[sa.ColumnElement[t.Any]] = set()
|
|
@@ -65,6 +94,14 @@ def get_audit_models(registry: orm.registry):
|
|
|
65
94
|
return models
|
|
66
95
|
|
|
67
96
|
|
|
97
|
+
def inject_model_metadata(models: t.Iterable[type[Audit]], **kwargs):
|
|
98
|
+
kwargs.setdefault("schema", "public")
|
|
99
|
+
|
|
100
|
+
for m in models:
|
|
101
|
+
for k, v in kwargs.items():
|
|
102
|
+
setattr(m, f"__audit_{k}__", v)
|
|
103
|
+
|
|
104
|
+
|
|
68
105
|
def is_object_modified(obj: object):
|
|
69
106
|
if isinstance(obj, Audit):
|
|
70
107
|
excluded = set(obj.__class__.__audit_args__.get("exclude", []))
|
|
@@ -242,6 +279,7 @@ class PostgreSQLAudit:
|
|
|
242
279
|
)
|
|
243
280
|
|
|
244
281
|
self.pg_audit_classes = get_audit_models(self.Base.registry)
|
|
282
|
+
inject_model_metadata(self.pg_audit_classes, schema=self.schema_name)
|
|
245
283
|
|
|
246
284
|
self.setup_db()
|
|
247
285
|
self.attach_listeners()
|
|
@@ -249,34 +287,6 @@ class PostgreSQLAudit:
|
|
|
249
287
|
app.extensions["postgresql-audit"] = self
|
|
250
288
|
|
|
251
289
|
def fetch_activity(self, obj: type[Audit] | Audit | t.Sequence[Audit]):
|
|
252
|
-
if inspect.isclass(obj):
|
|
253
|
-
source = obj
|
|
254
|
-
activity_join_cond = self.Activity.table_name == source.__tablename__
|
|
255
|
-
else:
|
|
256
|
-
objects = list(obj) if isinstance(obj, t.Sequence) else list([obj])
|
|
257
|
-
unions = []
|
|
258
|
-
for key, group in groupby(objects, key=lambda o: o.__class__):
|
|
259
|
-
relid = sa.text(f"'{key.__tablename__}'::regclass::oid")
|
|
260
|
-
jsonb = sa.func.to_jsonb(key.__table__.table_valued())
|
|
261
|
-
|
|
262
|
-
union = sa.select(
|
|
263
|
-
sa.literal(key.__tablename__).label("table_name"),
|
|
264
|
-
self.func.get_pk_values(relid, jsonb).label("row_key"),
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
for pk in key.__table__.primary_key.columns:
|
|
268
|
-
pk_attr: sa.ColumnElement[t.Any] = getattr(key, pk.name)
|
|
269
|
-
pks = [getattr(o, pk.name) for o in group]
|
|
270
|
-
union = union.filter(pk_attr.in_(pks))
|
|
271
|
-
|
|
272
|
-
unions.append(union)
|
|
273
|
-
|
|
274
|
-
source = sa.union(*unions).subquery("source")
|
|
275
|
-
activity_join_cond = sa.and_(
|
|
276
|
-
self.Activity.table_name == source.c.table_name,
|
|
277
|
-
self.Activity.row_key == source.c.row_key,
|
|
278
|
-
)
|
|
279
|
-
|
|
280
290
|
stmt = (
|
|
281
291
|
sa.select(
|
|
282
292
|
self.Activity.id,
|
|
@@ -289,10 +299,29 @@ class PostgreSQLAudit:
|
|
|
289
299
|
self.Activity.old_data,
|
|
290
300
|
self.Activity.changed_data,
|
|
291
301
|
)
|
|
292
|
-
.select_from(
|
|
293
|
-
.join(self.Activity, activity_join_cond)
|
|
302
|
+
.select_from(self.Activity)
|
|
294
303
|
.join(self.Transaction)
|
|
295
304
|
.order_by(self.Activity.id.desc())
|
|
296
305
|
)
|
|
297
306
|
|
|
307
|
+
if inspect.isclass(obj):
|
|
308
|
+
class_ = obj
|
|
309
|
+
stmt = stmt.filter(self.Activity.table_name == class_.__tablename__)
|
|
310
|
+
|
|
311
|
+
else:
|
|
312
|
+
objects = list(obj) if isinstance(obj, t.Sequence) else list([obj])
|
|
313
|
+
conditions = []
|
|
314
|
+
for class_, objs in groupby(objects, key=lambda o: o.__class__):
|
|
315
|
+
conditions.append(
|
|
316
|
+
sa.and_(
|
|
317
|
+
self.Activity.table_name == class_.__tablename__,
|
|
318
|
+
self.Activity.row_key.in_([o.__audit_rowkey__ for o in objs]),
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
if len(conditions) == 1:
|
|
323
|
+
stmt = stmt.filter(conditions[0])
|
|
324
|
+
else:
|
|
325
|
+
stmt = stmt.filter(sa.or_(*conditions))
|
|
326
|
+
|
|
298
327
|
return stmt
|
|
@@ -14,11 +14,8 @@ class DefaultConfig:
|
|
|
14
14
|
DB_PASSWORD = os.environ.get("POSTGRESQL_AUDIT_TEST_PASSWORD", "")
|
|
15
15
|
DB_NAME = os.environ.get("POSTGRESQL_AUDIT_TEST_DB", "postgresql_audit_test")
|
|
16
16
|
|
|
17
|
-
# SQLALCHEMY_DATABASE_URI = (
|
|
18
|
-
# f"postgresql://{DB_USER}:{DB_PASSWORD}@localhost/{DB_NAME}"
|
|
19
|
-
# )
|
|
20
17
|
SQLALCHEMY_DATABASE_URI = (
|
|
21
|
-
"postgresql://
|
|
18
|
+
f"postgresql://{DB_USER}:{DB_PASSWORD}@localhost/{DB_NAME}"
|
|
22
19
|
)
|
|
23
20
|
SQLALCHEMY_ECHO = False
|
|
24
21
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/base.py
RENAMED
|
File without changes
|
{flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/descriptors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/typing.py
RENAMED
|
File without changes
|
{flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flask_postgresql_audit-1.0.6 → flask_postgresql_audit-1.0.8}/tests/migrations/script.py.mako
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|