diff --git a/mcpgateway/alembic/versions/878e287d2366_resize_url_and_slug_columns_to_191.py b/mcpgateway/alembic/versions/878e287d2366_resize_url_and_slug_columns_to_191.py new file mode 100644 index 000000000..698fe2af4 --- /dev/null +++ b/mcpgateway/alembic/versions/878e287d2366_resize_url_and_slug_columns_to_191.py @@ -0,0 +1,130 @@ +"""" resize url and slug columns to 191" + +Revision ID: 878e287d2366 +Revises: 3c89a45f32e5 +Create Date: 2025-10-08 09:08:35.363100 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision: str = '878e287d2366' +down_revision: Union[str, Sequence[str], None] = '3c89a45f32e5' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # Get database dialect for dialect-specific operations + bind = op.get_bind() + dialect_name = bind.dialect.name + + # Truncate existing values longer than 191 chars using dialect-appropriate functions + if dialect_name == 'sqlite': + # SQLite uses SUBSTR and LENGTH + op.execute(""" + UPDATE gateways + SET slug = SUBSTR(slug, 1, 191), + url = SUBSTR(url, 1, 191) + WHERE LENGTH(slug) > 191 OR LENGTH(url) > 191; + """) + elif dialect_name == 'postgresql': + # PostgreSQL supports LEFT and CHAR_LENGTH + op.execute(""" + UPDATE gateways + SET slug = LEFT(slug, 191), + url = LEFT(url, 191) + WHERE CHAR_LENGTH(slug) > 191 OR CHAR_LENGTH(url) > 191; + """) + elif dialect_name == 'mysql': + # MySQL supports LEFT and CHAR_LENGTH (character-based, not byte-based) + op.execute(""" + UPDATE gateways + SET slug = LEFT(slug, 191), + url = LEFT(url, 191) + WHERE CHAR_LENGTH(slug) > 191 OR CHAR_LENGTH(url) > 191; + """) + else: + # Fallback for other databases + op.execute(""" + UPDATE gateways + SET slug = SUBSTR(slug, 1, 191), + url = SUBSTR(url, 1, 191) + WHERE LENGTH(slug) > 191 OR LENGTH(url) > 191; + """) + + # Resize columns to String(191) + # SQLite requires batch operations for ALTER COLUMN + if dialect_name == 'sqlite': + with op.batch_alter_table('gateways', schema=None) as batch_op: + batch_op.alter_column( + 'slug', + existing_type=sa.String(length=255), + type_=sa.String(length=191), + existing_nullable=False + ) + batch_op.alter_column( + 'url', + existing_type=sa.String(length=767), + type_=sa.String(length=191), + existing_nullable=False + ) + else: + # PostgreSQL and MySQL support direct ALTER COLUMN + op.alter_column( + 'gateways', + 'slug', + existing_type=sa.String(length=255), + type_=sa.String(length=191), + existing_nullable=False + ) + op.alter_column( + 'gateways', + 'url', + existing_type=sa.String(length=767), + type_=sa.String(length=191), + existing_nullable=False + ) + + +def downgrade() -> None: + """Downgrade schema.""" + # Get database dialect for dialect-specific operations + bind = op.get_bind() + dialect_name = bind.dialect.name + + # SQLite requires batch operations for ALTER COLUMN + if dialect_name == 'sqlite': + with op.batch_alter_table('gateways', schema=None) as batch_op: + batch_op.alter_column( + 'slug', + existing_type=sa.String(length=191), + type_=sa.String(length=255), + existing_nullable=False + ) + batch_op.alter_column( + 'url', + existing_type=sa.String(length=191), + type_=sa.String(length=767), + existing_nullable=False + ) + else: + # PostgreSQL and MySQL support direct ALTER COLUMN + op.alter_column( + 'gateways', + 'slug', + existing_type=sa.String(length=191), + type_=sa.String(length=255), + existing_nullable=False + ) + op.alter_column( + 'gateways', + 'url', + existing_type=sa.String(length=191), + type_=sa.String(length=767), + existing_nullable=False + ) diff --git a/mcpgateway/db.py b/mcpgateway/db.py index 7dfa40800..c905a00a8 100644 --- a/mcpgateway/db.py +++ b/mcpgateway/db.py @@ -2430,8 +2430,8 @@ class Gateway(Base): id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: uuid.uuid4().hex) name: Mapped[str] = mapped_column(String(255), nullable=False) - slug: Mapped[str] = mapped_column(String(255), nullable=False) - url: Mapped[str] = mapped_column(String(767), nullable=False) + slug: Mapped[str] = mapped_column(String(191), nullable=False) + url: Mapped[str] = mapped_column(String(191), nullable=False) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) transport: Mapped[str] = mapped_column(String(20), default="SSE") capabilities: Mapped[Dict[str, Any]] = mapped_column(JSON) diff --git a/tests/unit/mcpgateway/test_translate_stdio_endpoint.py b/tests/unit/mcpgateway/test_translate_stdio_endpoint.py index 4f08cdd9e..4ca7aaa7b 100644 --- a/tests/unit/mcpgateway/test_translate_stdio_endpoint.py +++ b/tests/unit/mcpgateway/test_translate_stdio_endpoint.py @@ -8,7 +8,6 @@ Tests for StdIOEndpoint class modifications to support dynamic environment variables. """ - import sys import asyncio import pytest @@ -256,15 +255,13 @@ async def test_multiple_env_vars(self, test_script): pubsub = _PubSub() env_vars = os.environ.copy() - env_vars.update( - { - "GITHUB_TOKEN": "github-token-123", - "TENANT_ID": "acme-corp", - "API_KEY": "api-key-456", - "ENVIRONMENT": "production", - "DEBUG": "false", - } - ) + env_vars.update({ + "GITHUB_TOKEN": "github-token-123", + "TENANT_ID": "acme-corp", + "API_KEY": "api-key-456", + "ENVIRONMENT": "production", + "DEBUG": "false", + }) endpoint = StdIOEndpoint(f"{sys.executable} {test_script}", pubsub, env_vars)