Coverage for scripts / decline_request.py: 17%
63 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-25 18:08 -0500
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-25 18:08 -0500
1#!/usr/bin/env python3
2"""
3Decline an approval request for a JSM request.
5This script declines pending approval requests in JSM workflows,
6typically used for change management and approval processes.
7"""
9import sys
10import argparse
11from pathlib import Path
12from datetime import datetime
14# Add shared lib to path
15sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / 'shared' / 'scripts' / 'lib'))
17from config_manager import get_jira_client
18from error_handler import handle_errors
21def get_jira_client(profile=None):
22 """Get JIRA client (overridable for testing)."""
23 from config_manager import get_jira_client as _get_client
24 return _get_client(profile)
27@handle_errors
28def main(args=None):
29 """Main function."""
30 parser = argparse.ArgumentParser(
31 description='Decline JSM approval request',
32 formatter_class=argparse.RawDescriptionHelpFormatter,
33 epilog="""
34Examples:
35 # Decline single approval
36 %(prog)s REQ-123 --approval-id 10050
38 # Skip confirmation
39 %(prog)s REQ-123 --approval-id 10050 --yes
41 # Dry run (show what would be declined)
42 %(prog)s REQ-123 --approval-id 10050 --dry-run
43 """
44 )
46 parser.add_argument('issue_key',
47 help='Request key (e.g., REQ-123)')
48 parser.add_argument('--approval-id', required=True, action='append',
49 help='Approval ID to decline (can specify multiple times)')
50 parser.add_argument('--yes', '-y', action='store_true',
51 help='Skip confirmation prompt')
52 parser.add_argument('--dry-run', action='store_true',
53 help='Show what would be declined without making changes')
54 parser.add_argument('--profile',
55 help='JIRA profile to use')
57 parsed_args = parser.parse_args(args)
59 # Get JIRA client
60 jira = get_jira_client(parsed_args.profile)
62 # Process each approval ID
63 for approval_id in parsed_args.approval_id:
64 # Get approval details for confirmation
65 try:
66 approval = jira.get_request_approval(parsed_args.issue_key, approval_id)
67 except:
68 print(f"\nError: Could not get approval {approval_id}")
69 continue
71 approval_name = approval.get('name', 'Unknown')
72 approvers = approval.get('approvers', [])
73 approvers_str = ', '.join([a.get('displayName', 'Unknown') for a in approvers])
74 created = approval.get('createdDate', '')
76 # Format created date
77 try:
78 created_dt = datetime.fromisoformat(created.replace('Z', '+00:00'))
79 created_str = created_dt.strftime('%Y-%m-%d %H:%M')
80 except:
81 created_str = created[:16] if created else 'Unknown'
83 # Show approval details
84 print(f"\nDecline approval for {parsed_args.issue_key}?")
85 print(f"\nApproval ID: {approval_id}")
86 print(f"Name: {approval_name}")
87 print(f"Approvers: {approvers_str}")
88 print(f"Created: {created_str}")
89 print("\nWarning: Declining this approval may prevent the change from proceeding.")
91 if parsed_args.dry_run:
92 print(f"\n[DRY RUN] Would decline approval {approval_id} for {parsed_args.issue_key}")
93 continue
95 # Confirm unless --yes
96 if not parsed_args.yes:
97 confirm = input("\nType 'yes' to confirm: ")
98 if confirm.lower() != 'yes':
99 print("Cancelled.")
100 continue
102 # Decline the approval
103 result = jira.answer_approval(parsed_args.issue_key, approval_id, 'decline')
105 completed = result.get('completedDate', '')
106 try:
107 completed_dt = datetime.fromisoformat(completed.replace('Z', '+00:00'))
108 completed_str = completed_dt.strftime('%Y-%m-%d %H:%M')
109 except:
110 completed_str = completed[:16] if completed else 'Just now'
112 print(f"\nApproval {approval_id} DECLINED for {parsed_args.issue_key}.")
113 print(f"\nStatus: decline")
114 print(f"Completed: {completed_str}")
115 print("Reason: Change request declined by approver")
117 return 0
120if __name__ == '__main__':
121 sys.exit(main())