@@ -109,6 +109,39 @@ def addContentToDictMappings(self, content: SecurityContentObject):
109109 self .uuid_to_content_map [content .id ] = content
110110
111111
112+ class Colors :
113+ HEADER = "\033 [95m"
114+ BLUE = "\033 [94m"
115+ CYAN = "\033 [96m"
116+ GREEN = "\033 [92m"
117+ YELLOW = "\033 [93m"
118+ RED = "\033 [91m"
119+ BOLD = "\033 [1m"
120+ UNDERLINE = "\033 [4m"
121+ END = "\033 [0m"
122+ MAGENTA = "\033 [35m"
123+ BRIGHT_MAGENTA = "\033 [95m"
124+
125+ # Add fallback symbols for Windows
126+ CHECK_MARK = "✓" if sys .platform != "win32" else "*"
127+ WARNING = "⚠️" if sys .platform != "win32" else "!"
128+ ERROR = "❌" if sys .platform != "win32" else "X"
129+ ARROW = "🎯" if sys .platform != "win32" else ">"
130+ TOOLS = "🛠️" if sys .platform != "win32" else "#"
131+ DOCS = "📚" if sys .platform != "win32" else "?"
132+ BULB = "💡" if sys .platform != "win32" else "i"
133+ SEARCH = "🔍" if sys .platform != "win32" else "@"
134+ ZAP = "⚡" if sys .platform != "win32" else "!"
135+
136+
137+ class ValidationFailedError (Exception ):
138+ """Custom exception for validation failures that already have formatted output."""
139+
140+ def __init__ (self , message : str ):
141+ self .message = message
142+ super ().__init__ (message )
143+
144+
112145class Director :
113146 input_dto : validate
114147 output_dto : DirectorOutputDto
@@ -268,18 +301,91 @@ def createSecurityContent(
268301 end = "" ,
269302 flush = True ,
270303 )
271- print ("Done!" )
272304
273305 if len (validation_errors ) > 0 :
274- errors_string = "\n \n " .join (
275- [
276- f"File: { e_tuple [0 ]} \n Error: { str (e_tuple [1 ])} "
277- for e_tuple in validation_errors
278- ]
306+ print ("\n " ) # Clean separation
307+ print (f"{ Colors .BOLD } { Colors .BRIGHT_MAGENTA } ╔{ '═' * 60 } ╗{ Colors .END } " )
308+ print (
309+ f"{ Colors .BOLD } { Colors .BRIGHT_MAGENTA } ║{ Colors .BLUE } { f'{ Colors .SEARCH } Content Validation Summary' :^60} { Colors .BRIGHT_MAGENTA } ║{ Colors .END } "
310+ )
311+ print (f"{ Colors .BOLD } { Colors .BRIGHT_MAGENTA } ╚{ '═' * 60 } ╝{ Colors .END } \n " )
312+
313+ print (
314+ f"{ Colors .BOLD } { Colors .GREEN } ✨ Validation Completed{ Colors .END } – Issues detected in { Colors .RED } { Colors .BOLD } { len (validation_errors )} { Colors .END } files.\n "
315+ )
316+
317+ for index , entry in enumerate (validation_errors , 1 ):
318+ file_path , error = entry
319+ width = max (70 , len (str (file_path )) + 15 )
320+
321+ # File header with numbered emoji
322+ number_emoji = f"{ index } ️⃣"
323+ print (f"{ Colors .YELLOW } ┏{ '━' * width } ┓{ Colors .END } " )
324+ print (
325+ f"{ Colors .YELLOW } ┃{ Colors .BOLD } { number_emoji } File: { Colors .CYAN } { file_path } { Colors .END } { ' ' * (width - len (str (file_path )) - 12 )} { Colors .YELLOW } ┃{ Colors .END } "
326+ )
327+ print (f"{ Colors .YELLOW } ┗{ '━' * width } ┛{ Colors .END } " )
328+
329+ print (
330+ f" { Colors .RED } { Colors .BOLD } { Colors .ZAP } Validation Issues:{ Colors .END } "
331+ )
332+
333+ if isinstance (error , ValidationError ):
334+ for err in error .errors ():
335+ error_msg = err .get ("msg" , "" )
336+ if "https://errors.pydantic.dev" in error_msg :
337+ continue
338+
339+ # Clean error categorization
340+ if "Field required" in error_msg :
341+ print (
342+ f" { Colors .YELLOW } { Colors .WARNING } Field Required: { err .get ('loc' , ['' ])[0 ]} { Colors .END } "
343+ )
344+ elif "Input should be" in error_msg :
345+ print (
346+ f" { Colors .MAGENTA } { Colors .ARROW } Invalid Value for { err .get ('loc' , ['' ])[0 ]} { Colors .END } "
347+ )
348+ if err .get ("ctx" , {}).get ("expected" , None ) is not None :
349+ print (
350+ f" Valid options: { err .get ('ctx' , {}).get ('expected' , None )} "
351+ )
352+ elif "Extra inputs" in error_msg :
353+ print (
354+ f" { Colors .BLUE } ❌ Unexpected Field: { err .get ('loc' , ['' ])[0 ]} { Colors .END } "
355+ )
356+ elif "Failed to find" in error_msg :
357+ print (
358+ f" { Colors .RED } 🔍 Missing Reference: { error_msg } { Colors .END } "
359+ )
360+ else :
361+ print (f" { Colors .RED } ❌ { error_msg } { Colors .END } " )
362+ else :
363+ print (f" { Colors .RED } ❌ { str (error )} { Colors .END } " )
364+ print ("" )
365+
366+ # Clean footer with next steps
367+ max_width = max (60 , max (len (str (e [0 ])) + 15 for e in validation_errors ))
368+ print (f"{ Colors .BOLD } { Colors .CYAN } ╔{ '═' * max_width } ╗{ Colors .END } " )
369+ print (
370+ f"{ Colors .BOLD } { Colors .CYAN } ║{ Colors .BLUE } { '🎯 Next Steps' :^{max_width }} { Colors .CYAN } ║{ Colors .END } "
371+ )
372+ print (f"{ Colors .BOLD } { Colors .CYAN } ╚{ '═' * max_width } ╝{ Colors .END } \n " )
373+
374+ print (
375+ f"{ Colors .GREEN } { Colors .TOOLS } Fix the validation issues in the listed files{ Colors .END } "
376+ )
377+ print (
378+ f"{ Colors .YELLOW } { Colors .DOCS } Check the documentation: { Colors .UNDERLINE } https://github.com/splunk/contentctl{ Colors .END } "
279379 )
280- # print(f"The following {len(validation_errors)} error(s) were found during validation:\n\n{errors_string}\n\nVALIDATION FAILED")
281- # We quit after validation a single type/group of content because it can cause significant cascading errors in subsequent
282- # types of content (since they may import or otherwise use it)
283- raise Exception (
284- f"The following { len (validation_errors )} error(s) were found during validation:\n \n { errors_string } \n \n VALIDATION FAILED"
380+ print (
381+ f"{ Colors .BLUE } { Colors .BULB } Use --verbose for detailed error information{ Colors .END } \n "
285382 )
383+
384+ raise ValidationFailedError (
385+ f"Validation failed with { len (validation_errors )} error(s)"
386+ )
387+
388+ # Success case
389+ print (
390+ f"\r { f'{ contentCartegoryName } Progress' .rjust (23 )} : [{ progress_percent :3.0f} %]... { Colors .GREEN } { Colors .CHECK_MARK } Done!{ Colors .END } "
391+ )
0 commit comments