Delete a feature and clean up references in other features' dependencies. When a feature is deleted, any other features that depend on it will have that dependency removed from their dependencies list. This prevents orphaned dependencies that would permanently block features.
(project_name: str, feature_id: int)
| 475 | |
| 476 | @router.delete("/{feature_id}") |
| 477 | async def delete_feature(project_name: str, feature_id: int): |
| 478 | """Delete a feature and clean up references in other features' dependencies. |
| 479 | |
| 480 | When a feature is deleted, any other features that depend on it will have |
| 481 | that dependency removed from their dependencies list. This prevents orphaned |
| 482 | dependencies that would permanently block features. |
| 483 | """ |
| 484 | project_name = validate_project_name(project_name) |
| 485 | project_dir = _get_project_path(project_name) |
| 486 | |
| 487 | if not project_dir: |
| 488 | raise HTTPException(status_code=404, detail=f"Project '{project_name}' not found in registry") |
| 489 | |
| 490 | if not project_dir.exists(): |
| 491 | raise HTTPException(status_code=404, detail="Project directory not found") |
| 492 | |
| 493 | _, Feature = _get_db_classes() |
| 494 | |
| 495 | try: |
| 496 | with get_db_session(project_dir) as session: |
| 497 | feature = session.query(Feature).filter(Feature.id == feature_id).first() |
| 498 | |
| 499 | if not feature: |
| 500 | raise HTTPException(status_code=404, detail=f"Feature {feature_id} not found") |
| 501 | |
| 502 | # Clean up dependency references in other features |
| 503 | # This prevents orphaned dependencies that would block features forever |
| 504 | affected_features = [] |
| 505 | for f in session.query(Feature).all(): |
| 506 | if f.dependencies and feature_id in f.dependencies: |
| 507 | # Remove the deleted feature from this feature's dependencies |
| 508 | deps = [d for d in f.dependencies if d != feature_id] |
| 509 | f.dependencies = deps if deps else None |
| 510 | affected_features.append(f.id) |
| 511 | |
| 512 | session.delete(feature) |
| 513 | session.commit() |
| 514 | |
| 515 | message = f"Feature {feature_id} deleted" |
| 516 | if affected_features: |
| 517 | message += f". Removed from dependencies of features: {affected_features}" |
| 518 | |
| 519 | return {"success": True, "message": message, "affected_features": affected_features} |
| 520 | except HTTPException: |
| 521 | raise |
| 522 | except Exception: |
| 523 | logger.exception("Failed to delete feature") |
| 524 | raise HTTPException(status_code=500, detail="Failed to delete feature") |
| 525 | |
| 526 | |
| 527 | @router.patch("/{feature_id}/skip") |
nothing calls this directly
no test coverage detected