chore(backend): standardize multiline formatting across modules

Reformatted multiline function calls, object definitions, and queries for improved code readability and consistency. Adjusted imports and constraints where necessary.
This commit is contained in:
2026-01-03 01:35:18 +01:00
parent da5affd613
commit acd18ff694
26 changed files with 540 additions and 357 deletions

View File

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

View File

@@ -5,6 +5,7 @@ Revises:
Create Date: 2025-11-27 09:08:09.464506
"""
from collections.abc import Sequence
import sqlalchemy as sa
@@ -12,7 +13,7 @@ from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '0001'
revision: str = "0001"
down_revision: str | None = None
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
@@ -20,243 +21,426 @@ depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('oauth_states',
sa.Column('state', sa.String(length=255), nullable=False),
sa.Column('code_verifier', sa.String(length=128), nullable=True),
sa.Column('nonce', sa.String(length=255), nullable=True),
sa.Column('provider', sa.String(length=50), nullable=False),
sa.Column('redirect_uri', sa.String(length=500), nullable=True),
sa.Column('user_id', sa.UUID(), nullable=True),
sa.Column('expires_at', sa.DateTime(timezone=True), 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.PrimaryKeyConstraint('id')
op.create_table(
"oauth_states",
sa.Column("state", sa.String(length=255), nullable=False),
sa.Column("code_verifier", sa.String(length=128), nullable=True),
sa.Column("nonce", sa.String(length=255), nullable=True),
sa.Column("provider", sa.String(length=50), nullable=False),
sa.Column("redirect_uri", sa.String(length=500), nullable=True),
sa.Column("user_id", sa.UUID(), nullable=True),
sa.Column("expires_at", sa.DateTime(timezone=True), 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.PrimaryKeyConstraint("id"),
)
op.create_index(op.f('ix_oauth_states_state'), 'oauth_states', ['state'], unique=True)
op.create_table('organizations',
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_oauth_states_state"), "oauth_states", ["state"], unique=True
)
op.create_index(op.f('ix_organizations_is_active'), 'organizations', ['is_active'], unique=False)
op.create_index(op.f('ix_organizations_name'), 'organizations', ['name'], unique=False)
op.create_index('ix_organizations_name_active', 'organizations', ['name', 'is_active'], unique=False)
op.create_index(op.f('ix_organizations_slug'), 'organizations', ['slug'], unique=True)
op.create_index('ix_organizations_slug_active', 'organizations', ['slug', 'is_active'], unique=False)
op.create_table('users',
sa.Column('email', sa.String(length=255), nullable=False),
sa.Column('password_hash', sa.String(length=255), nullable=True),
sa.Column('first_name', sa.String(length=100), nullable=False),
sa.Column('last_name', sa.String(length=100), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True),
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_table(
"organizations",
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_users_deleted_at'), 'users', ['deleted_at'], unique=False)
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
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_organizations_is_active"), "organizations", ["is_active"], unique=False
)
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_organizations_name"), "organizations", ["name"], unique=False
)
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_organizations_name_active",
"organizations",
["name", "is_active"],
unique=False,
)
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_organizations_slug"), "organizations", ["slug"], unique=True
)
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_organizations_slug_active",
"organizations",
["slug", "is_active"],
unique=False,
)
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_table(
"users",
sa.Column("email", sa.String(length=255), nullable=False),
sa.Column("password_hash", sa.String(length=255), nullable=True),
sa.Column("first_name", sa.String(length=100), nullable=False),
sa.Column("last_name", sa.String(length=100), nullable=True),
sa.Column("phone_number", sa.String(length=20), nullable=True),
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('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(op.f("ix_users_deleted_at"), "users", ["deleted_at"], unique=False)
op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
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.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 ###
def downgrade() -> None:
# ### 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.f('ix_oauth_provider_refresh_tokens_token_hash'), table_name='oauth_provider_refresh_tokens')
op.drop_index(op.f('ix_oauth_provider_refresh_tokens_revoked'), 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('ix_oauth_provider_refresh_tokens_client_user', table_name='oauth_provider_refresh_tokens')
op.drop_table('oauth_provider_refresh_tokens')
op.drop_index('ix_oauth_consents_user_client', table_name='oauth_consents')
op.drop_table('oauth_consents')
op.drop_index('ix_oauth_authorization_codes_expires_at', table_name='oauth_authorization_codes')
op.drop_index(op.f('ix_oauth_authorization_codes_code'), table_name='oauth_authorization_codes')
op.drop_index('ix_oauth_authorization_codes_client_user', table_name='oauth_authorization_codes')
op.drop_table('oauth_authorization_codes')
op.drop_index(op.f('ix_user_sessions_user_id'), table_name='user_sessions')
op.drop_index('ix_user_sessions_user_active', table_name='user_sessions')
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.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')
op.drop_index(
"ix_oauth_provider_refresh_tokens_user_revoked",
table_name="oauth_provider_refresh_tokens",
)
op.drop_index(
op.f("ix_oauth_provider_refresh_tokens_token_hash"),
table_name="oauth_provider_refresh_tokens",
)
op.drop_index(
op.f("ix_oauth_provider_refresh_tokens_revoked"),
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(
"ix_oauth_provider_refresh_tokens_client_user",
table_name="oauth_provider_refresh_tokens",
)
op.drop_table("oauth_provider_refresh_tokens")
op.drop_index("ix_oauth_consents_user_client", table_name="oauth_consents")
op.drop_table("oauth_consents")
op.drop_index(
"ix_oauth_authorization_codes_expires_at",
table_name="oauth_authorization_codes",
)
op.drop_index(
op.f("ix_oauth_authorization_codes_code"),
table_name="oauth_authorization_codes",
)
op.drop_index(
"ix_oauth_authorization_codes_client_user",
table_name="oauth_authorization_codes",
)
op.drop_table("oauth_authorization_codes")
op.drop_index(op.f("ix_user_sessions_user_id"), table_name="user_sessions")
op.drop_index("ix_user_sessions_user_active", table_name="user_sessions")
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.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 ###

View File

@@ -114,8 +114,13 @@ def upgrade() -> None:
def downgrade() -> None:
# Drop indexes in reverse order
op.drop_index("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_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_organizations_slug_lower", table_name="organizations")
op.drop_index("ix_perf_users_active", table_name="users")

View File

@@ -443,9 +443,7 @@ def upgrade() -> None:
),
sa.PrimaryKeyConstraint("id"),
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(
["parent_id"], ["issues.id"], ondelete="CASCADE"
),
sa.ForeignKeyConstraint(["parent_id"], ["issues.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["sprint_id"], ["sprints.id"], ondelete="SET NULL"),
sa.ForeignKeyConstraint(
["assigned_agent_id"], ["agent_instances.id"], ondelete="SET NULL"
@@ -462,7 +460,9 @@ def upgrade() -> None:
op.create_index("ix_issues_human_assignee", "issues", ["human_assignee"])
op.create_index("ix_issues_sprint_id", "issues", ["sprint_id"])
op.create_index("ix_issues_due_date", "issues", ["due_date"])
op.create_index("ix_issues_external_tracker_type", "issues", ["external_tracker_type"])
op.create_index(
"ix_issues_external_tracker_type", "issues", ["external_tracker_type"]
)
op.create_index("ix_issues_sync_status", "issues", ["sync_status"])
op.create_index("ix_issues_closed_at", "issues", ["closed_at"])
# Composite indexes
@@ -470,7 +470,9 @@ def upgrade() -> None:
op.create_index("ix_issues_project_priority", "issues", ["project_id", "priority"])
op.create_index("ix_issues_project_sprint", "issues", ["project_id", "sprint_id"])
op.create_index("ix_issues_project_type", "issues", ["project_id", "type"])
op.create_index("ix_issues_project_agent", "issues", ["project_id", "assigned_agent_id"])
op.create_index(
"ix_issues_project_agent", "issues", ["project_id", "assigned_agent_id"]
)
op.create_index(
"ix_issues_project_status_priority",
"issues",

View File

@@ -32,9 +32,7 @@ api_router.include_router(
api_router.include_router(events.router, tags=["Events"])
# Syndarix domain routers
api_router.include_router(
projects.router, prefix="/projects", tags=["Projects"]
)
api_router.include_router(projects.router, prefix="/projects", tags=["Projects"])
api_router.include_router(
agent_types.router, prefix="/agent-types", tags=["Agent Types"]
)

View File

@@ -57,8 +57,18 @@ RATE_MULTIPLIER = 100 if IS_TEST else 1
# Valid status transitions for agent lifecycle management
VALID_STATUS_TRANSITIONS: dict[AgentStatus, set[AgentStatus]] = {
AgentStatus.IDLE: {AgentStatus.WORKING, AgentStatus.PAUSED, AgentStatus.TERMINATED},
AgentStatus.WORKING: {AgentStatus.IDLE, AgentStatus.WAITING, AgentStatus.PAUSED, AgentStatus.TERMINATED},
AgentStatus.WAITING: {AgentStatus.IDLE, AgentStatus.WORKING, AgentStatus.PAUSED, AgentStatus.TERMINATED},
AgentStatus.WORKING: {
AgentStatus.IDLE,
AgentStatus.WAITING,
AgentStatus.PAUSED,
AgentStatus.TERMINATED,
},
AgentStatus.WAITING: {
AgentStatus.IDLE,
AgentStatus.WORKING,
AgentStatus.PAUSED,
AgentStatus.TERMINATED,
},
AgentStatus.PAUSED: {AgentStatus.IDLE, AgentStatus.TERMINATED},
AgentStatus.TERMINATED: set(), # Terminal state, no transitions allowed
}
@@ -870,9 +880,7 @@ async def terminate_agent(
agent_name = agent.name
# Terminate the agent
terminated_agent = await agent_instance_crud.terminate(
db, instance_id=agent_id
)
terminated_agent = await agent_instance_crud.terminate(db, instance_id=agent_id)
if not terminated_agent:
raise NotFoundError(
@@ -881,8 +889,7 @@ async def terminate_agent(
)
logger.info(
f"User {current_user.email} terminated agent {agent_name} "
f"(id={agent_id})"
f"User {current_user.email} terminated agent {agent_name} (id={agent_id})"
)
return MessageResponse(

View File

@@ -199,7 +199,9 @@ async def stream_project_events(
project_id: UUID,
db: "AsyncSession" = Depends(get_db),
event_bus: EventBus = Depends(get_event_bus),
token: str | None = Query(None, description="Auth token (for EventSource compatibility)"),
token: str | None = Query(
None, description="Auth token (for EventSource compatibility)"
),
authorization: str | None = Header(None, alias="Authorization"),
last_event_id: str | None = Header(None, alias="Last-Event-ID"),
):

View File

@@ -278,9 +278,7 @@ async def list_issues(
assigned_agent_id: UUID | None = Query(
None, description="Filter by assigned agent ID"
),
sync_status: SyncStatus | None = Query(
None, description="Filter by sync status"
),
sync_status: SyncStatus | None = Query(None, description="Filter by sync status"),
search: str | None = Query(
None, min_length=1, max_length=100, description="Search in title and body"
),
@@ -783,9 +781,7 @@ async def assign_issue(
updated_issue = await issue_crud.assign_to_agent(
db, issue_id=issue_id, agent_id=None
)
logger.info(
f"User {current_user.email} unassigned issue {issue_id}"
)
logger.info(f"User {current_user.email} unassigned issue {issue_id}")
if not updated_issue:
raise NotFoundError(

View File

@@ -197,10 +197,10 @@ async def list_projects(
status_filter: ProjectStatus | None = Query(
None, alias="status", description="Filter by project status"
),
search: str | None = Query(None, description="Search by name, slug, or description"),
all_projects: bool = Query(
False, description="Show all projects (superuser only)"
search: str | None = Query(
None, description="Search by name, slug, or description"
),
all_projects: bool = Query(False, description="Show all projects (superuser only)"),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> Any:
@@ -212,7 +212,9 @@ async def list_projects(
"""
try:
# Determine owner filter based on user role and request
owner_id = None if (current_user.is_superuser and all_projects) else current_user.id
owner_id = (
None if (current_user.is_superuser and all_projects) else current_user.id
)
projects_data, total = await project_crud.get_multi_with_counts(
db,
@@ -379,13 +381,15 @@ async def update_project(
_check_project_ownership(project, current_user)
# Update the project
updated_project = await project_crud.update(db, db_obj=project, obj_in=project_in)
logger.info(
f"User {current_user.email} updated project {updated_project.slug}"
updated_project = await project_crud.update(
db, db_obj=project, obj_in=project_in
)
logger.info(f"User {current_user.email} updated project {updated_project.slug}")
# Get updated project with counts
project_data = await project_crud.get_with_counts(db, project_id=updated_project.id)
project_data = await project_crud.get_with_counts(
db, project_id=updated_project.id
)
if not project_data:
# This shouldn't happen, but handle gracefully
@@ -551,7 +555,9 @@ async def pause_project(
logger.info(f"User {current_user.email} paused project {project.slug}")
# Get project with counts
project_data = await project_crud.get_with_counts(db, project_id=updated_project.id)
project_data = await project_crud.get_with_counts(
db, project_id=updated_project.id
)
if not project_data:
raise NotFoundError(
@@ -634,7 +640,9 @@ async def resume_project(
logger.info(f"User {current_user.email} resumed project {project.slug}")
# Get project with counts
project_data = await project_crud.get_with_counts(db, project_id=updated_project.id)
project_data = await project_crud.get_with_counts(
db, project_id=updated_project.id
)
if not project_data:
raise NotFoundError(

View File

@@ -320,7 +320,9 @@ async def list_sprints(
return PaginatedResponse(data=sprint_responses, pagination=pagination_meta)
except Exception as e:
logger.error(f"Error listing sprints for project {project_id}: {e!s}", exc_info=True)
logger.error(
f"Error listing sprints for project {project_id}: {e!s}", exc_info=True
)
raise
@@ -564,7 +566,9 @@ async def update_sprint(
)
# Update the sprint
updated_sprint = await sprint_crud.update(db, db_obj=sprint, obj_in=sprint_update)
updated_sprint = await sprint_crud.update(
db, db_obj=sprint, obj_in=sprint_update
)
logger.info(
f"User {current_user.id} updated sprint {sprint_id} in project {project_id}"
@@ -1123,7 +1127,9 @@ async def remove_issue_from_sprint(
request: Request,
project_id: UUID,
sprint_id: UUID,
issue_id: UUID = Query(..., description="ID of the issue to remove from the sprint"),
issue_id: UUID = Query(
..., description="ID of the issue to remove from the sprint"
),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> Any:

View File

@@ -243,7 +243,9 @@ class RedisClient:
try:
client = await self._get_client()
result = await client.expire(key, ttl)
logger.debug(f"Cache expire for key: {key} (TTL: {ttl}s, success: {result})")
logger.debug(
f"Cache expire for key: {key} (TTL: {ttl}s, success: {result})"
)
return result
except (ConnectionError, TimeoutError) as e:
logger.error(f"Redis cache_expire failed for key '{key}': {e}")
@@ -323,9 +325,7 @@ class RedisClient:
return 0
@asynccontextmanager
async def subscribe(
self, *channels: str
) -> AsyncGenerator[PubSub, None]:
async def subscribe(self, *channels: str) -> AsyncGenerator[PubSub, None]:
"""
Subscribe to one or more channels.
@@ -353,9 +353,7 @@ class RedisClient:
logger.debug(f"Unsubscribed from channels: {channels}")
@asynccontextmanager
async def psubscribe(
self, *patterns: str
) -> AsyncGenerator[PubSub, None]:
async def psubscribe(self, *patterns: str) -> AsyncGenerator[PubSub, None]:
"""
Subscribe to channels matching patterns.

View File

@@ -20,7 +20,9 @@ from app.schemas.syndarix import AgentInstanceCreate, AgentInstanceUpdate
logger = logging.getLogger(__name__)
class CRUDAgentInstance(CRUDBase[AgentInstance, AgentInstanceCreate, AgentInstanceUpdate]):
class CRUDAgentInstance(
CRUDBase[AgentInstance, AgentInstanceCreate, AgentInstanceUpdate]
):
"""Async CRUD operations for AgentInstance model."""
async def create(
@@ -91,8 +93,12 @@ class CRUDAgentInstance(CRUDBase[AgentInstance, AgentInstanceCreate, AgentInstan
return {
"instance": instance,
"agent_type_name": instance.agent_type.name if instance.agent_type else None,
"agent_type_slug": instance.agent_type.slug if instance.agent_type else None,
"agent_type_name": instance.agent_type.name
if instance.agent_type
else None,
"agent_type_slug": instance.agent_type.slug
if instance.agent_type
else None,
"project_name": instance.project.name if instance.project else None,
"project_slug": instance.project.slug if instance.project else None,
"assigned_issues_count": assigned_issues_count,
@@ -115,9 +121,7 @@ class CRUDAgentInstance(CRUDBase[AgentInstance, AgentInstanceCreate, AgentInstan
) -> tuple[list[AgentInstance], int]:
"""Get agent instances for a specific project."""
try:
query = select(AgentInstance).where(
AgentInstance.project_id == project_id
)
query = select(AgentInstance).where(AgentInstance.project_id == project_id)
if status is not None:
query = query.where(AgentInstance.status == status)

View File

@@ -22,17 +22,13 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
async def get_by_slug(self, db: AsyncSession, *, slug: str) -> AgentType | None:
"""Get agent type by slug."""
try:
result = await db.execute(
select(AgentType).where(AgentType.slug == slug)
)
result = await db.execute(select(AgentType).where(AgentType.slug == slug))
return result.scalar_one_or_none()
except Exception as e:
logger.error(f"Error getting agent type by slug {slug}: {e!s}")
raise
async def create(
self, db: AsyncSession, *, obj_in: AgentTypeCreate
) -> AgentType:
async def create(self, db: AsyncSession, *, obj_in: AgentTypeCreate) -> AgentType:
"""Create a new agent type with error handling."""
try:
db_obj = AgentType(
@@ -57,16 +53,12 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
error_msg = str(e.orig) if hasattr(e, "orig") else str(e)
if "slug" in error_msg.lower():
logger.warning(f"Duplicate slug attempted: {obj_in.slug}")
raise ValueError(
f"Agent type with slug '{obj_in.slug}' already exists"
)
raise ValueError(f"Agent type with slug '{obj_in.slug}' already exists")
logger.error(f"Integrity error creating agent type: {error_msg}")
raise ValueError(f"Database integrity error: {error_msg}")
except Exception as e:
await db.rollback()
logger.error(
f"Unexpected error creating agent type: {e!s}", exc_info=True
)
logger.error(f"Unexpected error creating agent type: {e!s}", exc_info=True)
raise
async def get_multi_with_filters(
@@ -215,9 +207,7 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
return results, total
except Exception as e:
logger.error(
f"Error getting agent types with counts: {e!s}", exc_info=True
)
logger.error(f"Error getting agent types with counts: {e!s}", exc_info=True)
raise
async def get_by_expertise(

View File

@@ -75,7 +75,9 @@ class CRUDIssue(CRUDBase[Issue, IssueCreate, IssueUpdate]):
.options(
joinedload(Issue.project),
joinedload(Issue.sprint),
joinedload(Issue.assigned_agent).joinedload(AgentInstance.agent_type),
joinedload(Issue.assigned_agent).joinedload(
AgentInstance.agent_type
),
)
.where(Issue.id == issue_id)
)
@@ -449,9 +451,7 @@ class CRUDIssue(CRUDBase[Issue, IssueCreate, IssueUpdate]):
from sqlalchemy import update
result = await db.execute(
update(Issue)
.where(Issue.sprint_id == sprint_id)
.values(sprint_id=None)
update(Issue).where(Issue.sprint_id == sprint_id).values(sprint_id=None)
)
await db.commit()
return result.rowcount

View File

@@ -2,11 +2,10 @@
"""Async CRUD operations for Project model using SQLAlchemy 2.0 patterns."""
import logging
from datetime import UTC, datetime
from typing import Any
from uuid import UUID
from datetime import UTC, datetime
from sqlalchemy import func, or_, select, update
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
@@ -234,9 +233,7 @@ class CRUDProject(CRUDBase[Project, ProjectCreate, ProjectUpdate]):
Sprint.status == SprintStatus.ACTIVE,
)
)
active_sprints = {
row.project_id: row.name for row in active_sprints_result
}
active_sprints = {row.project_id: row.name for row in active_sprints_result}
# Combine results
results = [
@@ -251,9 +248,7 @@ class CRUDProject(CRUDBase[Project, ProjectCreate, ProjectUpdate]):
return results, total
except Exception as e:
logger.error(
f"Error getting projects with counts: {e!s}", exc_info=True
)
logger.error(f"Error getting projects with counts: {e!s}", exc_info=True)
raise
async def get_projects_by_owner(
@@ -293,9 +288,7 @@ class CRUDProject(CRUDBase[Project, ProjectCreate, ProjectUpdate]):
- Unassigns issues from terminated agents
"""
try:
result = await db.execute(
select(Project).where(Project.id == project_id)
)
result = await db.execute(select(Project).where(Project.id == project_id))
project = result.scalar_one_or_none()
if not project:
@@ -361,9 +354,7 @@ class CRUDProject(CRUDBase[Project, ProjectCreate, ProjectUpdate]):
return project
except Exception as e:
await db.rollback()
logger.error(
f"Error archiving project {project_id}: {e!s}", exc_info=True
)
logger.error(f"Error archiving project {project_id}: {e!s}", exc_info=True)
raise

View File

@@ -193,9 +193,7 @@ class CRUDSprint(CRUDBase[Sprint, SprintCreate, SprintUpdate]):
try:
# Lock the sprint row to prevent concurrent modifications
result = await db.execute(
select(Sprint)
.where(Sprint.id == sprint_id)
.with_for_update()
select(Sprint).where(Sprint.id == sprint_id).with_for_update()
)
sprint = result.scalar_one_or_none()
@@ -257,9 +255,7 @@ class CRUDSprint(CRUDBase[Sprint, SprintCreate, SprintUpdate]):
try:
# Lock the sprint row to prevent concurrent modifications
result = await db.execute(
select(Sprint)
.where(Sprint.id == sprint_id)
.with_for_update()
select(Sprint).where(Sprint.id == sprint_id).with_for_update()
)
sprint = result.scalar_one_or_none()
@@ -308,9 +304,7 @@ class CRUDSprint(CRUDBase[Sprint, SprintCreate, SprintUpdate]):
try:
# Lock the sprint row to prevent concurrent modifications
result = await db.execute(
select(Sprint)
.where(Sprint.id == sprint_id)
.with_for_update()
select(Sprint).where(Sprint.id == sprint_id).with_for_update()
)
sprint = result.scalar_one_or_none()
@@ -425,7 +419,8 @@ class CRUDSprint(CRUDBase[Sprint, SprintCreate, SprintUpdate]):
{
"sprint": sprint,
**counts_map.get(
sprint.id, {"issue_count": 0, "open_issues": 0, "completed_issues": 0}
sprint.id,
{"issue_count": 0, "open_issues": 0, "completed_issues": 0},
),
}
for sprint in sprints

View File

@@ -158,7 +158,11 @@ class Issue(Base, UUIDMixin, TimestampMixin):
Index("ix_issues_project_status", "project_id", "status"),
Index("ix_issues_project_priority", "project_id", "priority"),
Index("ix_issues_project_sprint", "project_id", "sprint_id"),
Index("ix_issues_external_tracker_id", "external_tracker_type", "external_issue_id"),
Index(
"ix_issues_external_tracker_id",
"external_tracker_type",
"external_issue_id",
),
Index("ix_issues_sync_status", "sync_status"),
Index("ix_issues_project_agent", "project_id", "assigned_agent_id"),
Index("ix_issues_project_type", "project_id", "type"),

View File

@@ -5,7 +5,17 @@ Sprint model for Syndarix AI consulting platform.
A Sprint represents a time-boxed iteration for organizing and delivering work.
"""
from sqlalchemy import Column, Date, Enum, ForeignKey, Index, Integer, String, Text, UniqueConstraint
from sqlalchemy import (
Column,
Date,
Enum,
ForeignKey,
Index,
Integer,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.orm import relationship

View File

@@ -205,9 +205,7 @@ class SprintCompletedPayload(BaseModel):
sprint_id: UUID = Field(..., description="Sprint ID")
sprint_name: str = Field(..., description="Sprint name")
completed_issues: int = Field(default=0, description="Number of completed issues")
incomplete_issues: int = Field(
default=0, description="Number of incomplete issues"
)
incomplete_issues: int = Field(default=0, description="Number of incomplete issues")
class ApprovalRequestedPayload(BaseModel):

View File

@@ -99,9 +99,7 @@ class IssueAssign(BaseModel):
def validate_assignment(self) -> "IssueAssign":
"""Ensure only one type of assignee is set."""
if self.assigned_agent_id and self.human_assignee:
raise ValueError(
"Cannot assign to both an agent and a human. Choose one."
)
raise ValueError("Cannot assign to both an agent and a human. Choose one.")
return self

View File

@@ -54,22 +54,18 @@ class EventBusError(Exception):
"""Base exception for EventBus errors."""
class EventBusConnectionError(EventBusError):
"""Raised when connection to Redis fails."""
class EventBusPublishError(EventBusError):
"""Raised when publishing an event fails."""
class EventBusSubscriptionError(EventBusError):
"""Raised when subscribing to channels fails."""
class EventBus:
"""
EventBus for Redis Pub/Sub communication.

View File

@@ -343,7 +343,9 @@ class OAuthService:
await oauth_account.update_tokens(
db,
account=existing_oauth,
access_token_encrypted=token.get("access_token"), refresh_token_encrypted=token.get("refresh_token"), token_expires_at=datetime.now(UTC)
access_token_encrypted=token.get("access_token"),
refresh_token_encrypted=token.get("refresh_token"),
token_expires_at=datetime.now(UTC)
+ timedelta(seconds=token.get("expires_in", 3600)),
)
@@ -375,7 +377,9 @@ class OAuthService:
provider=provider,
provider_user_id=provider_user_id,
provider_email=provider_email,
access_token_encrypted=token.get("access_token"), refresh_token_encrypted=token.get("refresh_token"), token_expires_at=datetime.now(UTC)
access_token_encrypted=token.get("access_token"),
refresh_token_encrypted=token.get("refresh_token"),
token_expires_at=datetime.now(UTC)
+ timedelta(seconds=token.get("expires_in", 3600))
if token.get("expires_in")
else None,
@@ -644,7 +648,9 @@ class OAuthService:
provider=provider,
provider_user_id=provider_user_id,
provider_email=email,
access_token_encrypted=token.get("access_token"), refresh_token_encrypted=token.get("refresh_token"), token_expires_at=datetime.now(UTC)
access_token_encrypted=token.get("access_token"),
refresh_token_encrypted=token.get("refresh_token"),
token_expires_at=datetime.now(UTC)
+ timedelta(seconds=token.get("expires_in", 3600))
if token.get("expires_in")
else None,

View File

@@ -91,9 +91,7 @@ def spawn_agent(
Returns:
dict with status, agent_type_id, and project_id
"""
logger.info(
f"Spawning agent of type {agent_type_id} for project {project_id}"
)
logger.info(f"Spawning agent of type {agent_type_id} for project {project_id}")
# TODO: Implement agent spawning
# This will involve:
@@ -132,9 +130,7 @@ def terminate_agent(
Returns:
dict with status and agent_instance_id
"""
logger.info(
f"Terminating agent instance {agent_instance_id} with reason: {reason}"
)
logger.info(f"Terminating agent instance {agent_instance_id} with reason: {reason}")
# TODO: Implement agent termination
# This will involve:

View File

@@ -86,9 +86,7 @@ def commit_changes(
Returns:
dict with status and project_id
"""
logger.info(
f"Committing changes for project {project_id}: {message}"
)
logger.info(f"Committing changes for project {project_id}: {message}")
# TODO: Implement commit operation
# This will involve:
@@ -209,9 +207,7 @@ def push_changes(
Returns:
dict with status and project_id
"""
logger.info(
f"Pushing branch {branch} for project {project_id} (force={force})"
)
logger.info(f"Pushing branch {branch} for project {project_id} (force={force})")
# TODO: Implement push operation
# This will involve:

View File

@@ -140,9 +140,7 @@ def sync_project_issues(
Returns:
dict with status and project_id
"""
logger.info(
f"Syncing issues for project {project_id} (full={full})"
)
logger.info(f"Syncing issues for project {project_id} (full={full})")
# TODO: Implement project-specific sync
# This will involve:
@@ -180,9 +178,7 @@ def push_issue_to_external(
Returns:
dict with status, issue_id, and operation
"""
logger.info(
f"Pushing {operation} for issue {issue_id} in project {project_id}"
)
logger.info(f"Pushing {operation} for issue {issue_id} in project {project_id}")
# TODO: Implement outbound sync
# This will involve:

View File

@@ -72,9 +72,7 @@ def execute_workflow_step(
Returns:
dict with status, workflow_id, and transition
"""
logger.info(
f"Executing transition '{transition}' for workflow {workflow_id}"
)
logger.info(f"Executing transition '{transition}' for workflow {workflow_id}")
# TODO: Implement workflow transition
# This will involve:
@@ -196,9 +194,7 @@ def start_story_workflow(
Returns:
dict with status and story_id
"""
logger.info(
f"Starting story workflow for story {story_id} in project {project_id}"
)
logger.info(f"Starting story workflow for story {story_id} in project {project_id}")
# TODO: Implement story workflow initialization
# This will involve: