@@ -200,6 +200,33 @@ def _profile_request_note_metadata(
200200 }
201201
202202
203+ def _profile_request_followup_metadata (
204+ source_profile : dict ,
205+ * ,
206+ note : str ,
207+ desired_schedule : str = "" ,
208+ desired_watch_target : str = "" ,
209+ preserve_profile_metadata : bool = False ,
210+ ) -> dict :
211+ source_metadata = source_profile .get ("metadata_json" ) or {}
212+ metadata = (
213+ dict (source_metadata )
214+ if preserve_profile_metadata and isinstance (source_metadata , dict )
215+ else {}
216+ )
217+ metadata .pop ("request_note" , None )
218+ desired_schedule = desired_schedule .strip ()
219+ desired_watch_target = desired_watch_target .strip ()
220+ if desired_schedule :
221+ metadata ["desired_schedule" ] = desired_schedule
222+ if desired_watch_target :
223+ metadata ["desired_watch_target" ] = desired_watch_target
224+ request_note = note .strip ()
225+ if request_note :
226+ metadata ["request_note" ] = request_note
227+ return metadata
228+
229+
203230def _parse_trigger_definition_form ():
204231 """Return a raw trigger definition object from the submitted admin form."""
205232 actor = session .get ("user_email" , "" )
@@ -401,7 +428,6 @@ def _profile_request_change_rows(profile_request, source_profile):
401428 metadata_specs = [
402429 ("Desired Schedule" , "desired_schedule" ),
403430 ("Desired Watch Target" , "desired_watch_target" ),
404- ("Note" , "note" ),
405431 ]
406432 for label , key in metadata_specs :
407433 current = current_metadata .get (key )
@@ -415,6 +441,18 @@ def _profile_request_change_rows(profile_request, source_profile):
415441 return rows
416442
417443
444+ def _profile_request_has_effective_changes (requested_profile , source_profile ):
445+ return bool (
446+ _profile_request_change_rows (
447+ {
448+ "request_type" : "change_profile" ,
449+ "requested_profile" : requested_profile ,
450+ },
451+ source_profile ,
452+ )
453+ )
454+
455+
418456def _profile_request_diff_key (value ):
419457 if isinstance (value , (list , tuple )):
420458 return tuple (str (item ).strip () for item in value if str (item ).strip ())
@@ -920,14 +958,23 @@ def submit_execution_profile_followup_request():
920958
921959 requested_profile = dict (source_profile )
922960 requested_profile ["status" ] = "draft"
923- requested_profile ["metadata_json" ] = {
924- ** (source_profile .get ("metadata_json" ) or {}),
925- ** _profile_request_note_metadata (
926- note ,
927- desired_schedule = desired_schedule ,
928- desired_watch_target = desired_watch_target ,
929- ),
930- }
961+ requested_profile ["metadata_json" ] = _profile_request_followup_metadata (
962+ source_profile ,
963+ note = note ,
964+ desired_schedule = desired_schedule ,
965+ desired_watch_target = desired_watch_target ,
966+ preserve_profile_metadata = request_type == "change_profile" ,
967+ )
968+ if request_type == "change_profile" and not _profile_request_has_effective_changes (
969+ requested_profile ,
970+ source_profile ,
971+ ):
972+ flash (
973+ "Execution profile follow-up request was not created: "
974+ "change request needs a schedule, watch target, or profile field change; "
975+ "note-only changes are not actionable"
976+ )
977+ return redirect (url_for ("profile_requests.profile_requests" ))
931978 try :
932979 request_id = store .create_profile_request (
933980 requested_profile = requested_profile ,
@@ -975,6 +1022,44 @@ def resubmit_execution_profile_request(request_id):
9751022 return redirect (url_for ("profile_requests.profile_requests" ))
9761023
9771024 raw_profile , errors = _parse_execution_profile_request_form ()
1025+ source_profile = None
1026+ source_profile_id = profile_request .get ("source_profile_id" ) or ""
1027+ if source_profile_id :
1028+ source_profile = next (
1029+ (
1030+ profile
1031+ for profile in store .list_profiles ()
1032+ if profile ["id" ] == source_profile_id
1033+ ),
1034+ None ,
1035+ )
1036+ if not errors and source_profile and profile_request .get ("request_type" ) in {
1037+ "change_profile" ,
1038+ "pause_profile" ,
1039+ "resume_profile" ,
1040+ "retire_profile" ,
1041+ }:
1042+ submitted_metadata = raw_profile .get ("metadata_json" )
1043+ submitted_metadata = submitted_metadata if isinstance (submitted_metadata , dict ) else {}
1044+ raw_profile ["metadata_json" ] = _profile_request_followup_metadata (
1045+ source_profile ,
1046+ note = str (
1047+ submitted_metadata .get ("note" )
1048+ or submitted_metadata .get ("request_note" )
1049+ or ""
1050+ ),
1051+ desired_schedule = str (submitted_metadata .get ("desired_schedule" ) or "" ),
1052+ desired_watch_target = str (submitted_metadata .get ("desired_watch_target" ) or "" ),
1053+ preserve_profile_metadata = profile_request .get ("request_type" ) == "change_profile" ,
1054+ )
1055+ if not errors and profile_request .get ("request_type" ) == "change_profile" :
1056+ if not source_profile :
1057+ errors .append ("source profile was not found" )
1058+ elif not _profile_request_has_effective_changes (raw_profile , source_profile ):
1059+ errors .append (
1060+ "change request needs a schedule, watch target, or profile field change; "
1061+ "note-only changes are not actionable"
1062+ )
9781063 if errors :
9791064 ok = False
9801065 else :
0 commit comments