Refactor(backend): improve formatting in services, repositories & tests

- Consistently format multi-line function headers, exception handling, and repository method calls for readability.
- Reorganize misplaced imports across modules (e.g., services & tests) into proper sorted order.
- Adjust indentation, line breaks, and spacing inconsistencies in tests and migration files.
- Cleanup unnecessary trailing newlines and reorganize `__all__` declarations for consistency.
This commit is contained in:
2026-02-28 18:37:56 +01:00
parent 98b455fdc3
commit 4c6bf55bcc
38 changed files with 567 additions and 337 deletions

View File

@@ -40,6 +40,7 @@ def include_object(object, name, type_, reflected, compare_to):
return False return False
return True return True
# Interpret the config file for Python logging. # Interpret the config file for Python logging.
# This line sets up loggers basically. # This line sets up loggers basically.
if config.config_file_name is not None: if config.config_file_name is not None:

View File

@@ -1,262 +1,446 @@
"""initial models """initial models
Revision ID: 0001 Revision ID: 0001
Revises: Revises:
Create Date: 2025-11-27 09:08:09.464506 Create Date: 2025-11-27 09:08:09.464506
""" """
from typing import Sequence, Union
from alembic import op from collections.abc import Sequence
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '0001' revision: str = "0001"
down_revision: Union[str, None] = None down_revision: str | None = None
branch_labels: Union[str, Sequence[str], None] = None branch_labels: str | Sequence[str] | None = None
depends_on: Union[str, Sequence[str], None] = None depends_on: str | Sequence[str] | None = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.create_table('oauth_states', op.create_table(
sa.Column('state', sa.String(length=255), nullable=False), "oauth_states",
sa.Column('code_verifier', sa.String(length=128), nullable=True), sa.Column("state", sa.String(length=255), nullable=False),
sa.Column('nonce', sa.String(length=255), nullable=True), sa.Column("code_verifier", sa.String(length=128), nullable=True),
sa.Column('provider', sa.String(length=50), nullable=False), sa.Column("nonce", sa.String(length=255), nullable=True),
sa.Column('redirect_uri', sa.String(length=500), nullable=True), sa.Column("provider", sa.String(length=50), nullable=False),
sa.Column('user_id', sa.UUID(), nullable=True), sa.Column("redirect_uri", sa.String(length=500), nullable=True),
sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False), sa.Column("user_id", sa.UUID(), nullable=True),
sa.Column('id', sa.UUID(), nullable=False), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False), sa.Column("id", sa.UUID(), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id') sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint("id"),
) )
op.create_index(op.f('ix_oauth_states_state'), 'oauth_states', ['state'], unique=True) op.create_index(
op.create_table('organizations', op.f("ix_oauth_states_state"), "oauth_states", ["state"], unique=True
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('slug', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('settings', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id')
) )
op.create_index(op.f('ix_organizations_is_active'), 'organizations', ['is_active'], unique=False) op.create_table(
op.create_index(op.f('ix_organizations_name'), 'organizations', ['name'], unique=False) "organizations",
op.create_index('ix_organizations_name_active', 'organizations', ['name', 'is_active'], unique=False) sa.Column("name", sa.String(length=255), nullable=False),
op.create_index(op.f('ix_organizations_slug'), 'organizations', ['slug'], unique=True) sa.Column("slug", sa.String(length=255), nullable=False),
op.create_index('ix_organizations_slug_active', 'organizations', ['slug', 'is_active'], unique=False) sa.Column("description", sa.Text(), nullable=True),
op.create_table('users', sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column('email', sa.String(length=255), nullable=False), sa.Column("settings", postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('password_hash', sa.String(length=255), nullable=True), sa.Column("id", sa.UUID(), nullable=False),
sa.Column('first_name', sa.String(length=100), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column('last_name', sa.String(length=100), nullable=True), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.Column('phone_number', sa.String(length=20), nullable=True), sa.PrimaryKeyConstraint("id"),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('is_superuser', sa.Boolean(), nullable=False),
sa.Column('preferences', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
sa.Column('locale', sa.String(length=10), nullable=True),
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id')
) )
op.create_index(op.f('ix_users_deleted_at'), 'users', ['deleted_at'], unique=False) op.create_index(
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True) op.f("ix_organizations_is_active"), "organizations", ["is_active"], unique=False
op.create_index(op.f('ix_users_is_active'), 'users', ['is_active'], unique=False)
op.create_index(op.f('ix_users_is_superuser'), 'users', ['is_superuser'], unique=False)
op.create_index(op.f('ix_users_locale'), 'users', ['locale'], unique=False)
op.create_table('oauth_accounts',
sa.Column('user_id', sa.UUID(), nullable=False),
sa.Column('provider', sa.String(length=50), nullable=False),
sa.Column('provider_user_id', sa.String(length=255), nullable=False),
sa.Column('provider_email', sa.String(length=255), nullable=True),
sa.Column('access_token_encrypted', sa.String(length=2048), nullable=True),
sa.Column('refresh_token_encrypted', sa.String(length=2048), nullable=True),
sa.Column('token_expires_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('provider', 'provider_user_id', name='uq_oauth_provider_user')
) )
op.create_index(op.f('ix_oauth_accounts_provider'), 'oauth_accounts', ['provider'], unique=False) op.create_index(
op.create_index(op.f('ix_oauth_accounts_provider_email'), 'oauth_accounts', ['provider_email'], unique=False) op.f("ix_organizations_name"), "organizations", ["name"], unique=False
op.create_index(op.f('ix_oauth_accounts_user_id'), 'oauth_accounts', ['user_id'], unique=False)
op.create_index('ix_oauth_accounts_user_provider', 'oauth_accounts', ['user_id', 'provider'], unique=False)
op.create_table('oauth_clients',
sa.Column('client_id', sa.String(length=64), nullable=False),
sa.Column('client_secret_hash', sa.String(length=255), nullable=True),
sa.Column('client_name', sa.String(length=255), nullable=False),
sa.Column('client_description', sa.String(length=1000), nullable=True),
sa.Column('client_type', sa.String(length=20), nullable=False),
sa.Column('redirect_uris', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
sa.Column('allowed_scopes', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
sa.Column('access_token_lifetime', sa.String(length=10), nullable=False),
sa.Column('refresh_token_lifetime', sa.String(length=10), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('owner_user_id', sa.UUID(), nullable=True),
sa.Column('mcp_server_url', sa.String(length=2048), nullable=True),
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['owner_user_id'], ['users.id'], ondelete='SET NULL'),
sa.PrimaryKeyConstraint('id')
) )
op.create_index(op.f('ix_oauth_clients_client_id'), 'oauth_clients', ['client_id'], unique=True) op.create_index(
op.create_index(op.f('ix_oauth_clients_is_active'), 'oauth_clients', ['is_active'], unique=False) "ix_organizations_name_active",
op.create_table('user_organizations', "organizations",
sa.Column('user_id', sa.UUID(), nullable=False), ["name", "is_active"],
sa.Column('organization_id', sa.UUID(), nullable=False), unique=False,
sa.Column('role', sa.Enum('OWNER', 'ADMIN', 'MEMBER', 'GUEST', name='organizationrole'), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('custom_permissions', sa.String(length=500), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['organization_id'], ['organizations.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('user_id', 'organization_id')
) )
op.create_index('ix_user_org_org_active', 'user_organizations', ['organization_id', 'is_active'], unique=False) op.create_index(
op.create_index('ix_user_org_role', 'user_organizations', ['role'], unique=False) op.f("ix_organizations_slug"), "organizations", ["slug"], unique=True
op.create_index('ix_user_org_user_active', 'user_organizations', ['user_id', 'is_active'], unique=False)
op.create_index(op.f('ix_user_organizations_is_active'), 'user_organizations', ['is_active'], unique=False)
op.create_table('user_sessions',
sa.Column('user_id', sa.UUID(), nullable=False),
sa.Column('refresh_token_jti', sa.String(length=255), nullable=False),
sa.Column('device_name', sa.String(length=255), nullable=True),
sa.Column('device_id', sa.String(length=255), nullable=True),
sa.Column('ip_address', sa.String(length=45), nullable=True),
sa.Column('user_agent', sa.String(length=500), nullable=True),
sa.Column('last_used_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('location_city', sa.String(length=100), nullable=True),
sa.Column('location_country', sa.String(length=100), nullable=True),
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
) )
op.create_index(op.f('ix_user_sessions_is_active'), 'user_sessions', ['is_active'], unique=False) op.create_index(
op.create_index('ix_user_sessions_jti_active', 'user_sessions', ['refresh_token_jti', 'is_active'], unique=False) "ix_organizations_slug_active",
op.create_index(op.f('ix_user_sessions_refresh_token_jti'), 'user_sessions', ['refresh_token_jti'], unique=True) "organizations",
op.create_index('ix_user_sessions_user_active', 'user_sessions', ['user_id', 'is_active'], unique=False) ["slug", "is_active"],
op.create_index(op.f('ix_user_sessions_user_id'), 'user_sessions', ['user_id'], unique=False) unique=False,
op.create_table('oauth_authorization_codes',
sa.Column('code', sa.String(length=128), nullable=False),
sa.Column('client_id', sa.String(length=64), nullable=False),
sa.Column('user_id', sa.UUID(), nullable=False),
sa.Column('redirect_uri', sa.String(length=2048), nullable=False),
sa.Column('scope', sa.String(length=1000), nullable=False),
sa.Column('code_challenge', sa.String(length=128), nullable=True),
sa.Column('code_challenge_method', sa.String(length=10), nullable=True),
sa.Column('state', sa.String(length=256), nullable=True),
sa.Column('nonce', sa.String(length=256), nullable=True),
sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('used', sa.Boolean(), nullable=False),
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['client_id'], ['oauth_clients.client_id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
) )
op.create_index('ix_oauth_authorization_codes_client_user', 'oauth_authorization_codes', ['client_id', 'user_id'], unique=False) op.create_table(
op.create_index(op.f('ix_oauth_authorization_codes_code'), 'oauth_authorization_codes', ['code'], unique=True) "users",
op.create_index('ix_oauth_authorization_codes_expires_at', 'oauth_authorization_codes', ['expires_at'], unique=False) sa.Column("email", sa.String(length=255), nullable=False),
op.create_table('oauth_consents', sa.Column("password_hash", sa.String(length=255), nullable=True),
sa.Column('user_id', sa.UUID(), nullable=False), sa.Column("first_name", sa.String(length=100), nullable=False),
sa.Column('client_id', sa.String(length=64), nullable=False), sa.Column("last_name", sa.String(length=100), nullable=True),
sa.Column('granted_scopes', sa.String(length=1000), nullable=False), sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column('id', sa.UUID(), nullable=False), sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False), sa.Column("is_superuser", sa.Boolean(), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False), sa.Column(
sa.ForeignKeyConstraint(['client_id'], ['oauth_clients.client_id'], ondelete='CASCADE'), "preferences", postgresql.JSONB(astext_type=sa.Text()), nullable=True
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'), ),
sa.PrimaryKeyConstraint('id') sa.Column("locale", sa.String(length=10), nullable=True),
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("id", sa.UUID(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint("id"),
) )
op.create_index('ix_oauth_consents_user_client', 'oauth_consents', ['user_id', 'client_id'], unique=True) op.create_index(op.f("ix_users_deleted_at"), "users", ["deleted_at"], unique=False)
op.create_table('oauth_provider_refresh_tokens', op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
sa.Column('token_hash', sa.String(length=64), nullable=False), op.create_index(op.f("ix_users_is_active"), "users", ["is_active"], unique=False)
sa.Column('jti', sa.String(length=64), nullable=False), op.create_index(
sa.Column('client_id', sa.String(length=64), nullable=False), op.f("ix_users_is_superuser"), "users", ["is_superuser"], unique=False
sa.Column('user_id', sa.UUID(), nullable=False), )
sa.Column('scope', sa.String(length=1000), nullable=False), op.create_index(op.f("ix_users_locale"), "users", ["locale"], unique=False)
sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False), op.create_table(
sa.Column('revoked', sa.Boolean(), nullable=False), "oauth_accounts",
sa.Column('last_used_at', sa.DateTime(timezone=True), nullable=True), sa.Column("user_id", sa.UUID(), nullable=False),
sa.Column('device_info', sa.String(length=500), nullable=True), sa.Column("provider", sa.String(length=50), nullable=False),
sa.Column('ip_address', sa.String(length=45), nullable=True), sa.Column("provider_user_id", sa.String(length=255), nullable=False),
sa.Column('id', sa.UUID(), nullable=False), sa.Column("provider_email", sa.String(length=255), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False), sa.Column("access_token_encrypted", sa.String(length=2048), nullable=True),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False), sa.Column("refresh_token_encrypted", sa.String(length=2048), nullable=True),
sa.ForeignKeyConstraint(['client_id'], ['oauth_clients.client_id'], ondelete='CASCADE'), sa.Column("token_expires_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'), sa.Column("id", sa.UUID(), nullable=False),
sa.PrimaryKeyConstraint('id') sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint(
"provider", "provider_user_id", name="uq_oauth_provider_user"
),
)
op.create_index(
op.f("ix_oauth_accounts_provider"), "oauth_accounts", ["provider"], unique=False
)
op.create_index(
op.f("ix_oauth_accounts_provider_email"),
"oauth_accounts",
["provider_email"],
unique=False,
)
op.create_index(
op.f("ix_oauth_accounts_user_id"), "oauth_accounts", ["user_id"], unique=False
)
op.create_index(
"ix_oauth_accounts_user_provider",
"oauth_accounts",
["user_id", "provider"],
unique=False,
)
op.create_table(
"oauth_clients",
sa.Column("client_id", sa.String(length=64), nullable=False),
sa.Column("client_secret_hash", sa.String(length=255), nullable=True),
sa.Column("client_name", sa.String(length=255), nullable=False),
sa.Column("client_description", sa.String(length=1000), nullable=True),
sa.Column("client_type", sa.String(length=20), nullable=False),
sa.Column(
"redirect_uris", postgresql.JSONB(astext_type=sa.Text()), nullable=False
),
sa.Column(
"allowed_scopes", postgresql.JSONB(astext_type=sa.Text()), nullable=False
),
sa.Column("access_token_lifetime", sa.String(length=10), nullable=False),
sa.Column("refresh_token_lifetime", sa.String(length=10), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("owner_user_id", sa.UUID(), nullable=True),
sa.Column("mcp_server_url", sa.String(length=2048), nullable=True),
sa.Column("id", sa.UUID(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["owner_user_id"], ["users.id"], ondelete="SET NULL"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_oauth_clients_client_id"), "oauth_clients", ["client_id"], unique=True
)
op.create_index(
op.f("ix_oauth_clients_is_active"), "oauth_clients", ["is_active"], unique=False
)
op.create_table(
"user_organizations",
sa.Column("user_id", sa.UUID(), nullable=False),
sa.Column("organization_id", sa.UUID(), nullable=False),
sa.Column(
"role",
sa.Enum("OWNER", "ADMIN", "MEMBER", "GUEST", name="organizationrole"),
nullable=False,
),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("custom_permissions", sa.String(length=500), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(
["organization_id"], ["organizations.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("user_id", "organization_id"),
)
op.create_index(
"ix_user_org_org_active",
"user_organizations",
["organization_id", "is_active"],
unique=False,
)
op.create_index("ix_user_org_role", "user_organizations", ["role"], unique=False)
op.create_index(
"ix_user_org_user_active",
"user_organizations",
["user_id", "is_active"],
unique=False,
)
op.create_index(
op.f("ix_user_organizations_is_active"),
"user_organizations",
["is_active"],
unique=False,
)
op.create_table(
"user_sessions",
sa.Column("user_id", sa.UUID(), nullable=False),
sa.Column("refresh_token_jti", sa.String(length=255), nullable=False),
sa.Column("device_name", sa.String(length=255), nullable=True),
sa.Column("device_id", sa.String(length=255), nullable=True),
sa.Column("ip_address", sa.String(length=45), nullable=True),
sa.Column("user_agent", sa.String(length=500), nullable=True),
sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("location_city", sa.String(length=100), nullable=True),
sa.Column("location_country", sa.String(length=100), nullable=True),
sa.Column("id", sa.UUID(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_user_sessions_is_active"), "user_sessions", ["is_active"], unique=False
)
op.create_index(
"ix_user_sessions_jti_active",
"user_sessions",
["refresh_token_jti", "is_active"],
unique=False,
)
op.create_index(
op.f("ix_user_sessions_refresh_token_jti"),
"user_sessions",
["refresh_token_jti"],
unique=True,
)
op.create_index(
"ix_user_sessions_user_active",
"user_sessions",
["user_id", "is_active"],
unique=False,
)
op.create_index(
op.f("ix_user_sessions_user_id"), "user_sessions", ["user_id"], unique=False
)
op.create_table(
"oauth_authorization_codes",
sa.Column("code", sa.String(length=128), nullable=False),
sa.Column("client_id", sa.String(length=64), nullable=False),
sa.Column("user_id", sa.UUID(), nullable=False),
sa.Column("redirect_uri", sa.String(length=2048), nullable=False),
sa.Column("scope", sa.String(length=1000), nullable=False),
sa.Column("code_challenge", sa.String(length=128), nullable=True),
sa.Column("code_challenge_method", sa.String(length=10), nullable=True),
sa.Column("state", sa.String(length=256), nullable=True),
sa.Column("nonce", sa.String(length=256), nullable=True),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("used", sa.Boolean(), nullable=False),
sa.Column("id", sa.UUID(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(
["client_id"], ["oauth_clients.client_id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_oauth_authorization_codes_client_user",
"oauth_authorization_codes",
["client_id", "user_id"],
unique=False,
)
op.create_index(
op.f("ix_oauth_authorization_codes_code"),
"oauth_authorization_codes",
["code"],
unique=True,
)
op.create_index(
"ix_oauth_authorization_codes_expires_at",
"oauth_authorization_codes",
["expires_at"],
unique=False,
)
op.create_table(
"oauth_consents",
sa.Column("user_id", sa.UUID(), nullable=False),
sa.Column("client_id", sa.String(length=64), nullable=False),
sa.Column("granted_scopes", sa.String(length=1000), nullable=False),
sa.Column("id", sa.UUID(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(
["client_id"], ["oauth_clients.client_id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_oauth_consents_user_client",
"oauth_consents",
["user_id", "client_id"],
unique=True,
)
op.create_table(
"oauth_provider_refresh_tokens",
sa.Column("token_hash", sa.String(length=64), nullable=False),
sa.Column("jti", sa.String(length=64), nullable=False),
sa.Column("client_id", sa.String(length=64), nullable=False),
sa.Column("user_id", sa.UUID(), nullable=False),
sa.Column("scope", sa.String(length=1000), nullable=False),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("revoked", sa.Boolean(), nullable=False),
sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("device_info", sa.String(length=500), nullable=True),
sa.Column("ip_address", sa.String(length=45), nullable=True),
sa.Column("id", sa.UUID(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(
["client_id"], ["oauth_clients.client_id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_oauth_provider_refresh_tokens_client_user",
"oauth_provider_refresh_tokens",
["client_id", "user_id"],
unique=False,
)
op.create_index(
"ix_oauth_provider_refresh_tokens_expires_at",
"oauth_provider_refresh_tokens",
["expires_at"],
unique=False,
)
op.create_index(
op.f("ix_oauth_provider_refresh_tokens_jti"),
"oauth_provider_refresh_tokens",
["jti"],
unique=True,
)
op.create_index(
op.f("ix_oauth_provider_refresh_tokens_revoked"),
"oauth_provider_refresh_tokens",
["revoked"],
unique=False,
)
op.create_index(
op.f("ix_oauth_provider_refresh_tokens_token_hash"),
"oauth_provider_refresh_tokens",
["token_hash"],
unique=True,
)
op.create_index(
"ix_oauth_provider_refresh_tokens_user_revoked",
"oauth_provider_refresh_tokens",
["user_id", "revoked"],
unique=False,
) )
op.create_index('ix_oauth_provider_refresh_tokens_client_user', 'oauth_provider_refresh_tokens', ['client_id', 'user_id'], unique=False)
op.create_index('ix_oauth_provider_refresh_tokens_expires_at', 'oauth_provider_refresh_tokens', ['expires_at'], unique=False)
op.create_index(op.f('ix_oauth_provider_refresh_tokens_jti'), 'oauth_provider_refresh_tokens', ['jti'], unique=True)
op.create_index(op.f('ix_oauth_provider_refresh_tokens_revoked'), 'oauth_provider_refresh_tokens', ['revoked'], unique=False)
op.create_index(op.f('ix_oauth_provider_refresh_tokens_token_hash'), 'oauth_provider_refresh_tokens', ['token_hash'], unique=True)
op.create_index('ix_oauth_provider_refresh_tokens_user_revoked', 'oauth_provider_refresh_tokens', ['user_id', 'revoked'], unique=False)
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.drop_index('ix_oauth_provider_refresh_tokens_user_revoked', table_name='oauth_provider_refresh_tokens') op.drop_index(
op.drop_index(op.f('ix_oauth_provider_refresh_tokens_token_hash'), table_name='oauth_provider_refresh_tokens') "ix_oauth_provider_refresh_tokens_user_revoked",
op.drop_index(op.f('ix_oauth_provider_refresh_tokens_revoked'), table_name='oauth_provider_refresh_tokens') table_name="oauth_provider_refresh_tokens",
op.drop_index(op.f('ix_oauth_provider_refresh_tokens_jti'), table_name='oauth_provider_refresh_tokens') )
op.drop_index('ix_oauth_provider_refresh_tokens_expires_at', table_name='oauth_provider_refresh_tokens') op.drop_index(
op.drop_index('ix_oauth_provider_refresh_tokens_client_user', table_name='oauth_provider_refresh_tokens') op.f("ix_oauth_provider_refresh_tokens_token_hash"),
op.drop_table('oauth_provider_refresh_tokens') table_name="oauth_provider_refresh_tokens",
op.drop_index('ix_oauth_consents_user_client', table_name='oauth_consents') )
op.drop_table('oauth_consents') op.drop_index(
op.drop_index('ix_oauth_authorization_codes_expires_at', table_name='oauth_authorization_codes') op.f("ix_oauth_provider_refresh_tokens_revoked"),
op.drop_index(op.f('ix_oauth_authorization_codes_code'), table_name='oauth_authorization_codes') table_name="oauth_provider_refresh_tokens",
op.drop_index('ix_oauth_authorization_codes_client_user', table_name='oauth_authorization_codes') )
op.drop_table('oauth_authorization_codes') op.drop_index(
op.drop_index(op.f('ix_user_sessions_user_id'), table_name='user_sessions') op.f("ix_oauth_provider_refresh_tokens_jti"),
op.drop_index('ix_user_sessions_user_active', table_name='user_sessions') table_name="oauth_provider_refresh_tokens",
op.drop_index(op.f('ix_user_sessions_refresh_token_jti'), table_name='user_sessions') )
op.drop_index('ix_user_sessions_jti_active', table_name='user_sessions') op.drop_index(
op.drop_index(op.f('ix_user_sessions_is_active'), table_name='user_sessions') "ix_oauth_provider_refresh_tokens_expires_at",
op.drop_table('user_sessions') table_name="oauth_provider_refresh_tokens",
op.drop_index(op.f('ix_user_organizations_is_active'), table_name='user_organizations') )
op.drop_index('ix_user_org_user_active', table_name='user_organizations') op.drop_index(
op.drop_index('ix_user_org_role', table_name='user_organizations') "ix_oauth_provider_refresh_tokens_client_user",
op.drop_index('ix_user_org_org_active', table_name='user_organizations') table_name="oauth_provider_refresh_tokens",
op.drop_table('user_organizations') )
op.drop_index(op.f('ix_oauth_clients_is_active'), table_name='oauth_clients') op.drop_table("oauth_provider_refresh_tokens")
op.drop_index(op.f('ix_oauth_clients_client_id'), table_name='oauth_clients') op.drop_index("ix_oauth_consents_user_client", table_name="oauth_consents")
op.drop_table('oauth_clients') op.drop_table("oauth_consents")
op.drop_index('ix_oauth_accounts_user_provider', table_name='oauth_accounts') op.drop_index(
op.drop_index(op.f('ix_oauth_accounts_user_id'), table_name='oauth_accounts') "ix_oauth_authorization_codes_expires_at",
op.drop_index(op.f('ix_oauth_accounts_provider_email'), table_name='oauth_accounts') table_name="oauth_authorization_codes",
op.drop_index(op.f('ix_oauth_accounts_provider'), table_name='oauth_accounts') )
op.drop_table('oauth_accounts') op.drop_index(
op.drop_index(op.f('ix_users_locale'), table_name='users') op.f("ix_oauth_authorization_codes_code"),
op.drop_index(op.f('ix_users_is_superuser'), table_name='users') table_name="oauth_authorization_codes",
op.drop_index(op.f('ix_users_is_active'), table_name='users') )
op.drop_index(op.f('ix_users_email'), table_name='users') op.drop_index(
op.drop_index(op.f('ix_users_deleted_at'), table_name='users') "ix_oauth_authorization_codes_client_user",
op.drop_table('users') table_name="oauth_authorization_codes",
op.drop_index('ix_organizations_slug_active', table_name='organizations') )
op.drop_index(op.f('ix_organizations_slug'), table_name='organizations') op.drop_table("oauth_authorization_codes")
op.drop_index('ix_organizations_name_active', table_name='organizations') op.drop_index(op.f("ix_user_sessions_user_id"), table_name="user_sessions")
op.drop_index(op.f('ix_organizations_name'), table_name='organizations') op.drop_index("ix_user_sessions_user_active", table_name="user_sessions")
op.drop_index(op.f('ix_organizations_is_active'), table_name='organizations') op.drop_index(
op.drop_table('organizations') op.f("ix_user_sessions_refresh_token_jti"), table_name="user_sessions"
op.drop_index(op.f('ix_oauth_states_state'), table_name='oauth_states') )
op.drop_table('oauth_states') op.drop_index("ix_user_sessions_jti_active", table_name="user_sessions")
op.drop_index(op.f("ix_user_sessions_is_active"), table_name="user_sessions")
op.drop_table("user_sessions")
op.drop_index(
op.f("ix_user_organizations_is_active"), table_name="user_organizations"
)
op.drop_index("ix_user_org_user_active", table_name="user_organizations")
op.drop_index("ix_user_org_role", table_name="user_organizations")
op.drop_index("ix_user_org_org_active", table_name="user_organizations")
op.drop_table("user_organizations")
op.drop_index(op.f("ix_oauth_clients_is_active"), table_name="oauth_clients")
op.drop_index(op.f("ix_oauth_clients_client_id"), table_name="oauth_clients")
op.drop_table("oauth_clients")
op.drop_index("ix_oauth_accounts_user_provider", table_name="oauth_accounts")
op.drop_index(op.f("ix_oauth_accounts_user_id"), table_name="oauth_accounts")
op.drop_index(op.f("ix_oauth_accounts_provider_email"), table_name="oauth_accounts")
op.drop_index(op.f("ix_oauth_accounts_provider"), table_name="oauth_accounts")
op.drop_table("oauth_accounts")
op.drop_index(op.f("ix_users_locale"), table_name="users")
op.drop_index(op.f("ix_users_is_superuser"), table_name="users")
op.drop_index(op.f("ix_users_is_active"), table_name="users")
op.drop_index(op.f("ix_users_email"), table_name="users")
op.drop_index(op.f("ix_users_deleted_at"), table_name="users")
op.drop_table("users")
op.drop_index("ix_organizations_slug_active", table_name="organizations")
op.drop_index(op.f("ix_organizations_slug"), table_name="organizations")
op.drop_index("ix_organizations_name_active", table_name="organizations")
op.drop_index(op.f("ix_organizations_name"), table_name="organizations")
op.drop_index(op.f("ix_organizations_is_active"), table_name="organizations")
op.drop_table("organizations")
op.drop_index(op.f("ix_oauth_states_state"), table_name="oauth_states")
op.drop_table("oauth_states")
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@@ -114,8 +114,13 @@ def upgrade() -> None:
def downgrade() -> None: def downgrade() -> None:
# Drop indexes in reverse order # Drop indexes in reverse order
op.drop_index("ix_perf_oauth_auth_codes_expires", table_name="oauth_authorization_codes") op.drop_index(
op.drop_index("ix_perf_oauth_refresh_tokens_expires", table_name="oauth_provider_refresh_tokens") "ix_perf_oauth_auth_codes_expires", table_name="oauth_authorization_codes"
)
op.drop_index(
"ix_perf_oauth_refresh_tokens_expires",
table_name="oauth_provider_refresh_tokens",
)
op.drop_index("ix_perf_user_sessions_expires", table_name="user_sessions") op.drop_index("ix_perf_user_sessions_expires", table_name="user_sessions")
op.drop_index("ix_perf_organizations_slug_lower", table_name="organizations") op.drop_index("ix_perf_organizations_slug_lower", table_name="organizations")
op.drop_index("ix_perf_users_active", table_name="users") op.drop_index("ix_perf_users_active", table_name="users")

View File

@@ -8,7 +8,6 @@ Create Date: 2026-02-27 01:03:18.869178
from collections.abc import Sequence from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@@ -19,10 +18,18 @@ depends_on: str | Sequence[str] | None = None
def upgrade() -> None: def upgrade() -> None:
op.alter_column("oauth_accounts", "access_token_encrypted", new_column_name="access_token") op.alter_column(
op.alter_column("oauth_accounts", "refresh_token_encrypted", new_column_name="refresh_token") "oauth_accounts", "access_token_encrypted", new_column_name="access_token"
)
op.alter_column(
"oauth_accounts", "refresh_token_encrypted", new_column_name="refresh_token"
)
def downgrade() -> None: def downgrade() -> None:
op.alter_column("oauth_accounts", "access_token", new_column_name="access_token_encrypted") op.alter_column(
op.alter_column("oauth_accounts", "refresh_token", new_column_name="refresh_token_encrypted") "oauth_accounts", "access_token", new_column_name="access_token_encrypted"
)
op.alter_column(
"oauth_accounts", "refresh_token", new_column_name="refresh_token_encrypted"
)

View File

@@ -27,9 +27,6 @@ from app.core.exceptions import (
from app.core.repository_exceptions import DuplicateEntryError from app.core.repository_exceptions import DuplicateEntryError
from app.models.user import User from app.models.user import User
from app.models.user_organization import OrganizationRole from app.models.user_organization import OrganizationRole
from app.services.organization_service import organization_service
from app.services.session_service import session_service
from app.services.user_service import user_service
from app.schemas.common import ( from app.schemas.common import (
MessageResponse, MessageResponse,
PaginatedResponse, PaginatedResponse,
@@ -45,6 +42,9 @@ from app.schemas.organizations import (
) )
from app.schemas.sessions import AdminSessionResponse from app.schemas.sessions import AdminSessionResponse
from app.schemas.users import UserCreate, UserResponse, UserUpdate from app.schemas.users import UserCreate, UserResponse, UserUpdate
from app.services.organization_service import organization_service
from app.services.session_service import session_service
from app.services.user_service import user_service
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -429,9 +429,7 @@ async def confirm_password_reset(
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail=err_msg status_code=status.HTTP_400_BAD_REQUEST, detail=err_msg
) )
raise HTTPException( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=err_msg)
status_code=status.HTTP_404_NOT_FOUND, detail=err_msg
)
# SECURITY: Invalidate all existing sessions after password reset # SECURITY: Invalidate all existing sessions after password reset
# This prevents stolen sessions from being used after password change # This prevents stolen sessions from being used after password change

View File

@@ -25,7 +25,6 @@ from app.core.auth import decode_token
from app.core.config import settings from app.core.config import settings
from app.core.database import get_db from app.core.database import get_db
from app.core.exceptions import AuthenticationError as AuthError from app.core.exceptions import AuthenticationError as AuthError
from app.services.session_service import session_service
from app.models.user import User from app.models.user import User
from app.schemas.oauth import ( from app.schemas.oauth import (
OAuthAccountsListResponse, OAuthAccountsListResponse,
@@ -37,6 +36,7 @@ from app.schemas.oauth import (
from app.schemas.sessions import SessionCreate from app.schemas.sessions import SessionCreate
from app.schemas.users import Token from app.schemas.users import Token
from app.services.oauth_service import OAuthService from app.services.oauth_service import OAuthService
from app.services.session_service import session_service
from app.utils.device import extract_device_info from app.utils.device import extract_device_info
router = APIRouter() router = APIRouter()

View File

@@ -16,7 +16,6 @@ from app.api.dependencies.auth import get_current_user
from app.api.dependencies.permissions import require_org_admin, require_org_membership from app.api.dependencies.permissions import require_org_admin, require_org_membership
from app.core.database import get_db from app.core.database import get_db
from app.models.user import User from app.models.user import User
from app.services.organization_service import organization_service
from app.schemas.common import ( from app.schemas.common import (
PaginatedResponse, PaginatedResponse,
PaginationParams, PaginationParams,
@@ -27,6 +26,7 @@ from app.schemas.organizations import (
OrganizationResponse, OrganizationResponse,
OrganizationUpdate, OrganizationUpdate,
) )
from app.services.organization_service import organization_service
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -18,9 +18,9 @@ from app.core.auth import decode_token
from app.core.database import get_db from app.core.database import get_db
from app.core.exceptions import AuthorizationError, ErrorCode, NotFoundError from app.core.exceptions import AuthorizationError, ErrorCode, NotFoundError
from app.models.user import User from app.models.user import User
from app.services.session_service import session_service
from app.schemas.common import MessageResponse from app.schemas.common import MessageResponse
from app.schemas.sessions import SessionListResponse, SessionResponse from app.schemas.sessions import SessionListResponse, SessionResponse
from app.services.session_service import session_service
router = APIRouter() router = APIRouter()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -13,7 +13,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.api.dependencies.auth import get_current_superuser, get_current_user from app.api.dependencies.auth import get_current_superuser, get_current_user
from app.core.database import get_db from app.core.database import get_db
from app.core.exceptions import AuthorizationError, ErrorCode, NotFoundError from app.core.exceptions import AuthorizationError, ErrorCode
from app.models.user import User from app.models.user import User
from app.schemas.common import ( from app.schemas.common import (
MessageResponse, MessageResponse,
@@ -107,7 +107,9 @@ async def list_users(
""", """,
operation_id="get_current_user_profile", operation_id="get_current_user_profile",
) )
async def get_current_user_profile(current_user: User = Depends(get_current_user)) -> Any: async def get_current_user_profile(
current_user: User = Depends(get_current_user),
) -> Any:
"""Get current user's profile.""" """Get current user's profile."""
return current_user return current_user

View File

@@ -16,10 +16,10 @@ from sqlalchemy import select, text
from app.core.config import settings from app.core.config import settings
from app.core.database import SessionLocal, engine from app.core.database import SessionLocal, engine
from app.repositories.user import user_repo as user_crud
from app.models.organization import Organization from app.models.organization import Organization
from app.models.user import User from app.models.user import User
from app.models.user_organization import UserOrganization from app.models.user_organization import UserOrganization
from app.repositories.user import user_repo as user_crud
from app.schemas.users import UserCreate from app.schemas.users import UserCreate
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -18,22 +18,22 @@ from app.repositories.session import SessionRepository, session_repo
from app.repositories.user import UserRepository, user_repo from app.repositories.user import UserRepository, user_repo
__all__ = [ __all__ = [
"UserRepository",
"user_repo",
"OrganizationRepository",
"organization_repo",
"SessionRepository",
"session_repo",
"OAuthAccountRepository", "OAuthAccountRepository",
"oauth_account_repo",
"OAuthAuthorizationCodeRepository", "OAuthAuthorizationCodeRepository",
"oauth_authorization_code_repo",
"OAuthClientRepository", "OAuthClientRepository",
"oauth_client_repo",
"OAuthConsentRepository", "OAuthConsentRepository",
"oauth_consent_repo",
"OAuthProviderTokenRepository", "OAuthProviderTokenRepository",
"oauth_provider_token_repo",
"OAuthStateRepository", "OAuthStateRepository",
"OrganizationRepository",
"SessionRepository",
"UserRepository",
"oauth_account_repo",
"oauth_authorization_code_repo",
"oauth_client_repo",
"oauth_consent_repo",
"oauth_provider_token_repo",
"oauth_state_repo", "oauth_state_repo",
"organization_repo",
"session_repo",
"user_repo",
] ]

View File

@@ -411,4 +411,3 @@ class BaseRepository[
exc_info=True, exc_info=True,
) )
raise raise

View File

@@ -23,7 +23,9 @@ class EmptySchema(BaseModel):
"""Placeholder schema for repository operations that don't need update schemas.""" """Placeholder schema for repository operations that don't need update schemas."""
class OAuthAccountRepository(BaseRepository[OAuthAccount, OAuthAccountCreate, EmptySchema]): class OAuthAccountRepository(
BaseRepository[OAuthAccount, OAuthAccountCreate, EmptySchema]
):
"""Repository for OAuth account links.""" """Repository for OAuth account links."""
async def get_by_provider_id( async def get_by_provider_id(

View File

@@ -22,7 +22,9 @@ class EmptySchema(BaseModel):
"""Placeholder schema for repository operations that don't need update schemas.""" """Placeholder schema for repository operations that don't need update schemas."""
class OAuthClientRepository(BaseRepository[OAuthClient, OAuthClientCreate, EmptySchema]): class OAuthClientRepository(
BaseRepository[OAuthClient, OAuthClientCreate, EmptySchema]
):
"""Repository for OAuth clients (provider mode).""" """Repository for OAuth clients (provider mode)."""
async def get_by_client_id( async def get_by_client_id(

View File

@@ -2,9 +2,8 @@
"""Repository for OAuthConsent model.""" """Repository for OAuthConsent model."""
import logging import logging
from uuid import UUID
from typing import Any from typing import Any
from uuid import UUID
from sqlalchemy import and_, delete, select from sqlalchemy import and_, delete, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -49,7 +48,9 @@ class OAuthConsentRepository:
consent = await self.get_consent(db, user_id=user_id, client_id=client_id) consent = await self.get_consent(db, user_id=user_id, client_id=client_id)
if consent: if consent:
existing = set(consent.granted_scopes.split()) if consent.granted_scopes else set() existing = (
set(consent.granted_scopes.split()) if consent.granted_scopes else set()
)
merged = existing | set(scopes) merged = existing | set(scopes)
consent.granted_scopes = " ".join(sorted(merged)) # type: ignore[assignment] consent.granted_scopes = " ".join(sorted(merged)) # type: ignore[assignment]
else: else:

View File

@@ -99,9 +99,7 @@ class OAuthProviderTokenRepository:
await db.commit() await db.commit()
return count return count
async def revoke_all_for_user( async def revoke_all_for_user(self, db: AsyncSession, *, user_id: UUID) -> int:
self, db: AsyncSession, *, user_id: UUID
) -> int:
""" """
Revoke all active tokens for a user across all clients. Revoke all active tokens for a user across all clients.
@@ -123,9 +121,7 @@ class OAuthProviderTokenRepository:
await db.commit() await db.commit()
return count return count
async def cleanup_expired( async def cleanup_expired(self, db: AsyncSession, *, cutoff_days: int = 7) -> int:
self, db: AsyncSession, *, cutoff_days: int = 7
) -> int:
""" """
Delete expired refresh tokens older than cutoff_days. Delete expired refresh tokens older than cutoff_days.

View File

@@ -22,7 +22,9 @@ from app.schemas.organizations import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class OrganizationRepository(BaseRepository[Organization, OrganizationCreate, OrganizationUpdate]): class OrganizationRepository(
BaseRepository[Organization, OrganizationCreate, OrganizationUpdate]
):
"""Repository for Organization model.""" """Repository for Organization model."""
async def get_by_slug(self, db: AsyncSession, *, slug: str) -> Organization | None: async def get_by_slug(self, db: AsyncSession, *, slug: str) -> Organization | None:
@@ -55,7 +57,11 @@ class OrganizationRepository(BaseRepository[Organization, OrganizationCreate, Or
except IntegrityError as e: except IntegrityError as e:
await db.rollback() await db.rollback()
error_msg = str(e.orig) if hasattr(e, "orig") else str(e) error_msg = str(e.orig) if hasattr(e, "orig") else str(e)
if "slug" in error_msg.lower() or "unique" in error_msg.lower() or "duplicate" in error_msg.lower(): if (
"slug" in error_msg.lower()
or "unique" in error_msg.lower()
or "duplicate" in error_msg.lower()
):
logger.warning(f"Duplicate slug attempted: {obj_in.slug}") logger.warning(f"Duplicate slug attempted: {obj_in.slug}")
raise DuplicateEntryError( raise DuplicateEntryError(
f"Organization with slug '{obj_in.slug}' already exists" f"Organization with slug '{obj_in.slug}' already exists"
@@ -235,7 +241,9 @@ class OrganizationRepository(BaseRepository[Organization, OrganizationCreate, Or
await db.refresh(existing) await db.refresh(existing)
return existing return existing
else: else:
raise DuplicateEntryError("User is already a member of this organization") raise DuplicateEntryError(
"User is already a member of this organization"
)
user_org = UserOrganization( user_org = UserOrganization(
user_id=user_id, user_id=user_id,

View File

@@ -10,7 +10,7 @@ from sqlalchemy import and_, delete, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from app.core.repository_exceptions import InvalidInputError, IntegrityConstraintError from app.core.repository_exceptions import IntegrityConstraintError, InvalidInputError
from app.models.user_session import UserSession from app.models.user_session import UserSession
from app.repositories.base import BaseRepository from app.repositories.base import BaseRepository
from app.schemas.sessions import SessionCreate, SessionUpdate from app.schemas.sessions import SessionCreate, SessionUpdate

View File

@@ -58,7 +58,9 @@ class UserRepository(BaseRepository[User, UserCreate, UserUpdate]):
error_msg = str(e.orig) if hasattr(e, "orig") else str(e) error_msg = str(e.orig) if hasattr(e, "orig") else str(e)
if "email" in error_msg.lower(): if "email" in error_msg.lower():
logger.warning(f"Duplicate email attempted: {obj_in.email}") logger.warning(f"Duplicate email attempted: {obj_in.email}")
raise DuplicateEntryError(f"User with email {obj_in.email} already exists") raise DuplicateEntryError(
f"User with email {obj_in.email} already exists"
)
logger.error(f"Integrity error creating user: {error_msg}") logger.error(f"Integrity error creating user: {error_msg}")
raise DuplicateEntryError(f"Database integrity error: {error_msg}") raise DuplicateEntryError(f"Database integrity error: {error_msg}")
except Exception as e: except Exception as e:

View File

@@ -9,11 +9,11 @@ from .user_service import UserService, user_service
__all__ = [ __all__ = [
"AuthService", "AuthService",
"OAuthService", "OAuthService",
"UserService",
"OrganizationService", "OrganizationService",
"SessionService", "SessionService",
"UserService",
"oauth_provider_service", "oauth_provider_service",
"user_service",
"organization_service", "organization_service",
"session_service", "session_service",
"user_service",
] ]

View File

@@ -30,13 +30,13 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings from app.core.config import settings
from app.models.oauth_client import OAuthClient from app.models.oauth_client import OAuthClient
from app.schemas.oauth import OAuthClientCreate
from app.models.user import User from app.models.user import User
from app.repositories.oauth_authorization_code import oauth_authorization_code_repo from app.repositories.oauth_authorization_code import oauth_authorization_code_repo
from app.repositories.oauth_client import oauth_client_repo from app.repositories.oauth_client import oauth_client_repo
from app.repositories.oauth_consent import oauth_consent_repo from app.repositories.oauth_consent import oauth_consent_repo
from app.repositories.oauth_provider_token import oauth_provider_token_repo from app.repositories.oauth_provider_token import oauth_provider_token_repo
from app.repositories.user import user_repo from app.repositories.user import user_repo
from app.schemas.oauth import OAuthClientCreate
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -691,9 +691,7 @@ async def revoke_token(
jti = payload.get("jti") jti = payload.get("jti")
if jti: if jti:
# Find and revoke the associated refresh token # Find and revoke the associated refresh token
refresh_record = await oauth_provider_token_repo.get_by_jti( refresh_record = await oauth_provider_token_repo.get_by_jti(db, jti=jti)
db, jti=jti
)
if refresh_record: if refresh_record:
if client_id and refresh_record.client_id != client_id: if client_id and refresh_record.client_id != client_id:
raise InvalidClientError("Token was not issued to this client") raise InvalidClientError("Token was not issued to this client")
@@ -807,9 +805,7 @@ async def introspect_token(
# Check if associated refresh token is revoked # Check if associated refresh token is revoked
jti = payload.get("jti") jti = payload.get("jti")
if jti: if jti:
refresh_record = await oauth_provider_token_repo.get_by_jti( refresh_record = await oauth_provider_token_repo.get_by_jti(db, jti=jti)
db, jti=jti
)
if refresh_record and refresh_record.revoked: if refresh_record and refresh_record.revoked:
return {"active": False} return {"active": False}
@@ -862,7 +858,9 @@ async def get_consent(
client_id: str, client_id: str,
): ):
"""Get existing consent record for user-client pair.""" """Get existing consent record for user-client pair."""
return await oauth_consent_repo.get_consent(db, user_id=user_id, client_id=client_id) return await oauth_consent_repo.get_consent(
db, user_id=user_id, client_id=client_id
)
async def check_consent( async def check_consent(

View File

@@ -24,9 +24,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.core.auth import create_access_token, create_refresh_token from app.core.auth import create_access_token, create_refresh_token
from app.core.config import settings from app.core.config import settings
from app.core.exceptions import AuthenticationError from app.core.exceptions import AuthenticationError
from app.models.user import User
from app.repositories.oauth_account import oauth_account_repo as oauth_account from app.repositories.oauth_account import oauth_account_repo as oauth_account
from app.repositories.oauth_state import oauth_state_repo as oauth_state from app.repositories.oauth_state import oauth_state_repo as oauth_state
from app.models.user import User
from app.repositories.user import user_repo from app.repositories.user import user_repo
from app.schemas.oauth import ( from app.schemas.oauth import (
OAuthAccountCreate, OAuthAccountCreate,
@@ -344,7 +344,9 @@ class OAuthService:
await oauth_account.update_tokens( await oauth_account.update_tokens(
db, db,
account=existing_oauth, account=existing_oauth,
access_token=token.get("access_token"), refresh_token=token.get("refresh_token"), token_expires_at=datetime.now(UTC) access_token=token.get("access_token"),
refresh_token=token.get("refresh_token"),
token_expires_at=datetime.now(UTC)
+ timedelta(seconds=token.get("expires_in", 3600)), + timedelta(seconds=token.get("expires_in", 3600)),
) )
@@ -373,7 +375,9 @@ class OAuthService:
provider=provider, provider=provider,
provider_user_id=provider_user_id, provider_user_id=provider_user_id,
provider_email=provider_email, provider_email=provider_email,
access_token=token.get("access_token"), refresh_token=token.get("refresh_token"), token_expires_at=datetime.now(UTC) access_token=token.get("access_token"),
refresh_token=token.get("refresh_token"),
token_expires_at=datetime.now(UTC)
+ timedelta(seconds=token.get("expires_in", 3600)) + timedelta(seconds=token.get("expires_in", 3600))
if token.get("expires_in") if token.get("expires_in")
else None, else None,
@@ -639,7 +643,9 @@ class OAuthService:
provider=provider, provider=provider,
provider_user_id=provider_user_id, provider_user_id=provider_user_id,
provider_email=email, provider_email=email,
access_token=token.get("access_token"), refresh_token=token.get("refresh_token"), token_expires_at=datetime.now(UTC) access_token=token.get("access_token"),
refresh_token=token.get("refresh_token"),
token_expires_at=datetime.now(UTC)
+ timedelta(seconds=token.get("expires_in", 3600)) + timedelta(seconds=token.get("expires_in", 3600))
if token.get("expires_in") if token.get("expires_in")
else None, else None,

View File

@@ -51,9 +51,7 @@ class OrganizationService:
"""Permanently delete an organization by ID.""" """Permanently delete an organization by ID."""
await self._repo.remove(db, id=org_id) await self._repo.remove(db, id=org_id)
async def get_member_count( async def get_member_count(self, db: AsyncSession, *, organization_id: UUID) -> int:
self, db: AsyncSession, *, organization_id: UUID
) -> int:
"""Get number of active members in an organization.""" """Get number of active members in an organization."""
return await self._repo.get_member_count(db, organization_id=organization_id) return await self._repo.get_member_count(db, organization_id=organization_id)

View File

@@ -25,7 +25,9 @@ class SessionService:
"""Create a new session record.""" """Create a new session record."""
return await self._repo.create_session(db, obj_in=obj_in) return await self._repo.create_session(db, obj_in=obj_in)
async def get_session(self, db: AsyncSession, session_id: str) -> UserSession | None: async def get_session(
self, db: AsyncSession, session_id: str
) -> UserSession | None:
"""Get session by ID.""" """Get session by ID."""
return await self._repo.get(db, id=session_id) return await self._repo.get(db, id=session_id)
@@ -72,9 +74,7 @@ class SessionService:
db, session=session, new_jti=new_jti, new_expires_at=new_expires_at db, session=session, new_jti=new_jti, new_expires_at=new_expires_at
) )
async def cleanup_expired_for_user( async def cleanup_expired_for_user(self, db: AsyncSession, *, user_id: str) -> int:
self, db: AsyncSession, *, user_id: str
) -> int:
"""Remove expired sessions for a user. Returns count removed.""" """Remove expired sessions for a user. Returns count removed."""
return await self._repo.cleanup_expired_for_user(db, user_id=user_id) return await self._repo.cleanup_expired_for_user(db, user_id=user_id)

View File

@@ -96,7 +96,9 @@ class UserService:
await db.execute(select(func.count()).select_from(User)) await db.execute(select(func.count()).select_from(User))
).scalar() or 0 ).scalar() or 0
active_count = ( active_count = (
await db.execute(select(func.count()).select_from(User).where(User.is_active)) await db.execute(
select(func.count()).select_from(User).where(User.is_active)
)
).scalar() or 0 ).scalar() or 0
inactive_count = ( inactive_count = (
await db.execute( await db.execute(
@@ -104,9 +106,7 @@ class UserService:
) )
).scalar() or 0 ).scalar() or 0
all_users = list( all_users = list(
( (await db.execute(select(User).order_by(User.created_at))).scalars().all()
await db.execute(select(User).order_by(User.created_at))
).scalars().all()
) )
return { return {
"total_users": total_users, "total_users": total_users,

View File

@@ -12,8 +12,8 @@ These tests prevent real-world attack scenarios.
import pytest import pytest
from httpx import AsyncClient from httpx import AsyncClient
from app.repositories.session import session_repo as session_crud
from app.models.user import User from app.models.user import User
from app.repositories.session import session_repo as session_crud
class TestRevokedSessionSecurity: class TestRevokedSessionSecurity:

View File

@@ -11,9 +11,9 @@ These tests prevent unauthorized access and privilege escalation.
import pytest import pytest
from httpx import AsyncClient from httpx import AsyncClient
from app.repositories.user import user_repo as user_crud
from app.models.organization import Organization from app.models.organization import Organization
from app.models.user import User from app.models.user import User
from app.repositories.user import user_repo as user_crud
class TestInactiveUserBlocking: class TestInactiveUserBlocking:

View File

@@ -99,7 +99,8 @@ class TestUpdateCurrentUser:
from unittest.mock import patch from unittest.mock import patch
with patch( with patch(
"app.api.routes.users.user_service.update_user", side_effect=Exception("DB error") "app.api.routes.users.user_service.update_user",
side_effect=Exception("DB error"),
): ):
with pytest.raises(Exception): with pytest.raises(Exception):
await client.patch( await client.patch(
@@ -224,7 +225,8 @@ class TestUpdateUserById:
from unittest.mock import patch from unittest.mock import patch
with patch( with patch(
"app.api.routes.users.user_service.update_user", side_effect=ValueError("Invalid") "app.api.routes.users.user_service.update_user",
side_effect=ValueError("Invalid"),
): ):
with pytest.raises(ValueError): with pytest.raises(ValueError):
await client.patch( await client.patch(
@@ -241,7 +243,8 @@ class TestUpdateUserById:
from unittest.mock import patch from unittest.mock import patch
with patch( with patch(
"app.api.routes.users.user_service.update_user", side_effect=Exception("Unexpected") "app.api.routes.users.user_service.update_user",
side_effect=Exception("Unexpected"),
): ):
with pytest.raises(Exception): with pytest.raises(Exception):
await client.patch( await client.patch(

View File

@@ -170,7 +170,9 @@ class TestCRUDBaseCreate:
last_name="User", last_name="User",
) )
with pytest.raises(DuplicateEntryError, match="Database integrity error"): with pytest.raises(
DuplicateEntryError, match="Database integrity error"
):
await user_crud.create(session, obj_in=user_data) await user_crud.create(session, obj_in=user_data)
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -307,7 +309,9 @@ class TestCRUDBaseUpdate:
"statement", {}, Exception("constraint failed") "statement", {}, Exception("constraint failed")
), ),
): ):
with pytest.raises(IntegrityConstraintError, match="Database integrity error"): with pytest.raises(
IntegrityConstraintError, match="Database integrity error"
):
await user_crud.update( await user_crud.update(
session, db_obj=user, obj_in={"first_name": "Test"} session, db_obj=user, obj_in={"first_name": "Test"}
) )
@@ -327,7 +331,9 @@ class TestCRUDBaseUpdate:
"statement", {}, Exception("connection error") "statement", {}, Exception("connection error")
), ),
): ):
with pytest.raises(IntegrityConstraintError, match="Database operation failed"): with pytest.raises(
IntegrityConstraintError, match="Database operation failed"
):
await user_crud.update( await user_crud.update(
session, db_obj=user, obj_in={"first_name": "Test"} session, db_obj=user, obj_in={"first_name": "Test"}
) )
@@ -408,7 +414,8 @@ class TestCRUDBaseRemove:
), ),
): ):
with pytest.raises( with pytest.raises(
IntegrityConstraintError, match="Cannot delete.*referenced by other records" IntegrityConstraintError,
match="Cannot delete.*referenced by other records",
): ):
await user_crud.remove(session, id=str(async_test_user.id)) await user_crud.remove(session, id=str(async_test_user.id))
@@ -904,8 +911,8 @@ class TestCRUDBaseModelsWithoutSoftDelete:
_test_engine, SessionLocal = async_test_db _test_engine, SessionLocal = async_test_db
# Create an organization (which doesn't have deleted_at) # Create an organization (which doesn't have deleted_at)
from app.repositories.organization import organization_repo as org_crud
from app.models.organization import Organization from app.models.organization import Organization
from app.repositories.organization import organization_repo as org_crud
async with SessionLocal() as session: async with SessionLocal() as session:
org = Organization(name="Test Org", slug="test-org") org = Organization(name="Test Org", slug="test-org")
@@ -915,7 +922,9 @@ class TestCRUDBaseModelsWithoutSoftDelete:
# Try to soft delete organization (should fail) # Try to soft delete organization (should fail)
async with SessionLocal() as session: async with SessionLocal() as session:
with pytest.raises(InvalidInputError, match="does not have a deleted_at column"): with pytest.raises(
InvalidInputError, match="does not have a deleted_at column"
):
await org_crud.soft_delete(session, id=str(org_id)) await org_crud.soft_delete(session, id=str(org_id))
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -924,8 +933,8 @@ class TestCRUDBaseModelsWithoutSoftDelete:
_test_engine, SessionLocal = async_test_db _test_engine, SessionLocal = async_test_db
# Create an organization (which doesn't have deleted_at) # Create an organization (which doesn't have deleted_at)
from app.repositories.organization import organization_repo as org_crud
from app.models.organization import Organization from app.models.organization import Organization
from app.repositories.organization import organization_repo as org_crud
async with SessionLocal() as session: async with SessionLocal() as session:
org = Organization(name="Restore Test", slug="restore-test") org = Organization(name="Restore Test", slug="restore-test")
@@ -935,7 +944,9 @@ class TestCRUDBaseModelsWithoutSoftDelete:
# Try to restore organization (should fail) # Try to restore organization (should fail)
async with SessionLocal() as session: async with SessionLocal() as session:
with pytest.raises(InvalidInputError, match="does not have a deleted_at column"): with pytest.raises(
InvalidInputError, match="does not have a deleted_at column"
):
await org_crud.restore(session, id=str(org_id)) await org_crud.restore(session, id=str(org_id))
@@ -955,8 +966,8 @@ class TestCRUDBaseEagerLoadingWithRealOptions:
_test_engine, SessionLocal = async_test_db _test_engine, SessionLocal = async_test_db
# Create a session for the user # Create a session for the user
from app.repositories.session import session_repo as session_crud
from app.models.user_session import UserSession from app.models.user_session import UserSession
from app.repositories.session import session_repo as session_crud
async with SessionLocal() as session: async with SessionLocal() as session:
user_session = UserSession( user_session = UserSession(
@@ -994,8 +1005,8 @@ class TestCRUDBaseEagerLoadingWithRealOptions:
_test_engine, SessionLocal = async_test_db _test_engine, SessionLocal = async_test_db
# Create multiple sessions for the user # Create multiple sessions for the user
from app.repositories.session import session_repo as session_crud
from app.models.user_session import UserSession from app.models.user_session import UserSession
from app.repositories.session import session_repo as session_crud
async with SessionLocal() as session: async with SessionLocal() as session:
for i in range(3): for i in range(3):

View File

@@ -120,7 +120,9 @@ class TestBaseCRUDUpdateFailures:
with patch.object( with patch.object(
session, "rollback", new_callable=AsyncMock session, "rollback", new_callable=AsyncMock
) as mock_rollback: ) as mock_rollback:
with pytest.raises(IntegrityConstraintError, match="Database operation failed"): with pytest.raises(
IntegrityConstraintError, match="Database operation failed"
):
await user_crud.update( await user_crud.update(
session, db_obj=user, obj_in={"first_name": "Updated"} session, db_obj=user, obj_in={"first_name": "Updated"}
) )
@@ -142,7 +144,9 @@ class TestBaseCRUDUpdateFailures:
with patch.object( with patch.object(
session, "rollback", new_callable=AsyncMock session, "rollback", new_callable=AsyncMock
) as mock_rollback: ) as mock_rollback:
with pytest.raises(IntegrityConstraintError, match="Database operation failed"): with pytest.raises(
IntegrityConstraintError, match="Database operation failed"
):
await user_crud.update( await user_crud.update(
session, db_obj=user, obj_in={"first_name": "Updated"} session, db_obj=user, obj_in={"first_name": "Updated"}
) )

View File

@@ -63,7 +63,8 @@ class TestOAuthAccountCRUD:
# SQLite returns different error message than PostgreSQL # SQLite returns different error message than PostgreSQL
with pytest.raises( with pytest.raises(
DuplicateEntryError, match="(already linked|UNIQUE constraint failed|Failed to create)" DuplicateEntryError,
match="(already linked|UNIQUE constraint failed|Failed to create)",
): ):
await oauth_account.create_account(session, obj_in=account_data2) await oauth_account.create_account(session, obj_in=account_data2)

View File

@@ -10,9 +10,9 @@ import pytest
from sqlalchemy import select from sqlalchemy import select
from app.core.repository_exceptions import DuplicateEntryError, IntegrityConstraintError from app.core.repository_exceptions import DuplicateEntryError, IntegrityConstraintError
from app.repositories.organization import organization_repo as organization_crud
from app.models.organization import Organization from app.models.organization import Organization
from app.models.user_organization import OrganizationRole, UserOrganization from app.models.user_organization import OrganizationRole, UserOrganization
from app.repositories.organization import organization_repo as organization_crud
from app.schemas.organizations import OrganizationCreate from app.schemas.organizations import OrganizationCreate
@@ -973,7 +973,9 @@ class TestOrganizationExceptionHandlers:
with patch.object(session, "commit", side_effect=mock_commit): with patch.object(session, "commit", side_effect=mock_commit):
with patch.object(session, "rollback", new_callable=AsyncMock): with patch.object(session, "rollback", new_callable=AsyncMock):
org_in = OrganizationCreate(name="Test", slug="test") org_in = OrganizationCreate(name="Test", slug="test")
with pytest.raises(IntegrityConstraintError, match="Database integrity error"): with pytest.raises(
IntegrityConstraintError, match="Database integrity error"
):
await organization_crud.create(session, obj_in=org_in) await organization_crud.create(session, obj_in=org_in)
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -1059,7 +1061,8 @@ class TestOrganizationExceptionHandlers:
with patch.object(session, "commit", side_effect=mock_commit): with patch.object(session, "commit", side_effect=mock_commit):
with patch.object(session, "rollback", new_callable=AsyncMock): with patch.object(session, "rollback", new_callable=AsyncMock):
with pytest.raises( with pytest.raises(
IntegrityConstraintError, match="Failed to add user to organization" IntegrityConstraintError,
match="Failed to add user to organization",
): ):
await organization_crud.add_user( await organization_crud.add_user(
session, session,

View File

@@ -9,8 +9,8 @@ from uuid import uuid4
import pytest import pytest
from app.core.repository_exceptions import InvalidInputError from app.core.repository_exceptions import InvalidInputError
from app.repositories.session import session_repo as session_crud
from app.models.user_session import UserSession from app.models.user_session import UserSession
from app.repositories.session import session_repo as session_crud
from app.schemas.sessions import SessionCreate from app.schemas.sessions import SessionCreate

View File

@@ -11,8 +11,8 @@ import pytest
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from app.core.repository_exceptions import IntegrityConstraintError from app.core.repository_exceptions import IntegrityConstraintError
from app.repositories.session import session_repo as session_crud
from app.models.user_session import UserSession from app.models.user_session import UserSession
from app.repositories.session import session_repo as session_crud
from app.schemas.sessions import SessionCreate from app.schemas.sessions import SessionCreate
@@ -103,7 +103,9 @@ class TestSessionCRUDCreateSessionFailures:
last_used_at=datetime.now(UTC), last_used_at=datetime.now(UTC),
) )
with pytest.raises(IntegrityConstraintError, match="Failed to create session"): with pytest.raises(
IntegrityConstraintError, match="Failed to create session"
):
await session_crud.create_session(session, obj_in=session_data) await session_crud.create_session(session, obj_in=session_data)
mock_rollback.assert_called_once() mock_rollback.assert_called_once()
@@ -134,7 +136,9 @@ class TestSessionCRUDCreateSessionFailures:
last_used_at=datetime.now(UTC), last_used_at=datetime.now(UTC),
) )
with pytest.raises(IntegrityConstraintError, match="Failed to create session"): with pytest.raises(
IntegrityConstraintError, match="Failed to create session"
):
await session_crud.create_session(session, obj_in=session_data) await session_crud.create_session(session, obj_in=session_data)
mock_rollback.assert_called_once() mock_rollback.assert_called_once()

View File

@@ -4,12 +4,11 @@
import uuid import uuid
import pytest import pytest
import pytest_asyncio
from app.core.exceptions import NotFoundError from app.core.exceptions import NotFoundError
from app.models.user_organization import OrganizationRole from app.models.user_organization import OrganizationRole
from app.schemas.organizations import OrganizationCreate, OrganizationUpdate from app.schemas.organizations import OrganizationCreate, OrganizationUpdate
from app.services.organization_service import OrganizationService, organization_service from app.services.organization_service import organization_service
def _make_org_create(name=None, slug=None) -> OrganizationCreate: def _make_org_create(name=None, slug=None) -> OrganizationCreate:
@@ -50,9 +49,7 @@ class TestGetOrganization:
_test_engine, AsyncTestingSessionLocal = async_test_db _test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session: async with AsyncTestingSessionLocal() as session:
with pytest.raises(NotFoundError): with pytest.raises(NotFoundError):
await organization_service.get_organization( await organization_service.get_organization(session, str(uuid.uuid4()))
session, str(uuid.uuid4())
)
class TestCreateOrganization: class TestCreateOrganization:

View File

@@ -5,10 +5,9 @@ import uuid
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
import pytest import pytest
import pytest_asyncio
from app.schemas.sessions import SessionCreate from app.schemas.sessions import SessionCreate
from app.services.session_service import SessionService, session_service from app.services.session_service import session_service
def _make_session_create(user_id, jti=None) -> SessionCreate: def _make_session_create(user_id, jti=None) -> SessionCreate:

View File

@@ -4,13 +4,12 @@
import uuid import uuid
import pytest import pytest
import pytest_asyncio
from sqlalchemy import select from sqlalchemy import select
from app.core.exceptions import NotFoundError from app.core.exceptions import NotFoundError
from app.models.user import User from app.models.user import User
from app.schemas.users import UserCreate, UserUpdate from app.schemas.users import UserCreate, UserUpdate
from app.services.user_service import UserService, user_service from app.services.user_service import user_service
class TestGetUser: class TestGetUser: