diff --git a/migrations/versions/081_add_all_integration_credentials.py b/migrations/versions/081_add_all_integration_credentials.py index 52e461e..8e857ce 100644 --- a/migrations/versions/081_add_all_integration_credentials.py +++ b/migrations/versions/081_add_all_integration_credentials.py @@ -25,90 +25,159 @@ branch_labels = None depends_on = None +def _has_table(inspector, table_name: str) -> bool: + try: + return table_name in inspector.get_table_names() + except Exception: + return False + + +def _has_column(inspector, table_name: str, column_name: str) -> bool: + try: + return column_name in {c["name"] for c in inspector.get_columns(table_name)} + except Exception: + return False + + def upgrade(): """Add integration OAuth credential columns to settings table""" + bind = op.get_bind() + inspector = sa.inspect(bind) + + if not _has_table(inspector, "settings"): + return + + settings_cols = {c["name"] for c in inspector.get_columns("settings")} + with op.batch_alter_table('settings', schema=None) as batch_op: # Google Calendar - batch_op.add_column(sa.Column('google_calendar_client_id', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('google_calendar_client_secret', sa.String(length=255), nullable=True)) + if 'google_calendar_client_id' not in settings_cols: + batch_op.add_column(sa.Column('google_calendar_client_id', sa.String(length=255), nullable=True)) + if 'google_calendar_client_secret' not in settings_cols: + batch_op.add_column(sa.Column('google_calendar_client_secret', sa.String(length=255), nullable=True)) # Outlook Calendar - batch_op.add_column(sa.Column('outlook_calendar_client_id', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('outlook_calendar_client_secret', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('outlook_calendar_tenant_id', sa.String(length=255), nullable=True)) + if 'outlook_calendar_client_id' not in settings_cols: + batch_op.add_column(sa.Column('outlook_calendar_client_id', sa.String(length=255), nullable=True)) + if 'outlook_calendar_client_secret' not in settings_cols: + batch_op.add_column(sa.Column('outlook_calendar_client_secret', sa.String(length=255), nullable=True)) + if 'outlook_calendar_tenant_id' not in settings_cols: + batch_op.add_column(sa.Column('outlook_calendar_tenant_id', sa.String(length=255), nullable=True)) # Microsoft Teams - batch_op.add_column(sa.Column('microsoft_teams_client_id', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('microsoft_teams_client_secret', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('microsoft_teams_tenant_id', sa.String(length=255), nullable=True)) + if 'microsoft_teams_client_id' not in settings_cols: + batch_op.add_column(sa.Column('microsoft_teams_client_id', sa.String(length=255), nullable=True)) + if 'microsoft_teams_client_secret' not in settings_cols: + batch_op.add_column(sa.Column('microsoft_teams_client_secret', sa.String(length=255), nullable=True)) + if 'microsoft_teams_tenant_id' not in settings_cols: + batch_op.add_column(sa.Column('microsoft_teams_tenant_id', sa.String(length=255), nullable=True)) # Asana - batch_op.add_column(sa.Column('asana_client_id', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('asana_client_secret', sa.String(length=255), nullable=True)) + if 'asana_client_id' not in settings_cols: + batch_op.add_column(sa.Column('asana_client_id', sa.String(length=255), nullable=True)) + if 'asana_client_secret' not in settings_cols: + batch_op.add_column(sa.Column('asana_client_secret', sa.String(length=255), nullable=True)) # Trello - batch_op.add_column(sa.Column('trello_api_key', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('trello_api_secret', sa.String(length=255), nullable=True)) + if 'trello_api_key' not in settings_cols: + batch_op.add_column(sa.Column('trello_api_key', sa.String(length=255), nullable=True)) + if 'trello_api_secret' not in settings_cols: + batch_op.add_column(sa.Column('trello_api_secret', sa.String(length=255), nullable=True)) # GitLab - batch_op.add_column(sa.Column('gitlab_client_id', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('gitlab_client_secret', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('gitlab_instance_url', sa.String(length=500), nullable=True)) + if 'gitlab_client_id' not in settings_cols: + batch_op.add_column(sa.Column('gitlab_client_id', sa.String(length=255), nullable=True)) + if 'gitlab_client_secret' not in settings_cols: + batch_op.add_column(sa.Column('gitlab_client_secret', sa.String(length=255), nullable=True)) + if 'gitlab_instance_url' not in settings_cols: + batch_op.add_column(sa.Column('gitlab_instance_url', sa.String(length=500), nullable=True)) # QuickBooks - batch_op.add_column(sa.Column('quickbooks_client_id', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('quickbooks_client_secret', sa.String(length=255), nullable=True)) + if 'quickbooks_client_id' not in settings_cols: + batch_op.add_column(sa.Column('quickbooks_client_id', sa.String(length=255), nullable=True)) + if 'quickbooks_client_secret' not in settings_cols: + batch_op.add_column(sa.Column('quickbooks_client_secret', sa.String(length=255), nullable=True)) # Xero - batch_op.add_column(sa.Column('xero_client_id', sa.String(length=255), nullable=True)) - batch_op.add_column(sa.Column('xero_client_secret', sa.String(length=255), nullable=True)) + if 'xero_client_id' not in settings_cols: + batch_op.add_column(sa.Column('xero_client_id', sa.String(length=255), nullable=True)) + if 'xero_client_secret' not in settings_cols: + batch_op.add_column(sa.Column('xero_client_secret', sa.String(length=255), nullable=True)) - # Set default empty values for existing rows - op.execute(""" - UPDATE settings - SET google_calendar_client_id = '', - google_calendar_client_secret = '', - outlook_calendar_client_id = '', - outlook_calendar_client_secret = '', - outlook_calendar_tenant_id = '', - microsoft_teams_client_id = '', - microsoft_teams_client_secret = '', - microsoft_teams_tenant_id = '', - asana_client_id = '', - asana_client_secret = '', - trello_api_key = '', - trello_api_secret = '', - gitlab_client_id = '', - gitlab_client_secret = '', - gitlab_instance_url = '', - quickbooks_client_id = '', - quickbooks_client_secret = '', - xero_client_id = '', - xero_client_secret = '' - WHERE google_calendar_client_id IS NULL - """) + # Refresh column list after alterations, then set defaults only for columns that exist. + inspector = sa.inspect(op.get_bind()) + settings_cols = {c["name"] for c in inspector.get_columns("settings")} + + set_parts = [] + for col in [ + "google_calendar_client_id", + "google_calendar_client_secret", + "outlook_calendar_client_id", + "outlook_calendar_client_secret", + "outlook_calendar_tenant_id", + "microsoft_teams_client_id", + "microsoft_teams_client_secret", + "microsoft_teams_tenant_id", + "asana_client_id", + "asana_client_secret", + "trello_api_key", + "trello_api_secret", + "gitlab_client_id", + "gitlab_client_secret", + "gitlab_instance_url", + "quickbooks_client_id", + "quickbooks_client_secret", + "xero_client_id", + "xero_client_secret", + ]: + if col in settings_cols: + set_parts.append(f"{col} = ''") + + if set_parts: + where_col = ( + "google_calendar_client_id" + if "google_calendar_client_id" in settings_cols + else set_parts[0].split(" = ")[0] + ) + op.execute( + f"UPDATE settings SET {', '.join(set_parts)} WHERE {where_col} IS NULL" + ) def downgrade(): """Remove integration credential columns from settings table""" - with op.batch_alter_table('settings', schema=None) as batch_op: - batch_op.drop_column('xero_client_secret') - batch_op.drop_column('xero_client_id') - batch_op.drop_column('quickbooks_client_secret') - batch_op.drop_column('quickbooks_client_id') - batch_op.drop_column('gitlab_instance_url') - batch_op.drop_column('gitlab_client_secret') - batch_op.drop_column('gitlab_client_id') - batch_op.drop_column('trello_api_secret') - batch_op.drop_column('trello_api_key') - batch_op.drop_column('asana_client_secret') - batch_op.drop_column('asana_client_id') - batch_op.drop_column('microsoft_teams_tenant_id') - batch_op.drop_column('microsoft_teams_client_secret') - batch_op.drop_column('microsoft_teams_client_id') - batch_op.drop_column('outlook_calendar_tenant_id') - batch_op.drop_column('outlook_calendar_client_secret') - batch_op.drop_column('outlook_calendar_client_id') - batch_op.drop_column('google_calendar_client_secret') - batch_op.drop_column('google_calendar_client_id') + bind = op.get_bind() + inspector = sa.inspect(bind) + if not _has_table(inspector, "settings"): + return + + settings_cols = {c["name"] for c in inspector.get_columns("settings")} + + with op.batch_alter_table('settings', schema=None) as batch_op: + for col in [ + 'xero_client_secret', + 'xero_client_id', + 'quickbooks_client_secret', + 'quickbooks_client_id', + 'gitlab_instance_url', + 'gitlab_client_secret', + 'gitlab_client_id', + 'trello_api_secret', + 'trello_api_key', + 'asana_client_secret', + 'asana_client_id', + 'microsoft_teams_tenant_id', + 'microsoft_teams_client_secret', + 'microsoft_teams_client_id', + 'outlook_calendar_tenant_id', + 'outlook_calendar_client_secret', + 'outlook_calendar_client_id', + 'google_calendar_client_secret', + 'google_calendar_client_id', + ]: + if col in settings_cols: + try: + batch_op.drop_column(col) + except Exception: + pass diff --git a/setup.py b/setup.py index d3927c0..0b6aa19 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ from setuptools import setup, find_packages setup( name='timetracker', - version='4.8.11', + version='4.8.12', packages=find_packages(), include_package_data=True, install_requires=[