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

1#!/usr/bin/env python3 

2""" 

3Decline an approval request for a JSM request. 

4 

5This script declines pending approval requests in JSM workflows, 

6typically used for change management and approval processes. 

7""" 

8 

9import sys 

10import argparse 

11from pathlib import Path 

12from datetime import datetime 

13 

14# Add shared lib to path 

15sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / 'shared' / 'scripts' / 'lib')) 

16 

17from config_manager import get_jira_client 

18from error_handler import handle_errors 

19 

20 

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) 

25 

26 

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 

37 

38 # Skip confirmation 

39 %(prog)s REQ-123 --approval-id 10050 --yes 

40 

41 # Dry run (show what would be declined) 

42 %(prog)s REQ-123 --approval-id 10050 --dry-run 

43 """ 

44 ) 

45 

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') 

56 

57 parsed_args = parser.parse_args(args) 

58 

59 # Get JIRA client 

60 jira = get_jira_client(parsed_args.profile) 

61 

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 

70 

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', '') 

75 

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' 

82 

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.") 

90 

91 if parsed_args.dry_run: 

92 print(f"\n[DRY RUN] Would decline approval {approval_id} for {parsed_args.issue_key}") 

93 continue 

94 

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 

101 

102 # Decline the approval 

103 result = jira.answer_approval(parsed_args.issue_key, approval_id, 'decline') 

104 

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' 

111 

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") 

116 

117 return 0 

118 

119 

120if __name__ == '__main__': 

121 sys.exit(main())