Skip to content

R. Yusup#2

Open
Yusuprozimemet wants to merge 9 commits into
HackYourAssignment:mainfrom
Yusuprozimemet:main
Open

R. Yusup#2
Yusuprozimemet wants to merge 9 commits into
HackYourAssignment:mainfrom
Yusuprozimemet:main

Conversation

@Yusuprozimemet

Copy link
Copy Markdown

No description provided.

@cometbroom cometbroom left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

Comment thread task-1/API_DESIGN.md

## Endpoint overview

```mermaid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice mermaid graph! Love it 👍

@RequestParam(required = false) String eventSource,
@RequestParam(required = false) String sessionId
) {
AnalyticsSummary summary = service.getSummary(from, to, eventType, eventSource, sessionId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very thin controller too 👍

.path(request.getRequestURI())
.details(details)
.build();
return ResponseEntity.badRequest().body(body);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for eventSource. Can be an enum.

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Week4ApplicationTests {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For increased robustness, I'd highly recomment adding validation for limit. This can prevent smart DDoS attacks which request extremely high limits.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants