flask-postgresql-audit 1.0.7__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.7 → flask_postgresql_audit-1.0.8}/PKG-INFO +1 -1
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/__init__.py +1 -1
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/entities.py +9 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/setup.py +1 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/core.py +60 -37
- {flask_postgresql_audit-1.0.7 → 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.7 → flask_postgresql_audit-1.0.8}/.gitignore +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/CHANGES.md +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/LICENSE +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/README.md +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/__init__.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/alembic/schema.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/base.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/descriptors.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/__init__.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/alembic_utils/__init__.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/alembic_utils/pg_aggregate.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/__init__.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/enum.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/listener.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/extensions/document_staging/model.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/models.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/audit_trigger_delete.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/audit_trigger_insert.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/audit_trigger_update.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/create_activity.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/create_schema.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/drop_schema.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/get_setting.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/jsonb_subtract.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/row_audit_trigger.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/templates/row_create_activity.sql +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/typing.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/utils.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/pyproject.toml +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/__init__.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/app.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/conftest.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/migrations/README +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/migrations/alembic.ini +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/migrations/env.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/migrations/script.py.mako +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/test_defaults.py +0 -0
- {flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/test_operators.py +0 -0
- {flask_postgresql_audit-1.0.7 → 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.7 → 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,40 +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
|
-
relid = sa.text(f"'{source.__tablename__}'::regclass::oid")
|
|
255
|
-
jsonb = sa.func.to_jsonb(source.__table__.table_valued())
|
|
256
|
-
activity_join_cond = sa.and_(
|
|
257
|
-
self.Activity.table_name == source.__tablename__,
|
|
258
|
-
self.Activity.row_key == self.func.get_pk_values(relid, jsonb),
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
else:
|
|
262
|
-
objects = list(obj) if isinstance(obj, t.Sequence) else list([obj])
|
|
263
|
-
unions = []
|
|
264
|
-
for key, group in groupby(objects, key=lambda o: o.__class__):
|
|
265
|
-
relid = sa.text(f"'{key.__tablename__}'::regclass::oid")
|
|
266
|
-
jsonb = sa.func.to_jsonb(key.__table__.table_valued())
|
|
267
|
-
|
|
268
|
-
union = sa.select(
|
|
269
|
-
sa.literal(key.__tablename__).label("table_name"),
|
|
270
|
-
self.func.get_pk_values(relid, jsonb).label("row_key"),
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
for pk in key.__table__.primary_key.columns:
|
|
274
|
-
pk_attr: sa.ColumnElement[t.Any] = getattr(key, pk.name)
|
|
275
|
-
pks = [getattr(o, pk.name) for o in group]
|
|
276
|
-
union = union.filter(pk_attr.in_(pks))
|
|
277
|
-
|
|
278
|
-
unions.append(union)
|
|
279
|
-
|
|
280
|
-
source = sa.union(*unions).subquery("source")
|
|
281
|
-
activity_join_cond = sa.and_(
|
|
282
|
-
self.Activity.table_name == source.c.table_name,
|
|
283
|
-
self.Activity.row_key == source.c.row_key,
|
|
284
|
-
)
|
|
285
|
-
|
|
286
290
|
stmt = (
|
|
287
291
|
sa.select(
|
|
288
292
|
self.Activity.id,
|
|
@@ -295,10 +299,29 @@ class PostgreSQLAudit:
|
|
|
295
299
|
self.Activity.old_data,
|
|
296
300
|
self.Activity.changed_data,
|
|
297
301
|
)
|
|
298
|
-
.select_from(
|
|
299
|
-
.join(self.Activity, activity_join_cond)
|
|
302
|
+
.select_from(self.Activity)
|
|
300
303
|
.join(self.Transaction)
|
|
301
304
|
.order_by(self.Activity.id.desc())
|
|
302
305
|
)
|
|
303
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
|
+
|
|
304
327
|
return stmt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/base.py
RENAMED
|
File without changes
|
{flask_postgresql_audit-1.0.7 → 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.7 → 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.7 → flask_postgresql_audit-1.0.8}/flask_postgresql_audit/typing.py
RENAMED
|
File without changes
|
{flask_postgresql_audit-1.0.7 → 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
|
|
File without changes
|
{flask_postgresql_audit-1.0.7 → flask_postgresql_audit-1.0.8}/tests/migrations/script.py.mako
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|