From 523b40dbfc5b4ef83781b16a3c3badab79cf14f7 Mon Sep 17 00:00:00 2001 From: Rohit Malhotra Date: Wed, 22 Oct 2025 10:52:10 -0400 Subject: [PATCH] SAAS: drop deprecated table (#11469) Co-authored-by: openhands --- .../versions/077_drop_settings_table.py | 27 +++++++++++++++++ enterprise/storage/saas_settings_store.py | 28 ------------------ enterprise/storage/stored_settings.py | 29 ------------------- enterprise/tests/unit/conftest.py | 3 +- .../tests/unit/test_saas_settings_store.py | 21 -------------- .../tests/unit/test_stripe_service_db.py | 3 +- 6 files changed, 29 insertions(+), 82 deletions(-) create mode 100644 enterprise/migrations/versions/077_drop_settings_table.py delete mode 100644 enterprise/storage/stored_settings.py diff --git a/enterprise/migrations/versions/077_drop_settings_table.py b/enterprise/migrations/versions/077_drop_settings_table.py new file mode 100644 index 0000000000..b4a1292434 --- /dev/null +++ b/enterprise/migrations/versions/077_drop_settings_table.py @@ -0,0 +1,27 @@ +"""drop settings table + +Revision ID: 077 +Revises: 076 +Create Date: 2025-10-21 00:00:00.000000 + +""" + +from typing import Sequence, Union + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = '077' +down_revision: Union[str, None] = '076' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Drop the deprecated settings table.""" + op.execute('DROP TABLE IF EXISTS settings') + + +def downgrade() -> None: + """No-op downgrade since the settings table is deprecated.""" + pass diff --git a/enterprise/storage/saas_settings_store.py b/enterprise/storage/saas_settings_store.py index 3614d99d49..0b5d40fe2c 100644 --- a/enterprise/storage/saas_settings_store.py +++ b/enterprise/storage/saas_settings_store.py @@ -24,7 +24,6 @@ from server.constants import ( from server.logger import logger from sqlalchemy.orm import sessionmaker from storage.database import session_maker -from storage.stored_settings import StoredSettings from storage.user_settings import UserSettings from openhands.core.config.openhands_config import OpenHandsConfig @@ -144,33 +143,6 @@ class SaasSettingsStore(SettingsStore): await self.store(settings) return settings - def load_legacy_db_settings(self, github_user_id: str) -> Settings | None: - if not github_user_id: - return None - - with self.session_maker() as session: - settings = ( - session.query(StoredSettings) - .filter(StoredSettings.id == github_user_id) - .first() - ) - if settings is None: - return None - - logger.info( - 'saas_settings_store:load_legacy_db_settings:found', - extra={'github_user_id': github_user_id}, - ) - kwargs = { - c.name: getattr(settings, c.name) - for c in StoredSettings.__table__.columns - if c.name in Settings.model_fields - } - self._decrypt_kwargs(kwargs) - del kwargs['secrets_store'] - settings = Settings(**kwargs) - return settings - async def load_legacy_file_store_settings(self, github_user_id: str): if not github_user_id: return None diff --git a/enterprise/storage/stored_settings.py b/enterprise/storage/stored_settings.py deleted file mode 100644 index f9502fdd34..0000000000 --- a/enterprise/storage/stored_settings.py +++ /dev/null @@ -1,29 +0,0 @@ -import uuid - -from sqlalchemy import JSON, Boolean, Column, Float, Integer, String -from storage.base import Base - - -class StoredSettings(Base): # type: ignore - """ - Legacy user settings storage. This should be considered deprecated - use UserSettings isntead - """ - - __tablename__ = 'settings' - id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) - language = Column(String, nullable=True) - agent = Column(String, nullable=True) - max_iterations = Column(Integer, nullable=True) - security_analyzer = Column(String, nullable=True) - confirmation_mode = Column(Boolean, nullable=True, default=False) - llm_model = Column(String, nullable=True) - llm_api_key = Column(String, nullable=True) - llm_base_url = Column(String, nullable=True) - remote_runtime_resource_factor = Column(Integer, nullable=True) - enable_default_condenser = Column(Boolean, nullable=False, default=True) - user_consents_to_analytics = Column(Boolean, nullable=True) - margin = Column(Float, nullable=True) - enable_sound_notifications = Column(Boolean, nullable=True, default=False) - sandbox_base_container_image = Column(String, nullable=True) - sandbox_runtime_container_image = Column(String, nullable=True) - secrets_store = Column(JSON, nullable=True) diff --git a/enterprise/tests/unit/conftest.py b/enterprise/tests/unit/conftest.py index 930098b4d3..08516fd813 100644 --- a/enterprise/tests/unit/conftest.py +++ b/enterprise/tests/unit/conftest.py @@ -17,7 +17,6 @@ from storage.github_app_installation import GithubAppInstallation from storage.maintenance_task import MaintenanceTask, MaintenanceTaskStatus from storage.stored_conversation_metadata import StoredConversationMetadata from storage.stored_offline_token import StoredOfflineToken -from storage.stored_settings import StoredSettings from storage.stripe_customer import StripeCustomer from storage.user_settings import UserSettings @@ -85,7 +84,7 @@ def add_minimal_fixtures(session_maker): updated_at=datetime.fromisoformat('2025-03-08'), ) ) - session.add(StoredSettings(id='mock-user-id', user_consents_to_analytics=True)) + session.add( StripeCustomer( keycloak_user_id='mock-user-id', diff --git a/enterprise/tests/unit/test_saas_settings_store.py b/enterprise/tests/unit/test_saas_settings_store.py index de6fcd349c..6a01eb8213 100644 --- a/enterprise/tests/unit/test_saas_settings_store.py +++ b/enterprise/tests/unit/test_saas_settings_store.py @@ -8,7 +8,6 @@ from server.constants import ( LITE_LLM_TEAM_ID, ) from storage.saas_settings_store import SaasSettingsStore -from storage.stored_settings import StoredSettings from storage.user_settings import UserSettings from openhands.core.config.openhands_config import OpenHandsConfig @@ -303,26 +302,6 @@ async def test_create_default_settings_require_payment_disabled( assert settings.language == 'en' -@pytest.mark.asyncio -async def test_create_default_settings_with_existing_llm_key( - settings_store, mock_stripe, mock_github_user, mock_litellm_api, session_maker -): - # Test that existing llm_api_key is preserved and not overwritten with litellm default - with ( - patch('storage.saas_settings_store.REQUIRE_PAYMENT', False), - patch('storage.saas_settings_store.LITE_LLM_API_KEY', 'mock-api-key'), - patch('storage.saas_settings_store.session_maker', session_maker), - ): - with settings_store.session_maker() as session: - kwargs = {'id': '12345', 'language': 'en', 'llm_api_key': 'existing_key'} - settings_store._encrypt_kwargs(kwargs) - session.merge(StoredSettings(**kwargs)) - session.commit() - updated_settings = await settings_store.create_default_settings(None) - assert updated_settings is not None - assert updated_settings.llm_api_key.get_secret_value() == 'test_api_key' - - @pytest.mark.asyncio async def test_create_default_lite_llm_settings_no_api_config(settings_store): with ( diff --git a/enterprise/tests/unit/test_stripe_service_db.py b/enterprise/tests/unit/test_stripe_service_db.py index f9448dd29f..8a2a89f2eb 100644 --- a/enterprise/tests/unit/test_stripe_service_db.py +++ b/enterprise/tests/unit/test_stripe_service_db.py @@ -13,7 +13,6 @@ from integrations.stripe_service import ( ) from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from storage.stored_settings import Base as StoredBase from storage.stripe_customer import Base as StripeCustomerBase from storage.stripe_customer import StripeCustomer from storage.user_settings import Base as UserBase @@ -22,7 +21,7 @@ from storage.user_settings import Base as UserBase @pytest.fixture def engine(): engine = create_engine('sqlite:///:memory:') - StoredBase.metadata.create_all(engine) + UserBase.metadata.create_all(engine) StripeCustomerBase.metadata.create_all(engine) return engine