R. Yusup#2
Conversation
…pplies the rules of the application. returns plain java objects
cometbroom
left a comment
There was a problem hiding this comment.
Good work overall Yusup!
Your submission covers most of the required API behavior: the API design is documented clearly, the URIs are resource-oriented, and the implementation supports creating, listing, filtering, fetching, replacing, deleting, and summarizing analytics records.
The split between controllers, DTOs, service, repository, model, and exception handling is also easy to follow.
The strongest parts are the API contract and the core service behavior.
The main issue is testing since the assignment specifically asked for service-layer unit tests.
Overall, this is a solid and readable submission that meets most of the core API requirements. The biggest improvement needed is adding proper service-layer unit tests and tightening validation around query parameters and edge cases.
|
|
||
| @RestController | ||
| @RequestMapping("/api/analytics-summary") | ||
| public class AnalyticsSummaryController { |
There was a problem hiding this comment.
Nice improvement separating the summary endpoint into its own controller Yusup!
AnalyticsController can stay focused on managing individual analytics records, while AnalyticsSummaryController owns aggregate/reporting-style behavior.
That separation makes the code easier to navigate now, and if someone were to ask you to add more functionality for summary, I bet you'd be easily be able to imagine what you have to do and where 👍
|
|
||
| ## Endpoint overview | ||
|
|
||
| ```mermaid |
| @RequestParam(required = false) String eventSource, | ||
| @RequestParam(required = false) String sessionId | ||
| ) { | ||
| AnalyticsSummary summary = service.getSummary(from, to, eventType, eventSource, sessionId); |
| .path(request.getRequestURI()) | ||
| .details(details) | ||
| .build(); | ||
| return ResponseEntity.badRequest().body(body); |
There was a problem hiding this comment.
Impressive. Not just an ErrorResponse DTO, it's also wrapped with ResponseEntity and the API contract is respected for multiple errors aswell as one.
However, I do have a few comments on the implementation detail that I'll leave in relevant places here.
| .error("Bad request") | ||
| .message("Validation failed") | ||
| .path(request.getRequestURI()) | ||
| .details(details) |
There was a problem hiding this comment.
While this respects API contract. It does increase complexity of your response object. A client has to always check for details when multiple error are encountered.
So better would be to have an ErrorResponse DTO type that has errors list property rather than error
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(
MethodArgumentNotValidException ex,
HttpServletRequest request
) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult()
.getFieldErrors()
.forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));
ErrorResponse response = ErrorResponse.builder()
.timestamp(Instant.now())
.status(400)
.message("Validation failed")
.path(request.getRequestURI())
.errors(errors)
.build();
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}|
|
||
| @NotBlank | ||
| @Size(min =1, max = 64) | ||
| private String eventType; |
There was a problem hiding this comment.
Same comment as for eventSource. Can be an enum.
| import org.springframework.boot.test.context.SpringBootTest; | ||
|
|
||
| @SpringBootTest | ||
| class Week4ApplicationTests { |
There was a problem hiding this comment.
Don't see any tests added. Having controller layer thin means you can insrease confidence in application functionality greatly by adding unit tests. For the extra confidence you would then add Integration Tests from the controller which you'll learn about in Week 6.
| @RequestParam(required = false) String eventType, | ||
| @RequestParam(required = false) String eventSource, | ||
| @RequestParam(required = false) String sessionId, | ||
| @RequestParam(defaultValue = "100") int limit, |
There was a problem hiding this comment.
For increased robustness, I'd highly recomment adding validation for limit. This can prevent smart DDoS attacks which request extremely high limits.
There was a problem hiding this comment.
I see. I will think about all of your comments, thanks a lot!
| @RequestParam(required = false) String eventSource, | ||
| @RequestParam(required = false) String sessionId, | ||
| @RequestParam(defaultValue = "100") int limit, | ||
| @RequestParam(defaultValue = "0") int offset |
There was a problem hiding this comment.
Also highly recommended to add validation for offset. While this doesn't specifically help against DDoS attacks, it helps the client work with your API. If they entered a negative number thinking they're getting a specific functionality, better let them know that's not allowed.
|
|
||
| @GetMapping | ||
| public ResponseEntity<List<AnalyticsRecord>> list( | ||
| @RequestParam(required = false) Instant from, |
There was a problem hiding this comment.
ood choice using Instant for the time filters here!
For analytics data, an instant in time is usually a better fit than LocalDateTime, because it avoids ambiguity around server timezone or client timezone.
No description provided.