Fix test failures and workload/websocket behavior
This commit is contained in:
@@ -4,7 +4,7 @@ from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.models import User, Project, Trigger, TriggerLog
|
||||
from app.models import User, Project, Trigger, TriggerLog, CustomField
|
||||
from app.schemas.trigger import (
|
||||
TriggerCreate, TriggerUpdate, TriggerResponse, TriggerListResponse,
|
||||
TriggerLogResponse, TriggerLogListResponse, TriggerUserInfo
|
||||
@@ -16,6 +16,10 @@ from app.services.action_executor import ActionValidationError
|
||||
|
||||
router = APIRouter(tags=["triggers"])
|
||||
|
||||
FIELD_CHANGE_FIELDS = {"status_id", "assignee_id", "priority", "start_date", "due_date", "custom_fields"}
|
||||
FIELD_CHANGE_OPERATORS = {"equals", "not_equals", "changed_to", "changed_from", "before", "after", "in"}
|
||||
DATE_FIELDS = {"start_date", "due_date"}
|
||||
|
||||
|
||||
def trigger_to_response(trigger: Trigger) -> TriggerResponse:
|
||||
"""Convert Trigger model to TriggerResponse."""
|
||||
@@ -39,6 +43,96 @@ def trigger_to_response(trigger: Trigger) -> TriggerResponse:
|
||||
)
|
||||
|
||||
|
||||
def _validate_field_change_conditions(conditions, project_id: str, db: Session) -> None:
|
||||
rules = []
|
||||
if conditions.rules is not None:
|
||||
if conditions.logic != "and":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Composite conditions only support logic 'and'",
|
||||
)
|
||||
if not conditions.rules:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Composite conditions require at least one rule",
|
||||
)
|
||||
rules = conditions.rules
|
||||
else:
|
||||
if not conditions.field:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Field is required for field_change triggers",
|
||||
)
|
||||
rules = [conditions]
|
||||
|
||||
for rule in rules:
|
||||
field = rule.field
|
||||
operator = rule.operator
|
||||
value = rule.value
|
||||
field_id = rule.field_id or getattr(conditions, "field_id", None)
|
||||
|
||||
if field not in FIELD_CHANGE_FIELDS:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid condition field. Must be 'status_id', 'assignee_id', 'priority', 'start_date', 'due_date', or 'custom_fields'",
|
||||
)
|
||||
if not operator:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Operator is required for field_change triggers",
|
||||
)
|
||||
if operator not in FIELD_CHANGE_OPERATORS:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid operator. Must be 'equals', 'not_equals', 'changed_to', 'changed_from', 'before', 'after', or 'in'",
|
||||
)
|
||||
if value is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Condition value is required for field_change triggers",
|
||||
)
|
||||
|
||||
field_type = None
|
||||
if field in DATE_FIELDS:
|
||||
field_type = "date"
|
||||
elif field == "custom_fields":
|
||||
if not field_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Custom field ID is required when field is custom_fields",
|
||||
)
|
||||
custom_field = db.query(CustomField).filter(
|
||||
CustomField.id == field_id,
|
||||
CustomField.project_id == project_id,
|
||||
).first()
|
||||
if not custom_field:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Custom field not found in this project",
|
||||
)
|
||||
field_type = custom_field.field_type
|
||||
|
||||
if operator in {"before", "after"}:
|
||||
if field_type not in {"date", "number", "formula"}:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Operator 'before/after' is only valid for date or number fields",
|
||||
)
|
||||
|
||||
if operator == "in":
|
||||
if field_type == "date":
|
||||
if not isinstance(value, dict) or "start" not in value or "end" not in value:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Date 'in' operator requires a range with start and end",
|
||||
)
|
||||
elif not isinstance(value, list):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Operator 'in' requires a list of values",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/api/projects/{project_id}/triggers", response_model=TriggerResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_trigger(
|
||||
project_id: str,
|
||||
@@ -71,27 +165,7 @@ async def create_trigger(
|
||||
|
||||
# Validate conditions based on trigger type
|
||||
if trigger_data.trigger_type == "field_change":
|
||||
# Validate field_change conditions
|
||||
if not trigger_data.conditions.field:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Field is required for field_change triggers",
|
||||
)
|
||||
if trigger_data.conditions.field not in ["status_id", "assignee_id", "priority"]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid condition field. Must be 'status_id', 'assignee_id', or 'priority'",
|
||||
)
|
||||
if not trigger_data.conditions.operator:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Operator is required for field_change triggers",
|
||||
)
|
||||
if trigger_data.conditions.operator not in ["equals", "not_equals", "changed_to", "changed_from"]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid operator. Must be 'equals', 'not_equals', 'changed_to', or 'changed_from'",
|
||||
)
|
||||
_validate_field_change_conditions(trigger_data.conditions, project_id, db)
|
||||
elif trigger_data.trigger_type == "schedule":
|
||||
# Validate schedule conditions
|
||||
has_cron = trigger_data.conditions.cron_expression is not None
|
||||
@@ -234,11 +308,7 @@ async def update_trigger(
|
||||
if trigger_data.conditions is not None:
|
||||
# Validate conditions based on trigger type
|
||||
if trigger.trigger_type == "field_change":
|
||||
if trigger_data.conditions.field and trigger_data.conditions.field not in ["status_id", "assignee_id", "priority"]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid condition field",
|
||||
)
|
||||
_validate_field_change_conditions(trigger_data.conditions, trigger.project_id, db)
|
||||
elif trigger.trigger_type == "schedule":
|
||||
# Validate cron expression if provided
|
||||
if trigger_data.conditions.cron_expression is not None:
|
||||
|
||||
Reference in New Issue
Block a user