Compare commits

..

No commits in common. "master" and "v0.3.0" have entirely different histories.

5 changed files with 29 additions and 69 deletions

View file

@ -5,12 +5,6 @@ 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/) 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). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## v0.3.1 (2025-12-31)
### Fix
- set transaction user based on session info, when applicable
## v0.3.0 (2025-12-20) ## v0.3.0 (2025-12-20)
### Feat ### Feat

View file

@ -3,4 +3,4 @@
SQLAlchemy-Continuum versioning for Wutta Framework SQLAlchemy-Continuum versioning for Wutta Framework
See docs at https://docs.wuttaproject.org/wutta-continuum/ See docs at https://rattailproject.org/docs/wutta-continuum/

View file

@ -6,7 +6,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "Wutta-Continuum" name = "Wutta-Continuum"
version = "0.3.1" version = "0.3.0"
description = "SQLAlchemy-Continuum versioning for Wutta Framework" description = "SQLAlchemy-Continuum versioning for Wutta Framework"
readme = "README.md" readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]

View file

@ -80,9 +80,6 @@ class WuttaContinuumConfigExtension(WuttaConfigExtension):
[wutta_continuum] [wutta_continuum]
wutta_plugin_spec = poser.db.continuum:PoserContinuumPlugin wutta_plugin_spec = poser.db.continuum:PoserContinuumPlugin
See also the SQLAlchemy-Continuum docs for
:doc:`sqlalchemy-continuum:plugins`.
""" """
# only do this if config enables it # only do this if config enables it
if not config.get_bool( if not config.get_bool(
@ -116,64 +113,44 @@ class WuttaContinuumPlugin(Plugin):
""" """
SQLAlchemy-Continuum manager plugin for Wutta-Continuum. SQLAlchemy-Continuum manager plugin for Wutta-Continuum.
This is the default plugin used within This tries to assign the current user and IP address to the
:meth:`~WuttaContinuumConfigExtension.startup()` unless config transaction.
overrides.
This tries to establish the user and IP address responsible, and It will assume the "current machine" IP address, which may be
comment if applicable, for the current transaction. suitable for some apps but not all (e.g. web apps, where IP
address should reflect an arbitrary client machine).
However it does not actually have a way to determine the current
user. WuttaWeb therefore uses a different plugin, based on this
one, to get both the user and IP address from current request.
You can override this to use a custom plugin for this purpose; if
so you must specify in your config file:
.. code-block:: ini
[wutta_continuum]
wutta_plugin_spec = poser.db.continuum:PoserContinuumPlugin
See also the SQLAlchemy-Continuum docs for See also the SQLAlchemy-Continuum docs for
:doc:`sqlalchemy-continuum:plugins`. :doc:`sqlalchemy-continuum:plugins`.
""" """
def get_remote_addr(self, uow, session): # pylint: disable=unused-argument def get_remote_addr( # pylint: disable=empty-docstring,unused-argument
""" self, uow, session
This should return the effective IP address responsible for ):
the current change(s). """ """
Default logic will assume the "current machine" e.g. where a
CLI command or script is running. In practice that often
means this winds up being ``127.0.0.1`` or similar.
:returns: IP address (v4 or v6) as string
"""
host = socket.gethostname() host = socket.gethostname()
return socket.gethostbyname(host) return socket.gethostbyname(host)
def get_user_id(self, uow, session): # pylint: disable=unused-argument def get_user_id( # pylint: disable=empty-docstring,unused-argument
""" self, uow, session
This should return the effective ``User.uuid`` indicating who ):
is responsible for the current change(s). """ """
Default logic does not have a way to determine current user on
its own per se. However it can inspect the session, and use a
value from there if found.
Any session can therefore declare the resonsible user::
myuser = session.query(model.User).first()
session.info["continuum_user_id"] = myuser.uuid
:returns: :attr:`wuttjamaican.db.model.auth.User.uuid` value,
or ``None``
"""
if user_id := session.info.get("continuum_user_id"):
return user_id
return None return None
def transaction_args(self, uow, session): def transaction_args(self, uow, session): # pylint: disable=empty-docstring
""" """ """
This is a standard hook method for SQLAchemy-Continuum
plugins. We use it to (try to) inject these values, which
then become set on the current (new) transaction:
* ``remote_addr`` - effective IP address causing the change
* see :meth:`get_remote_addr()`
* ``user_id`` - effective ``User.uuid`` for change authorship
* see :meth:`get_user_id()`
"""
kwargs = {} kwargs = {}
remote_addr = self.get_remote_addr(uow, session) remote_addr = self.get_remote_addr(uow, session)

View file

@ -55,20 +55,9 @@ class TestWuttaContinuumPlugin(DataTestCase):
self.assertEqual(plugin.get_remote_addr(None, self.session), "127.0.0.1") self.assertEqual(plugin.get_remote_addr(None, self.session), "127.0.0.1")
def test_user_id(self): def test_user_id(self):
model = self.app.model
plugin = self.make_plugin() plugin = self.make_plugin()
fred = model.User(username="fred")
self.session.add(fred)
self.session.commit()
# empty by default
self.assertIsNone(plugin.get_user_id(None, self.session)) self.assertIsNone(plugin.get_user_id(None, self.session))
# but session can declare one
self.session.info["continuum_user_id"] = fred.uuid
self.assertEqual(plugin.get_user_id(None, self.session), fred.uuid)
def test_transaction_args(self): def test_transaction_args(self):
plugin = self.make_plugin() plugin = self.make_plugin()
with patch.object(socket, "gethostbyname", return_value="127.0.0.1"): with patch.object(socket, "gethostbyname", return_value="127.0.0.1"):