77import org .springframework .beans .factory .annotation .Autowired ;
88import org .springframework .http .ResponseEntity ;
99import org .springframework .http .HttpStatus ;
10+ import org .springframework .http .ProblemDetail ;
1011import com .example .demo .service .FilmService ;
1112import com .example .demo .dto .FilmResponse ;
1213
1819import io .swagger .v3 .oas .annotations .media .Content ;
1920import io .swagger .v3 .oas .annotations .media .Schema ;
2021
22+ import jakarta .servlet .http .HttpServletRequest ;
23+ import java .net .URI ;
24+ import java .time .Instant ;
2125import java .util .Map ;
26+ import java .util .Objects ;
2227import java .util .HashMap ;
2328import java .util .List ;
2429
@@ -65,8 +70,8 @@ public FilmController(FilmService filmService) {
6570 * Retrieves films from the Sakila database, optionally filtered by starting letter.
6671 *
6772 * @param startsWith Optional parameter to filter films by starting letter (single character A-Z)
68- * @return JSON response containing films array, count, and filter information
69- * @throws IllegalArgumentException if startsWith parameter is invalid (not a single letter)
73+ * @param request HttpServletRequest for building error responses
74+ * @return JSON response containing films array, count, and filter information or error response
7075 */
7176 @ Operation (
7277 summary = "Query films by starting letter" ,
@@ -78,8 +83,6 @@ public FilmController(FilmService filmService) {
7883
7984 **Expected Results**:
8085 - Letter "A": 46 films
81- - Letter "B": 54 films
82- - Letter "C": 58 films
8386 """ ,
8487 operationId = "getFilmsByStartingLetter"
8588 )
@@ -110,18 +113,22 @@ public FilmController(FilmService filmService) {
110113 )
111114 })
112115 @ GetMapping ("/films" )
113- public ResponseEntity <FilmResponse > getFilms (
116+ public ResponseEntity <? > getFilms (
114117 @ Parameter (
115118 name = "startsWith" ,
116119 description = "Filter films by starting letter (case-insensitive, single character A-Z)" ,
117120 example = "A" ,
118121 schema = @ Schema (type = "string" , pattern = "^[A-Za-z]$" )
119122 )
120- @ RequestParam (required = false ) String startsWith ) {
123+ @ RequestParam (required = false ) String startsWith ,
124+ HttpServletRequest request ) {
121125
122126 // Task 4.4: Implement parameter validation logic (single letter, not empty)
123- if (startsWith != null ) {
124- validateStartsWithParameter (startsWith );
127+ if (Objects .nonNull (startsWith )) {
128+ ValidationResult validationResult = validateStartsWithParameter (startsWith );
129+ if (!validationResult .valid ()) {
130+ return createErrorResponse (validationResult .errorMessage (), request );
131+ }
125132 }
126133
127134 // Call service layer to get films
@@ -151,25 +158,55 @@ public ResponseEntity<FilmResponse> getFilms(
151158 * - Cannot be numeric or special characters
152159 *
153160 * @param startsWith The parameter value to validate
154- * @throws IllegalArgumentException if the parameter is invalid
161+ * @return ValidationResult containing validation status and error message if invalid
155162 */
156- private void validateStartsWithParameter (String startsWith ) {
157- if (startsWith == null || startsWith .trim ().isEmpty ()) {
158- throw new IllegalArgumentException ( "Parameter 'startsWith' cannot be empty" );
163+ private ValidationResult validateStartsWithParameter (String startsWith ) {
164+ if (Objects . isNull ( startsWith ) || startsWith .trim ().isEmpty ()) {
165+ return new ValidationResult ( false , "Parameter 'startsWith' cannot be empty" );
159166 }
160167
161168 String trimmed = startsWith .trim ();
162169
163170 // Check if it's a single character
164171 if (trimmed .length () != 1 ) {
165- throw new IllegalArgumentException ( "Parameter 'startsWith' must be a single letter (A-Z)" );
172+ return new ValidationResult ( false , "Parameter 'startsWith' must be a single letter (A-Z)" );
166173 }
167174
168175 char character = trimmed .charAt (0 );
169176
170177 // Check if it's a letter (A-Z, a-z)
171178 if (!Character .isLetter (character )) {
172- throw new IllegalArgumentException ( "Parameter 'startsWith' must be a single letter (A-Z)" );
179+ return new ValidationResult ( false , "Parameter 'startsWith' must be a single letter (A-Z)" );
173180 }
181+
182+ return new ValidationResult (true , null );
183+ }
184+
185+ /**
186+ * Creates an error response matching the format from GlobalExceptionHandler
187+ *
188+ * @param errorMessage The error message to include in the response
189+ * @param request The HTTP request for building the error response
190+ * @return ResponseEntity with ProblemDetail matching GlobalExceptionHandler format
191+ */
192+ private ResponseEntity <ProblemDetail > createErrorResponse (String errorMessage , HttpServletRequest request ) {
193+ ProblemDetail problemDetail = ProblemDetail .forStatusAndDetail (
194+ HttpStatus .BAD_REQUEST , errorMessage
195+ );
196+
197+ problemDetail .setType (URI .create ("https://example.com/problems/invalid-parameter" ));
198+ problemDetail .setTitle ("Invalid Parameter" );
199+ problemDetail .setInstance (URI .create (request .getRequestURI ()));
200+ problemDetail .setProperty ("timestamp" , Instant .now ());
201+
202+ return ResponseEntity .badRequest ().body (problemDetail );
174203 }
204+
205+ /**
206+ * Record to hold validation result with validation status and error message
207+ *
208+ * @param valid true if validation passed, false otherwise
209+ * @param errorMessage error message if validation failed, null if valid
210+ */
211+ private record ValidationResult (boolean valid , String errorMessage ) { }
175212}
0 commit comments