2026-10-08 18:00:47 +02:00
|
|
|
"""Opt-in integration and database runtime switch. No implicit activation."""
|
|
|
|
|
import json
|
|
|
|
|
import logging
|
|
|
|
|
from app.core.config import settings
|
2026-10-08 21:09:25 +02:00
|
|
|
from app.core.database import execute_query_single, execute_query, execute_update
|
2026-10-08 18:00:47 +02:00
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2026-10-08 21:09:25 +02:00
|
|
|
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)
|
2026-10-08 18:00:47 +02:00
|
|
|
|
|
|
|
|
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
|
2026-10-08 21:09:25 +02:00
|
|
|
return customer_enabled(customer_id)
|
2026-10-08 18:00:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
2026-10-08 21:09:25 +02:00
|
|
|
return process_pending_events(limit=20, mode=settings.WORKFLOWS_MODE)
|
2026-10-08 18:00:47 +02:00
|
|
|
except Exception:
|
|
|
|
|
logger.exception('Workflow queue processing failed')
|