1+ package com .countyhospital .healthapi .common .dto ;
2+
3+ import java .time .LocalDateTime ;
4+ import java .util .ArrayList ;
5+ import java .util .List ;
6+ import java .util .Objects ;
7+
8+ import com .fasterxml .jackson .annotation .JsonFormat ;
9+ import com .fasterxml .jackson .annotation .JsonInclude ;
10+
11+ import io .swagger .v3 .oas .annotations .media .Schema ;
12+
13+ @ JsonInclude (JsonInclude .Include .NON_NULL )
14+ @ Schema (description = "Standard API error response structure" )
15+ public class ApiErrorResponse {
16+
17+ @ JsonFormat (shape = JsonFormat .Shape .STRING , pattern = "yyyy-MM-dd HH:mm:ss" )
18+ @ Schema (description = "Timestamp when the error occurred" , example = "2025-11-15 10:30:00" )
19+ private LocalDateTime timestamp ;
20+
21+ @ Schema (description = "HTTP status code" , example = "400" )
22+ private int status ;
23+
24+ @ Schema (description = "HTTP status description" , example = "Bad Request" )
25+ private String error ;
26+
27+ @ Schema (description = "Detailed error message" , example = "Validation failed" )
28+ private String message ;
29+
30+ @ Schema (description = "API path where the error occurred" , example = "/api/patients" )
31+ private String path ;
32+
33+ @ Schema (description = "List of field-specific validation errors" )
34+ private List <ValidationError > validationErrors ;
35+
36+ @ Schema (description = "Debug message for developers (only in non-production)" )
37+ private String debugMessage ;
38+
39+ // Constructors
40+ public ApiErrorResponse () {
41+ this .timestamp = LocalDateTime .now ();
42+ }
43+
44+ public ApiErrorResponse (int status , String error , String message , String path ) {
45+ this ();
46+ this .status = status ;
47+ this .error = error ;
48+ this .message = message ;
49+ this .path = path ;
50+ }
51+
52+ public ApiErrorResponse (int status , String error , String message , String path , String debugMessage ) {
53+ this (status , error , message , path );
54+ this .debugMessage = debugMessage ;
55+ }
56+
57+ // Builder pattern
58+ public static ApiErrorResponseBuilder builder () {
59+ return new ApiErrorResponseBuilder ();
60+ }
61+
62+ // Getters and setters
63+ public LocalDateTime getTimestamp () { return timestamp ; }
64+ public void setTimestamp (LocalDateTime timestamp ) { this .timestamp = timestamp ; }
65+
66+ public int getStatus () { return status ; }
67+ public void setStatus (int status ) { this .status = status ; }
68+
69+ public String getError () { return error ; }
70+ public void setError (String error ) { this .error = error ; }
71+
72+ public String getMessage () { return message ; }
73+ public void setMessage (String message ) { this .message = message ; }
74+
75+ public String getPath () { return path ; }
76+ public void setPath (String path ) { this .path = path ; }
77+
78+ public List <ValidationError > getValidationErrors () { return validationErrors ; }
79+ public void setValidationErrors (List <ValidationError > validationErrors ) { this .validationErrors = validationErrors ; }
80+
81+ public String getDebugMessage () { return debugMessage ; }
82+ public void setDebugMessage (String debugMessage ) { this .debugMessage = debugMessage ; }
83+
84+ // Validation error
85+ public void addValidationError (String field , String message ) {
86+ if (this .validationErrors == null ) {
87+ this .validationErrors = new ArrayList <>();
88+ }
89+ this .validationErrors .add (new ValidationError (field , message ));
90+ }
91+
92+ // Validation error inner class
93+ @ JsonInclude (JsonInclude .Include .NON_NULL )
94+ public static class ValidationError {
95+ @ Schema (description = "Field name that failed validation" , example = "email" )
96+ private final String field ;
97+
98+ @ Schema (description = "Validation error message" , example = "Email must be valid" )
99+ private final String message ;
100+
101+ @ Schema (description = "Rejected value" , example = "invalid-email" )
102+ private final Object rejectedValue ;
103+
104+ public ValidationError (String field , String message ) {
105+ this .field = field ;
106+ this .message = message ;
107+ this .rejectedValue = null ;
108+ }
109+
110+ public ValidationError (String field , String message , Object rejectedValue ) {
111+ this .field = field ;
112+ this .message = message ;
113+ this .rejectedValue = rejectedValue ;
114+ }
115+
116+ public String getField () { return field ; }
117+ public String getMessage () { return message ; }
118+ public Object getRejectedValue () { return rejectedValue ; }
119+ }
120+
121+ // Builder class
122+ public static class ApiErrorResponseBuilder {
123+ private int status ;
124+ private String error ;
125+ private String message ;
126+ private String path ;
127+ private String debugMessage ;
128+ private List <ValidationError > validationErrors ;
129+
130+ public ApiErrorResponseBuilder status (int status ) {
131+ this .status = status ;
132+ return this ;
133+ }
134+
135+ public ApiErrorResponseBuilder error (String error ) {
136+ this .error = error ;
137+ return this ;
138+ }
139+
140+ public ApiErrorResponseBuilder message (String message ) {
141+ this .message = message ;
142+ return this ;
143+ }
144+
145+ public ApiErrorResponseBuilder path (String path ) {
146+ this .path = path ;
147+ return this ;
148+ }
149+
150+ public ApiErrorResponseBuilder debugMessage (String debugMessage ) {
151+ this .debugMessage = debugMessage ;
152+ return this ;
153+ }
154+
155+ public ApiErrorResponseBuilder validationErrors (List <ValidationError > validationErrors ) {
156+ this .validationErrors = validationErrors ;
157+ return this ;
158+ }
159+
160+ public ApiErrorResponse build () {
161+ ApiErrorResponse response = new ApiErrorResponse (status , error , message , path , debugMessage );
162+ if (validationErrors != null ) {
163+ response .setValidationErrors (validationErrors );
164+ }
165+ return response ;
166+ }
167+ }
168+
169+ @ Override
170+ public boolean equals (Object o ) {
171+ if (this == o ) return true ;
172+ if (o == null || getClass () != o .getClass ()) return false ;
173+ ApiErrorResponse that = (ApiErrorResponse ) o ;
174+ return status == that .status &&
175+ Objects .equals (timestamp , that .timestamp ) &&
176+ Objects .equals (error , that .error ) &&
177+ Objects .equals (message , that .message ) &&
178+ Objects .equals (path , that .path );
179+ }
180+
181+ @ Override
182+ public int hashCode () {
183+ return Objects .hash (timestamp , status , error , message , path );
184+ }
185+
186+ @ Override
187+ public String toString () {
188+ return "ApiErrorResponse{" +
189+ "timestamp=" + timestamp +
190+ ", status=" + status +
191+ ", error='" + error + '\'' +
192+ ", message='" + message + '\'' +
193+ ", path='" + path + '\'' +
194+ ", validationErrors=" + validationErrors +
195+ '}' ;
196+ }
197+ }
0 commit comments