"""Opt-in integration and database runtime switch. No implicit activation.""" import json import logging from app.core.config import settings from app.core.database import execute_query_single, execute_query, execute_update logger = logging.getLogger(__name__) def customer_enabled(customer_id,cursor=None,fallback_ids=None): """Saved true/false overrides env preference; missing table preserves legacy config.""" if customer_id is None: return bool(settings.WORKFLOWS_INTERNAL_ENABLED) fallback=settings.WORKFLOWS_CUSTOMER_IDS if fallback_ids is None else fallback_ids inherited=str(customer_id) in {str(value) for value in fallback} if cursor is None: present=execute_query_single("SELECT to_regclass('public.bmc_workflow_customer_activation') AS name") if not present or not present.get('name'): return inherited row=execute_query_single('SELECT enabled FROM bmc_workflow_customer_activation WHERE customer_id=%s FOR SHARE',(int(customer_id),)) else: cursor.execute("SELECT to_regclass('public.bmc_workflow_customer_activation') AS name") present=cursor.fetchone() if not present or not present.get('name'): return inherited cursor.execute('SELECT enabled FROM bmc_workflow_customer_activation WHERE customer_id=%s FOR SHARE',(int(customer_id),)) row=cursor.fetchone() return bool(row['enabled']) if row else inherited def effective_customer_ids(cursor=None): ids={int(value) for value in settings.WORKFLOWS_CUSTOMER_IDS} if cursor is None: present=execute_query_single("SELECT to_regclass('public.bmc_workflow_customer_activation') AS name") rows=execute_query('SELECT customer_id,enabled FROM bmc_workflow_customer_activation') if present and present.get('name') else [] else: cursor.execute("SELECT to_regclass('public.bmc_workflow_customer_activation') AS name") present=cursor.fetchone() if present and present.get('name'): cursor.execute('SELECT customer_id,enabled FROM bmc_workflow_customer_activation') rows=cursor.fetchall() else: rows=[] for row in rows: if row['enabled']: ids.add(int(row['customer_id'])) else: ids.discard(int(row['customer_id'])) return sorted(ids) def active_for_customer(customer_id): if not settings.WORKFLOWS_ENABLED or settings.WORKFLOWS_MODE != 'active': return False if customer_id is None: return settings.WORKFLOWS_INTERNAL_ENABLED return customer_enabled(customer_id) def configure_runtime(): """Synchronize the opt-in switch before workers start; tolerate an unmigrated database when disabled.""" # A migrated database may retain its enabled flag after a previous run. exists = execute_query_single("SELECT to_regclass('public.bmc_workflow_runtime') AS name") if not exists or not exists.get('name'): if settings.WORKFLOWS_ENABLED: raise RuntimeError('Workflow migrations must be applied before enabling workflows') return if settings.WORKFLOWS_MODE not in {'shadow', 'active'}: raise RuntimeError('WORKFLOWS_MODE must be shadow or active') execute_update("UPDATE bmc_workflow_runtime SET enabled=%s, mode=%s, customer_ids=%s::jsonb, internal_enabled=%s, updated_at=now() WHERE id=1", (settings.WORKFLOWS_ENABLED, settings.WORKFLOWS_MODE, json.dumps(settings.WORKFLOWS_CUSTOMER_IDS), settings.WORKFLOWS_INTERNAL_ENABLED)) def run_workflow_jobs(): if not settings.WORKFLOWS_ENABLED: return from .worker import process_pending_events try: return process_pending_events(limit=20, mode=settings.WORKFLOWS_MODE) except Exception: logger.exception('Workflow queue processing failed')