From 09e4ef6a1eeb3fc37cbf7fa2b89aeb179e289853 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 18 Dec 2025 20:57:46 -0600 Subject: [PATCH 1/3] docs: add basic feature, usage docs --- docs/index.rst | 4 +++- docs/narr/features.rst | 26 ++++++++++++++++++++++++++ docs/narr/install.rst | 2 +- docs/narr/usage.rst | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 docs/narr/features.rst create mode 100644 docs/narr/usage.rst diff --git a/docs/index.rst b/docs/index.rst index a8f656f..6b003b9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,7 +5,7 @@ Wutta-Continuum This package adds data versioning/history for `WuttJamaican`_, using `SQLAlchemy-Continuum`_. -.. _WuttJamaican: https://rattailproject.org/docs/wuttjamaican/ +.. _WuttJamaican: https://docs.wuttaproject.org/wuttjamaican/ .. _SQLAlchemy-Continuum: https://sqlalchemy-continuum.readthedocs.io/en/latest/ @@ -20,7 +20,9 @@ This package adds data versioning/history for `WuttJamaican`_, using :maxdepth: 2 :caption: Documentation + narr/features narr/install + narr/usage .. toctree:: :maxdepth: 1 diff --git a/docs/narr/features.rst b/docs/narr/features.rst new file mode 100644 index 0000000..20b08a4 --- /dev/null +++ b/docs/narr/features.rst @@ -0,0 +1,26 @@ + +Features +======== + +The general idea is to provide an audit/versioning trail for important +data tables. + +Each table defined in the :term:`app model` can either be versioned, +or not. Nothing changes for a non-versioned table. + +For a "versioned" table, a secondary "versions" table is created, +schema for which is a superset of the original "versioned" table. +When records change in the original table, new "version" records are +added to the versions table. + +Therefore you can see how a record has changed over time, by +inspecting its corresponding versions. + +When any record changes (for any versioned table), a new "transaction" +record is also created. This identifies the user responsible, and +timestamp etc. Any new version records will tie back to this +transaction record. + +All this is made possible by SQLAlchemy-Continuum; the Wutta-Continuum +package mostly just adds config glue. See also +:doc:`sqlalchemy-continuum:index`. diff --git a/docs/narr/install.rst b/docs/narr/install.rst index 4959f96..1494d22 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -31,7 +31,7 @@ this package for database migrations. You should already have an [alembic] script_location = wuttjamaican.db:alembic - version_locations = wuttjamaican.db:alembic/versions wutta_continuum.db:alembic/versions + version_locations = wutta_continuum.db:alembic/versions poser.db:alembic/versions wuttjamaican.db:alembic/versions Then (as you would have done previously in :ref:`wuttjamaican:db-setup`) you can migrate your database to add the diff --git a/docs/narr/usage.rst b/docs/narr/usage.rst new file mode 100644 index 0000000..4d42288 --- /dev/null +++ b/docs/narr/usage.rst @@ -0,0 +1,41 @@ + +Usage +===== + +You can check the feature status with +:meth:`~wutta_continuum.app.WuttaContinuumAppProvider.continuum_is_enabled()`:: + + app = config.get_app() + + if not app.continuum_is_enabled(): + print("Oh no! Continuum is not enabled.") + +The rest of this will assume the feature is enabled. + + +Built-In Models +--------------- + +The following built-in models are versioned. So, when records are +added / modified / removed via the ORM, new version records are +automatically created for each of these: + +* :class:`~wuttjamaican:wuttjamaican.db.model.auth.Permission` +* :class:`~wuttjamaican:wuttjamaican.db.model.base.Person` +* :class:`~wuttjamaican:wuttjamaican.db.model.auth.Role` +* :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` +* :class:`~wuttjamaican:wuttjamaican.db.model.auth.UserRole` + + +Object Versions +--------------- + +A versioned model works normally but also has a ``versions`` +attribute, which reflects the list of version records:: + + user = session.query(model.User).first() + + for version in user.versions: + print(version) + +See also :doc:`sqlalchemy-continuum:version_objects`. From b19f565aa10b6de9de4332c4e66c479c03456036 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 18 Dec 2025 23:02:08 -0600 Subject: [PATCH 2/3] feat: add TransactionMetaPlugin to save comments when applicable --- src/wutta_continuum/conf.py | 31 +++++++++++++- .../46fb4711411d_add_transaction_meta.py | 40 +++++++++++++++++++ tests/test_conf.py | 19 ++++++++- 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 src/wutta_continuum/db/alembic/versions/46fb4711411d_add_transaction_meta.py diff --git a/src/wutta_continuum/conf.py b/src/wutta_continuum/conf.py index 1cb3bd7..361131a 100644 --- a/src/wutta_continuum/conf.py +++ b/src/wutta_continuum/conf.py @@ -28,7 +28,7 @@ import socket from sqlalchemy.orm import configure_mappers from sqlalchemy_continuum import make_versioned -from sqlalchemy_continuum.plugins import Plugin +from sqlalchemy_continuum.plugins import Plugin, TransactionMetaPlugin from wuttjamaican.conf import WuttaConfigExtension from wuttjamaican.util import load_object @@ -66,6 +66,20 @@ class WuttaContinuumConfigExtension(WuttaConfigExtension): For more about SQLAlchemy-Continuum see :doc:`sqlalchemy-continuum:intro`. + + Two plugins are provided to ``make_versioned()``: + + The first is ``TransactionMetaPlugin`` for sake of adding + comments (see + :mod:`~sqlalchemy-continuum:sqlalchemy_continuum.plugins.transaction_meta`). + + The second by default is :class:`WuttaContinuumPlugin` but you + can override with config: + + .. code-block:: ini + + [wutta_continuum] + wutta_plugin_spec = poser.db.continuum:PoserContinuumPlugin """ # only do this if config enables it if not config.get_bool( @@ -86,7 +100,7 @@ class WuttaContinuumConfigExtension(WuttaConfigExtension): raise RuntimeError("something not right, app already has model") # let sqlalchemy-continuum do its thing - make_versioned(plugins=[plugin()]) + make_versioned(plugins=[TransactionMetaPlugin(), plugin()]) # must load model *between* prev and next calls app.get_model() @@ -148,3 +162,16 @@ class WuttaContinuumPlugin(Plugin): kwargs["user_id"] = user_id return kwargs + + def before_flush(self, uow, session): + """ + We use this hook to inject the "comment" for current + transaction, if applicable. + + This checks the session for the comment; so any session can + specify one like so:: + + session.info["continuum_comment"] = "hello world" + """ + if comment := session.info.get("continuum_comment"): + uow.current_transaction.meta["comment"] = comment diff --git a/src/wutta_continuum/db/alembic/versions/46fb4711411d_add_transaction_meta.py b/src/wutta_continuum/db/alembic/versions/46fb4711411d_add_transaction_meta.py new file mode 100644 index 0000000..ebaf636 --- /dev/null +++ b/src/wutta_continuum/db/alembic/versions/46fb4711411d_add_transaction_meta.py @@ -0,0 +1,40 @@ +"""add transaction_meta + +Revision ID: 46fb4711411d +Revises: 989392cc191d +Create Date: 2025-12-18 21:22:33.382628 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import wuttjamaican.db.util + + +# revision identifiers, used by Alembic. +revision: str = "46fb4711411d" +down_revision: Union[str, None] = "989392cc191d" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + + # transaction_meta + op.create_table( + "transaction_meta", + sa.Column("transaction_id", sa.BigInteger(), nullable=False), + sa.Column("key", sa.Unicode(length=255), nullable=False), + sa.Column("value", sa.UnicodeText(), nullable=True), + sa.PrimaryKeyConstraint( + "transaction_id", "key", name=op.f("pk_transaction_meta") + ), + ) + + +def downgrade() -> None: + + # transaction_meta + op.drop_table("transaction_meta") diff --git a/tests/test_conf.py b/tests/test_conf.py index 1ea5d44..697592a 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -2,7 +2,7 @@ import socket -from unittest.mock import patch +from unittest.mock import patch, Mock from wuttjamaican.testing import ConfigTestCase, DataTestCase @@ -71,3 +71,20 @@ class TestWuttaContinuumPlugin(DataTestCase): plugin.transaction_args(None, self.session), {"remote_addr": "127.0.0.1", "user_id": "some-random-uuid"}, ) + + def test_before_flush(self): + plugin = self.make_plugin() + + meta = {} + txn = Mock(meta=meta) + uow = Mock(current_transaction=txn) + + # no comment in session or transaction + plugin.before_flush(uow, self.session) + self.assertNotIn("comment", meta) + + # transaction comment matches session + self.session.info["continuum_comment"] = "whaddyaknow" + plugin.before_flush(uow, self.session) + self.assertIn("comment", meta) + self.assertEqual(meta["comment"], "whaddyaknow") From 1fc280eff1f84b2d18d9437a0b981c9a4d4a7fda Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 20 Dec 2025 20:09:59 -0600 Subject: [PATCH 3/3] =?UTF-8?q?bump:=20version=200.2.2=20=E2=86=92=200.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ pyproject.toml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e3522c..6829688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to Wutta-Continuum will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.0 (2025-12-20) + +### Feat + +- add TransactionMetaPlugin to save comments when applicable + ## v0.2.2 (2025-10-29) ### Fix diff --git a/pyproject.toml b/pyproject.toml index aa500d7..7fbb112 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "Wutta-Continuum" -version = "0.2.2" +version = "0.3.0" description = "SQLAlchemy-Continuum versioning for Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] @@ -27,7 +27,7 @@ classifiers = [ requires-python = ">= 3.8" dependencies = [ "SQLAlchemy-Continuum", - "WuttJamaican[db]>=0.24.1", + "WuttJamaican[db]>=0.27.0", ]