@@ -14,16 +14,22 @@ class Command(BaseCommand):
1414
1515 def add_arguments (self , parser ):
1616 parser .add_argument ('credential_type_id' , type = str )
17+ parser .add_argument (
18+ '--force' ,
19+ action = 'store_true' ,
20+ help = 'Skip confirmation prompt and proceed with deletion'
21+ )
1722
1823 def handle (self , * args , ** options ):
1924 credential_type_id = options ['credential_type_id' ]
2025
21- # Track affected topics for targeted reindexing
22- affected_topics = []
26+ # Pre-flight check and confirmation before opening Solr queue
27+ affected_topics = self . _check_and_confirm_deletion ( * args , ** options )
2328
29+ # If we get here, user confirmed deletion - proceed with operations
2430 queue = SolrQueue ()
2531 with queue :
26- affected_topics = self .delete_credential_type ( * args , ** options )
32+ self ._perform_deletion ( affected_topics , * args , ** options )
2733
2834 # Wait for queue to drain while still in context
2935 eject = 20
@@ -45,10 +51,8 @@ def handle(self, *args, **options):
4551 self .stdout .write ("Performing targeted search index refresh ..." )
4652 self ._refresh_affected_indexes (credential_type_id , affected_topics )
4753
48- def delete_credential_type (self , * args , ** options ):
49- start_time = time .perf_counter ()
50-
51- # get Credential Type ID from input parameters
54+ def _check_and_confirm_deletion (self , * args , ** options ):
55+ """Check credential type exists and get user confirmation"""
5256 credential_type_id = options ['credential_type_id' ]
5357 self .stdout .write ("Deleting credential_type_id: " + credential_type_id )
5458
@@ -60,7 +64,7 @@ def delete_credential_type(self, *args, **options):
6064 credential_type = CredentialType .objects .get (id = credential_type_id )
6165 except CredentialType .DoesNotExist :
6266 self .stdout .write (" ... credential_type_id not found in OrgBook." )
63- return list ( affected_topics )
67+ raise SystemExit ( 1 )
6468
6569 # Find all credentials for this credential type
6670 credentials = Credential .objects .filter (
@@ -71,10 +75,92 @@ def delete_credential_type(self, *args, **options):
7175 if credentials .exists ():
7276 topic_ids = set (credentials .values_list ('topic_id' , flat = True ))
7377 affected_topics .update (topic_ids )
78+
79+ # Display information about the operation
80+ self .stdout .write ("\n " + "=" * 60 )
81+ self .stdout .write ("CREDENTIAL TYPE DELETION CONFIRMATION" )
82+ self .stdout .write ("=" * 60 )
83+
84+ # Use description if available, otherwise fall back to raw_data.type
85+ display_name = credential_type .description
86+ if (not display_name and hasattr (credential_type , 'raw_data' ) and
87+ credential_type .raw_data ):
88+ try :
89+ import json
90+
91+ # Parse JSON string to get the type attribute
92+ raw_data_obj = json .loads (credential_type .raw_data )
93+ display_name = raw_data_obj .get ('type' )
94+ except (json .JSONDecodeError , AttributeError , TypeError , KeyError ):
95+ display_name = None
96+
97+ # Final fallback to ID if no display name found
98+ if not display_name :
99+ display_name = f"Credential Type ID { credential_type_id } "
100+
101+ self .stdout .write (f"Credential Type: { display_name } " )
102+ self .stdout .write (f"ID: { credential_type_id } " )
103+ self .stdout .write (
104+ f"Found { credentials .count ()} credentials to delete"
105+ )
106+ self .stdout .write (f"Topics affected: { len (affected_topics )} " )
107+ self .stdout .write ("=" * 60 )
108+
109+ if affected_topics :
110+ self .stdout .write (
111+ f"WARNING: This operation will affect "
112+ f"{ len (affected_topics )} topics and trigger search index "
113+ f"refresh."
114+ )
115+ else :
74116 self .stdout .write (
75- f" ... found { len (topic_ids )} topics affected by "
76- f"credential deletion"
117+ "No topics will be affected by this operation."
77118 )
119+
120+ self .stdout .write (
121+ "\n This action cannot be undone. All credentials of this type "
122+ "will be permanently deleted."
123+ )
124+
125+ # Ask for user confirmation unless --force flag is used
126+ if not options .get ('force' , False ):
127+ try :
128+ confirmation = input (
129+ "\n Do you want to continue? (y/N): "
130+ ).strip ()
131+ except (EOFError , KeyboardInterrupt ):
132+ self .stdout .write ("\n \n Operation cancelled by user." )
133+ raise SystemExit (0 )
134+
135+ if confirmation .lower () not in ['y' , 'yes' ]:
136+ self .stdout .write (
137+ "Operation cancelled. No changes were made."
138+ )
139+ raise SystemExit (0 )
140+ else :
141+ self .stdout .write (
142+ "\n --force flag provided, skipping confirmation."
143+ )
144+
145+ self .stdout .write ("\n Proceeding with credential type deletion..." )
146+ self .stdout .write ("=" * 60 + "\n " )
147+
148+ return list (affected_topics )
149+
150+ def _perform_deletion (self , affected_topics , * args , ** options ):
151+ """Perform the actual credential type deletion"""
152+ start_time = time .perf_counter ()
153+ credential_type_id = options ['credential_type_id' ]
154+
155+ # Re-fetch the credential type and related data (since confirmation
156+ # happened earlier)
157+ credential_type = CredentialType .objects .get (id = credential_type_id )
158+ credentials = Credential .objects .filter (
159+ credential_type = credential_type
160+ )
161+
162+ # Convert to set for efficient updates (we received it as a list)
163+ affected_topics = set (affected_topics )
78164
79165 # delete credentials from wallet first
80166 if credentials .exists ():
0 commit comments