diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d9aee6..de6daa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,6 @@ All notable changes to WuttJamaican 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.17.0 (2024-12-07) - -### Feat - -- convert all uuid fields from str to proper UUID - ## v0.16.2 (2024-12-06) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 7044e1e..e41c289 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "WuttJamaican" -version = "0.17.0" +version = "0.16.2" description = "Base package for Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] diff --git a/src/wuttjamaican/auth.py b/src/wuttjamaican/auth.py index 264fdb8..008d352 100644 --- a/src/wuttjamaican/auth.py +++ b/src/wuttjamaican/auth.py @@ -26,8 +26,6 @@ Auth Handler This defines the default :term:`auth handler`. """ -import uuid as _uuid - from wuttjamaican.app import GenericHandler @@ -128,28 +126,17 @@ class AuthHandler(GenericHandler): if not key: return - # maybe it is a uuid - if isinstance(key, _uuid.UUID): - role = session.get(model.Role, key) - if role: - return role + # try to match on Role.uuid + role = session.get(model.Role, key) + if role: + return role - else: # assuming it is a string - - # try to match on Role.uuid - try: - role = session.get(model.Role, _uuid.UUID(key)) - if role: - return role - except ValueError: - pass - - # try to match on Role.name - role = session.query(model.Role)\ - .filter_by(name=key)\ - .first() - if role: - return role + # try to match on Role.name + role = session.query(model.Role)\ + .filter_by(name=key)\ + .first() + if role: + return role # try settings; if value then recurse key = self.config.get(f'{self.appname}.role.{key}', @@ -191,32 +178,21 @@ class AuthHandler(GenericHandler): if isinstance(obj, model.User): return obj - # nb. these lookups require a db session - if session: + # or maybe it is a string + # (nb. these lookups require a db session) + if isinstance(obj, str) and session: - # or maybe it is a uuid - if isinstance(obj, _uuid.UUID): - user = session.get(model.User, obj) - if user: - return user + # try to match on User.uuid + user = session.get(model.User, obj) + if user: + return user - # or maybe it is a string - elif isinstance(obj, str): - - # try to match on User.uuid - try: - user = session.get(model.User, _uuid.UUID(obj)) - if user: - return user - except ValueError: - pass - - # try to match on User.username - user = session.query(model.User)\ - .filter(model.User.username == obj)\ - .first() - if user: - return user + # try to match on User.username + user = session.query(model.User)\ + .filter(model.User.username == obj)\ + .first() + if user: + return user # nb. obj is presumbly another type of object, e.g. Person diff --git a/src/wuttjamaican/db/alembic/versions/3abcc44f7f91_add_people.py b/src/wuttjamaican/db/alembic/versions/3abcc44f7f91_add_people.py index 754cbfa..143f6df 100644 --- a/src/wuttjamaican/db/alembic/versions/3abcc44f7f91_add_people.py +++ b/src/wuttjamaican/db/alembic/versions/3abcc44f7f91_add_people.py @@ -9,7 +9,6 @@ from typing import Sequence, Union from alembic import op import sqlalchemy as sa -import wuttjamaican.db.util # revision identifiers, used by Alembic. @@ -23,7 +22,7 @@ def upgrade() -> None: # person op.create_table('person', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('full_name', sa.String(length=100), nullable=False), sa.Column('first_name', sa.String(length=50), nullable=True), sa.Column('middle_name', sa.String(length=50), nullable=True), @@ -32,7 +31,7 @@ def upgrade() -> None: ) # user - op.add_column('user', sa.Column('person_uuid', wuttjamaican.db.util.UUID(), nullable=True)) + op.add_column('user', sa.Column('person_uuid', sa.String(length=32), nullable=True)) op.create_foreign_key(op.f('fk_user_person_uuid_person'), 'user', 'person', ['person_uuid'], ['uuid']) diff --git a/src/wuttjamaican/db/alembic/versions/6be0ed225f4d_convert_uuid_types.py b/src/wuttjamaican/db/alembic/versions/6be0ed225f4d_convert_uuid_types.py new file mode 100644 index 0000000..26db2ce --- /dev/null +++ b/src/wuttjamaican/db/alembic/versions/6be0ed225f4d_convert_uuid_types.py @@ -0,0 +1,66 @@ +"""convert uuid types + +Revision ID: 6be0ed225f4d +Revises: 6bf900765500 +Create Date: 2024-11-30 17:03:08.930050 + +""" +import uuid as _uuid +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import wuttjamaican.db.util + + +# revision identifiers, used by Alembic. +revision: str = '6be0ed225f4d' +down_revision: Union[str, None] = '6bf900765500' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + + # upgrade (convert uuid) + op.add_column('upgrade', sa.Column('uuid_true', wuttjamaican.db.util.UUID(), nullable=True)) + upgrade = sa.sql.table('upgrade', + sa.sql.column('uuid'), + sa.sql.column('uuid_true')) + + engine = op.get_bind() + cursor = engine.execute(upgrade.select()) + for row in cursor.fetchall(): + if row['uuid']: + uuid_true = _uuid.UUID(row['uuid']) + engine.execute(upgrade.update()\ + .where(upgrade.c.uuid == row['uuid'])\ + .values({'uuid_true': uuid_true})) + + op.drop_constraint('pk_upgrade', 'upgrade', type_='primary') + op.drop_column('upgrade', 'uuid') + op.alter_column('upgrade', 'uuid_true', new_column_name='uuid') + op.create_primary_key('pk_upgrade', 'upgrade', ['uuid']) + + +def downgrade() -> None: + + # upgrade (convert uuid) + op.add_column('upgrade', sa.Column('uuid_str', sa.VARCHAR(length=32), nullable=True)) + upgrade = sa.sql.table('upgrade', + sa.sql.column('uuid'), + sa.sql.column('uuid_str')) + + engine = op.get_bind() + cursor = engine.execute(upgrade.select()) + for row in cursor.fetchall(): + if row['uuid']: + uuid_str = row['uuid'].hex + engine.execute(upgrade.update()\ + .where(upgrade.c.uuid == row['uuid'])\ + .values({'uuid_str': uuid_str})) + + op.drop_constraint('pk_upgrade', 'upgrade', type_='primary') + op.drop_column('upgrade', 'uuid') + op.alter_column('upgrade', 'uuid_str', new_column_name='uuid') + op.create_primary_key('pk_upgrade', 'upgrade', ['uuid']) diff --git a/src/wuttjamaican/db/alembic/versions/d686f7abe3e0_add_users_roles.py b/src/wuttjamaican/db/alembic/versions/d686f7abe3e0_add_users_roles.py index b0a541f..ffacd30 100644 --- a/src/wuttjamaican/db/alembic/versions/d686f7abe3e0_add_users_roles.py +++ b/src/wuttjamaican/db/alembic/versions/d686f7abe3e0_add_users_roles.py @@ -9,7 +9,6 @@ from typing import Sequence, Union from alembic import op import sqlalchemy as sa -import wuttjamaican.db.util # revision identifiers, used by Alembic. @@ -23,7 +22,7 @@ def upgrade() -> None: # role op.create_table('role', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('name', sa.String(length=100), nullable=False), sa.Column('notes', sa.Text(), nullable=True), sa.PrimaryKeyConstraint('uuid'), @@ -32,7 +31,7 @@ def upgrade() -> None: # user op.create_table('user', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('username', sa.String(length=25), nullable=False), sa.Column('password', sa.String(length=60), nullable=True), sa.Column('active', sa.Boolean(), nullable=False), @@ -42,7 +41,7 @@ def upgrade() -> None: # permission op.create_table('permission', - sa.Column('role_uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('role_uuid', sa.String(length=32), nullable=False), sa.Column('permission', sa.String(length=254), nullable=False), sa.ForeignKeyConstraint(['role_uuid'], ['role.uuid'], name=op.f('fk_permission_role_uuid_role')), sa.PrimaryKeyConstraint('role_uuid', 'permission') @@ -50,9 +49,9 @@ def upgrade() -> None: # user_x_role op.create_table('user_x_role', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('user_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('role_uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('uuid', sa.String(length=32), nullable=False), + sa.Column('user_uuid', sa.String(length=32), nullable=False), + sa.Column('role_uuid', sa.String(length=32), nullable=False), sa.ForeignKeyConstraint(['role_uuid'], ['role.uuid'], name=op.f('fk_user_x_role_role_uuid_role')), sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], name=op.f('fk_user_x_role_user_uuid_user')), sa.PrimaryKeyConstraint('uuid') diff --git a/src/wuttjamaican/db/alembic/versions/ebd75b9feaa7_add_upgrades.py b/src/wuttjamaican/db/alembic/versions/ebd75b9feaa7_add_upgrades.py index 0afd3b5..1ccbd66 100644 --- a/src/wuttjamaican/db/alembic/versions/ebd75b9feaa7_add_upgrades.py +++ b/src/wuttjamaican/db/alembic/versions/ebd75b9feaa7_add_upgrades.py @@ -10,7 +10,6 @@ from typing import Sequence, Union from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql -import wuttjamaican.db.util # revision identifiers, used by Alembic. revision: str = 'ebd75b9feaa7' @@ -24,15 +23,15 @@ def upgrade() -> None: # upgrade sa.Enum('PENDING', 'EXECUTING', 'SUCCESS', 'FAILURE', name='upgradestatus').create(op.get_bind()) op.create_table('upgrade', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('created', sa.DateTime(timezone=True), nullable=False), - sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('created_by_uuid', sa.String(length=32), nullable=False), sa.Column('description', sa.String(length=255), nullable=False), sa.Column('notes', sa.Text(), nullable=True), sa.Column('executing', sa.Boolean(), nullable=False), sa.Column('status', postgresql.ENUM('PENDING', 'EXECUTING', 'SUCCESS', 'FAILURE', name='upgradestatus', create_type=False), nullable=False), sa.Column('executed', sa.DateTime(timezone=True), nullable=True), - sa.Column('executed_by_uuid', wuttjamaican.db.util.UUID(), nullable=True), + sa.Column('executed_by_uuid', sa.String(length=32), nullable=True), sa.Column('exit_code', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_upgrade_created_by_uuid_user')), sa.ForeignKeyConstraint(['executed_by_uuid'], ['user.uuid'], name=op.f('fk_upgrade_executed_by_uuid_user')), diff --git a/src/wuttjamaican/db/util.py b/src/wuttjamaican/db/util.py index bc06e22..643e267 100644 --- a/src/wuttjamaican/db/util.py +++ b/src/wuttjamaican/db/util.py @@ -30,7 +30,7 @@ import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.dialects.postgresql import UUID as PGUUID -from wuttjamaican.util import make_true_uuid +from wuttjamaican.util import make_uuid # nb. this convention comes from upstream docs @@ -111,10 +111,10 @@ def uuid_column(*args, **kwargs): Returns a UUID column for use as a table's primary key. """ if not args: - args = (UUID(),) + args = (sa.String(length=32),) kwargs.setdefault('primary_key', True) kwargs.setdefault('nullable', False) - kwargs.setdefault('default', make_true_uuid) + kwargs.setdefault('default', make_uuid) return sa.Column(*args, **kwargs) @@ -126,7 +126,7 @@ def uuid_fk_column(target_column, *args, **kwargs): e.g. ``'user.uuid'``. """ if not args: - args = (UUID(), sa.ForeignKey(target_column)) + args = (sa.String(length=32), sa.ForeignKey(target_column)) return sa.Column(*args, **kwargs) diff --git a/tests/db/test_util.py b/tests/db/test_util.py index 44ff6f0..8ffef76 100644 --- a/tests/db/test_util.py +++ b/tests/db/test_util.py @@ -114,7 +114,8 @@ else: def test_basic(self): column = mod.uuid_column() self.assertIsInstance(column, sa.Column) - self.assertIsInstance(column.type, mod.UUID) + self.assertIsInstance(column.type, sa.String) + self.assertEqual(column.type.length, 32) class TestUUIDFKColumn(TestCase): @@ -122,7 +123,8 @@ else: def test_basic(self): column = mod.uuid_fk_column('foo.bar') self.assertIsInstance(column, sa.Column) - self.assertIsInstance(column.type, mod.UUID) + self.assertIsInstance(column.type, sa.String) + self.assertEqual(column.type.length, 32) class TestMakeTopoSortkey(DataTestCase): diff --git a/tests/test_auth.py b/tests/test_auth.py index 8780ba4..cf37417 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -93,7 +93,7 @@ else: self.config.usedb = True role = self.handler.get_role(self.session, 'mykey') self.assertIsNone(role) - setting = model.Setting(name='wutta.role.mykey', value=myrole.uuid.hex) + setting = model.Setting(name='wutta.role.mykey', value=myrole.uuid) self.session.add(setting) self.session.commit() role = self.handler.get_role(self.session, 'mykey') @@ -117,10 +117,6 @@ else: user = self.handler.get_user(myuser.uuid, session=self.session) self.assertIs(user, myuser) - # match on User.uuid (str) - user = self.handler.get_user(myuser.uuid.hex, session=self.session) - self.assertIs(user, myuser) - # match on User.username user = self.handler.get_user(myuser.username, session=self.session) self.assertIs(user, myuser)