3
0
Fork 0

feat: convert all uuid fields from str to proper UUID

ugh had to rewrite alembic migrations instead of just adding a new
one..  will be good to be past this hiccup
This commit is contained in:
Lance Edgar 2024-12-07 23:45:47 -06:00
parent 6a471b87c2
commit e1785ccfcc
8 changed files with 72 additions and 109 deletions

View file

@ -26,6 +26,8 @@ Auth Handler
This defines the default :term:`auth handler`. This defines the default :term:`auth handler`.
""" """
import uuid as _uuid
from wuttjamaican.app import GenericHandler from wuttjamaican.app import GenericHandler
@ -126,17 +128,28 @@ class AuthHandler(GenericHandler):
if not key: if not key:
return return
# try to match on Role.uuid # maybe it is a uuid
role = session.get(model.Role, key) if isinstance(key, _uuid.UUID):
if role: role = session.get(model.Role, key)
return role if role:
return role
# try to match on Role.name else: # assuming it is a string
role = session.query(model.Role)\
.filter_by(name=key)\ # try to match on Role.uuid
.first() try:
if role: role = session.get(model.Role, _uuid.UUID(key))
return role 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 settings; if value then recurse # try settings; if value then recurse
key = self.config.get(f'{self.appname}.role.{key}', key = self.config.get(f'{self.appname}.role.{key}',
@ -178,21 +191,32 @@ class AuthHandler(GenericHandler):
if isinstance(obj, model.User): if isinstance(obj, model.User):
return obj return obj
# or maybe it is a string # nb. these lookups require a db session
# (nb. these lookups require a db session) if session:
if isinstance(obj, str) and session:
# try to match on User.uuid # or maybe it is a uuid
user = session.get(model.User, obj) if isinstance(obj, _uuid.UUID):
if user: user = session.get(model.User, obj)
return user if user:
return user
# try to match on User.username # or maybe it is a string
user = session.query(model.User)\ elif isinstance(obj, str):
.filter(model.User.username == obj)\
.first() # try to match on User.uuid
if user: try:
return user 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
# nb. obj is presumbly another type of object, e.g. Person # nb. obj is presumbly another type of object, e.g. Person

View file

@ -9,6 +9,7 @@ from typing import Sequence, Union
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import wuttjamaican.db.util
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@ -22,7 +23,7 @@ def upgrade() -> None:
# person # person
op.create_table('person', op.create_table('person',
sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('full_name', sa.String(length=100), nullable=False), sa.Column('full_name', sa.String(length=100), nullable=False),
sa.Column('first_name', sa.String(length=50), nullable=True), sa.Column('first_name', sa.String(length=50), nullable=True),
sa.Column('middle_name', sa.String(length=50), nullable=True), sa.Column('middle_name', sa.String(length=50), nullable=True),
@ -31,7 +32,7 @@ def upgrade() -> None:
) )
# user # user
op.add_column('user', sa.Column('person_uuid', sa.String(length=32), nullable=True)) op.add_column('user', sa.Column('person_uuid', wuttjamaican.db.util.UUID(), nullable=True))
op.create_foreign_key(op.f('fk_user_person_uuid_person'), 'user', 'person', ['person_uuid'], ['uuid']) op.create_foreign_key(op.f('fk_user_person_uuid_person'), 'user', 'person', ['person_uuid'], ['uuid'])

View file

@ -1,66 +0,0 @@
"""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'])

View file

@ -9,6 +9,7 @@ from typing import Sequence, Union
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import wuttjamaican.db.util
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@ -22,7 +23,7 @@ def upgrade() -> None:
# role # role
op.create_table('role', op.create_table('role',
sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False), sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('notes', sa.Text(), nullable=True), sa.Column('notes', sa.Text(), nullable=True),
sa.PrimaryKeyConstraint('uuid'), sa.PrimaryKeyConstraint('uuid'),
@ -31,7 +32,7 @@ def upgrade() -> None:
# user # user
op.create_table('user', op.create_table('user',
sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('username', sa.String(length=25), nullable=False), sa.Column('username', sa.String(length=25), nullable=False),
sa.Column('password', sa.String(length=60), nullable=True), sa.Column('password', sa.String(length=60), nullable=True),
sa.Column('active', sa.Boolean(), nullable=False), sa.Column('active', sa.Boolean(), nullable=False),
@ -41,7 +42,7 @@ def upgrade() -> None:
# permission # permission
op.create_table('permission', op.create_table('permission',
sa.Column('role_uuid', sa.String(length=32), nullable=False), sa.Column('role_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('permission', sa.String(length=254), 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.ForeignKeyConstraint(['role_uuid'], ['role.uuid'], name=op.f('fk_permission_role_uuid_role')),
sa.PrimaryKeyConstraint('role_uuid', 'permission') sa.PrimaryKeyConstraint('role_uuid', 'permission')
@ -49,9 +50,9 @@ def upgrade() -> None:
# user_x_role # user_x_role
op.create_table('user_x_role', op.create_table('user_x_role',
sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('user_uuid', sa.String(length=32), nullable=False), sa.Column('user_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('role_uuid', sa.String(length=32), nullable=False), sa.Column('role_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.ForeignKeyConstraint(['role_uuid'], ['role.uuid'], name=op.f('fk_user_x_role_role_uuid_role')), 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.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], name=op.f('fk_user_x_role_user_uuid_user')),
sa.PrimaryKeyConstraint('uuid') sa.PrimaryKeyConstraint('uuid')

View file

@ -10,6 +10,7 @@ from typing import Sequence, Union
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
import wuttjamaican.db.util
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = 'ebd75b9feaa7' revision: str = 'ebd75b9feaa7'
@ -23,15 +24,15 @@ def upgrade() -> None:
# upgrade # upgrade
sa.Enum('PENDING', 'EXECUTING', 'SUCCESS', 'FAILURE', name='upgradestatus').create(op.get_bind()) sa.Enum('PENDING', 'EXECUTING', 'SUCCESS', 'FAILURE', name='upgradestatus').create(op.get_bind())
op.create_table('upgrade', op.create_table('upgrade',
sa.Column('uuid', sa.String(length=32), nullable=False), sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('created', sa.DateTime(timezone=True), nullable=False), sa.Column('created', sa.DateTime(timezone=True), nullable=False),
sa.Column('created_by_uuid', sa.String(length=32), nullable=False), sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('description', sa.String(length=255), nullable=False), sa.Column('description', sa.String(length=255), nullable=False),
sa.Column('notes', sa.Text(), nullable=True), sa.Column('notes', sa.Text(), nullable=True),
sa.Column('executing', sa.Boolean(), nullable=False), 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('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', sa.DateTime(timezone=True), nullable=True),
sa.Column('executed_by_uuid', sa.String(length=32), nullable=True), sa.Column('executed_by_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('exit_code', sa.Integer(), 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(['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')), sa.ForeignKeyConstraint(['executed_by_uuid'], ['user.uuid'], name=op.f('fk_upgrade_executed_by_uuid_user')),

View file

@ -30,7 +30,7 @@ import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
from sqlalchemy.dialects.postgresql import UUID as PGUUID from sqlalchemy.dialects.postgresql import UUID as PGUUID
from wuttjamaican.util import make_uuid from wuttjamaican.util import make_true_uuid
# nb. this convention comes from upstream docs # 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. Returns a UUID column for use as a table's primary key.
""" """
if not args: if not args:
args = (sa.String(length=32),) args = (UUID(),)
kwargs.setdefault('primary_key', True) kwargs.setdefault('primary_key', True)
kwargs.setdefault('nullable', False) kwargs.setdefault('nullable', False)
kwargs.setdefault('default', make_uuid) kwargs.setdefault('default', make_true_uuid)
return sa.Column(*args, **kwargs) return sa.Column(*args, **kwargs)
@ -126,7 +126,7 @@ def uuid_fk_column(target_column, *args, **kwargs):
e.g. ``'user.uuid'``. e.g. ``'user.uuid'``.
""" """
if not args: if not args:
args = (sa.String(length=32), sa.ForeignKey(target_column)) args = (UUID(), sa.ForeignKey(target_column))
return sa.Column(*args, **kwargs) return sa.Column(*args, **kwargs)

View file

@ -114,8 +114,7 @@ else:
def test_basic(self): def test_basic(self):
column = mod.uuid_column() column = mod.uuid_column()
self.assertIsInstance(column, sa.Column) self.assertIsInstance(column, sa.Column)
self.assertIsInstance(column.type, sa.String) self.assertIsInstance(column.type, mod.UUID)
self.assertEqual(column.type.length, 32)
class TestUUIDFKColumn(TestCase): class TestUUIDFKColumn(TestCase):
@ -123,8 +122,7 @@ else:
def test_basic(self): def test_basic(self):
column = mod.uuid_fk_column('foo.bar') column = mod.uuid_fk_column('foo.bar')
self.assertIsInstance(column, sa.Column) self.assertIsInstance(column, sa.Column)
self.assertIsInstance(column.type, sa.String) self.assertIsInstance(column.type, mod.UUID)
self.assertEqual(column.type.length, 32)
class TestMakeTopoSortkey(DataTestCase): class TestMakeTopoSortkey(DataTestCase):

View file

@ -93,7 +93,7 @@ else:
self.config.usedb = True self.config.usedb = True
role = self.handler.get_role(self.session, 'mykey') role = self.handler.get_role(self.session, 'mykey')
self.assertIsNone(role) self.assertIsNone(role)
setting = model.Setting(name='wutta.role.mykey', value=myrole.uuid) setting = model.Setting(name='wutta.role.mykey', value=myrole.uuid.hex)
self.session.add(setting) self.session.add(setting)
self.session.commit() self.session.commit()
role = self.handler.get_role(self.session, 'mykey') role = self.handler.get_role(self.session, 'mykey')
@ -117,6 +117,10 @@ else:
user = self.handler.get_user(myuser.uuid, session=self.session) user = self.handler.get_user(myuser.uuid, session=self.session)
self.assertIs(user, myuser) 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 # match on User.username
user = self.handler.get_user(myuser.username, session=self.session) user = self.handler.get_user(myuser.username, session=self.session)
self.assertIs(user, myuser) self.assertIs(user, myuser)