Skip to content

feat: implement PATCH /players/{squadNumber} for partial updates#329

Open
the-Sunny-Sharma wants to merge 1 commit intonanotaboada:masterfrom
the-Sunny-Sharma:feat/path-players-318
Open

feat: implement PATCH /players/{squadNumber} for partial updates#329
the-Sunny-Sharma wants to merge 1 commit intonanotaboada:masterfrom
the-Sunny-Sharma:feat/path-players-318

Conversation

@the-Sunny-Sharma
Copy link
Copy Markdown

@the-Sunny-Sharma the-Sunny-Sharma commented May 9, 2026

Closes #318

What this PR does

Implements PATCH /players/{squadNumber} for partial player updates
following RFC 7396 (JSON Merge Patch) semantics.

Changes

  • Added PlayerPatchDTO — all fields nullable, annotated with
    @JsonInclude(NON_NULL), excludes id and squadNumber from patching
  • Added patch() in PlayersService — applies only non-null fields
    to the existing entity, follows existing Optional chain pattern
  • Added @PatchMapping("/players/{squadNumber}") in PlayersController
    with full Swagger @Operation and @ApiResponses documentation

Acceptance criteria

  • PATCH /players/{squadNumber} implemented
  • Only non-null fields updated; absent fields unchanged
  • Returns 204 No Content on success
  • Returns 400 Bad Request if body contains squadNumber or id
  • Returns 404 Not Found when player not found
  • All existing 41 tests pass
  • Code style matches existing patterns throughout

This change is Reviewable

Summary by CodeRabbit

  • New Features
    • Added support for partial player record updates via HTTP PATCH endpoint
    • Implemented validation to protect immutable fields from modification
    • Returns appropriate HTTP status codes (204 on success, 404 if player not found, 400 for invalid requests)

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Walkthrough

This PR implements RFC 5789 PATCH support for partial player updates. A new PlayerPatchDTO DTO with nullable fields provides the request contract, a PlayersService.patch method applies only non-null fields to existing players and clears the cache, and a PlayersController.patch endpoint validates immutable fields and routes to 204 No Content or 404 Not Found responses.

Changes

PATCH Endpoint for Partial Player Updates

Layer / File(s) Summary
Data Shape
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/models/PlayerPatchDTO.java
New DTO with @Data and @JsonInclude(NON_NULL) defines nullable fields for all patchable player attributes; id and squadNumber must not appear in requests and are documented as immutable.
Service Implementation
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/PlayersService.java
New patch(squadNumber, playerPatchDTO) method validates input, fetches the player, applies only non-null DTO fields, persists the update, evicts the "players" cache, and returns true on success or false if the player is not found.
Controller Endpoint
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/PlayersController.java
New @PatchMapping("/players/{squadNumber}") validates that id and squadNumber are absent from the request body (returning 400 Bad Request if present), delegates to the service, and maps the result to 204 No Content or 404 Not Found.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Assessment against linked issues

Objective Addressed Explanation
Implement PATCH /players/{squadNumber} endpoint
All fields except squadNumber and id are patchable
Absent fields remain unchanged; only non-null fields are applied
Returns 204 No Content on success
Returns 400 Bad Request if body contains squadNumber or id
Returns 404 Not Found when no player has that squad number
Tests added following naming conventions No test files are present in this PR.
CHANGELOG.md updated No CHANGELOG.md entry is included in this PR.

Possibly related issues

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title uses Conventional Commits format (feat:), is descriptive and specific about the PATCH endpoint implementation, and is 64 characters—well under the 80-character limit.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • 🛠️ sync documentation: Commit on current branch
  • 🛠️ sync documentation: Create PR
  • 🛠️ enforce http error handling: Commit on current branch
  • 🛠️ enforce http error handling: Create PR
  • 🛠️ idiomatic review: Commit on current branch
  • 🛠️ idiomatic review: Create PR
  • 🛠️ verify api contract: Commit on current branch
  • 🛠️ verify api contract: Create PR

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 9, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/PlayersController.java`:
- Around line 250-254: In the patch method, add request validation and a
null-body guard: annotate the PlayerPatchDTO parameter with `@Valid` (e.g., public
ResponseEntity<Void> patch(`@PathVariable` Integer squadNumber, `@Valid`
`@RequestBody` PlayerPatchDTO playerPatchDTO)) and update the initial conditional
to check for null before dereferencing (e.g., if (playerPatchDTO == null ||
playerPatchDTO.getSquadNumber() != null || playerPatchDTO.getId() != null)
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();). Ensure the
controller imports javax.validation.Valid (or jakarta.validation.Valid) so DTO
constraints on PlayerPatchDTO are enforced.

In
`@src/main/java/ar/com/nanotaboada/java/samples/spring/boot/models/PlayerPatchDTO.java`:
- Around line 35-45: PlayerPatchDTO lacks Bean Validation annotations—add the
same field-level JSR-380 constraints used in PlayerDTO but using constraints
that permit null (so patch fields remain optional): annotate squadNumber with
`@Min/`@Max, dateOfBirth with `@Past`, string fields (firstName, middleName,
lastName, position, abbrPosition, team, league) with `@Size`(max=...) and any
`@Pattern`(...) used in PlayerDTO for formats, and apply any ID/UUID format
constraint used in PlayerDTO to id; do not add `@NotNull/`@NotBlank (since fields
must remain nullable). Import javax.validation.constraints.* and mirror the
exact size/regex/min-max values from PlayerDTO when updating class
PlayerPatchDTO.

In
`@src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/PlayersService.java`:
- Around line 235-243: The method currently dereferences playerPatchDTO without
null-checking (see squadNumber check and the
playersRepository.findBySquadNumber(...).map(existing -> { ... }) block), so add
an early guard for playerPatchDTO == null at the start of the method (similar to
the squadNumber null check) to log a warning and return false; then proceed to
use playerPatchDTO safely in the mapping where existing.setFirstName(...),
existing.setMiddleName(...), etc. are called. Ensure the log uses the same
logger (log) and behavior as the squadNumber guard to keep semantics consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 288c1c36-cc52-413f-a575-628f4f188d18

📥 Commits

Reviewing files that changed from the base of the PR and between db4db58 and 0ea4892.

📒 Files selected for processing (3)
  • src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/PlayersController.java
  • src/main/java/ar/com/nanotaboada/java/samples/spring/boot/models/PlayerPatchDTO.java
  • src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/PlayersService.java

Comment on lines +250 to +254
public ResponseEntity<Void> patch(
@PathVariable Integer squadNumber,
@RequestBody PlayerPatchDTO playerPatchDTO) {
if (playerPatchDTO.getSquadNumber() != null || playerPatchDTO.getId() != null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent null-body NPE and enable validation on PATCH input.

On Line 253, playerPatchDTO is dereferenced directly. A null JSON body can cause a 500. Also, @Valid is missing, so DTO constraints are skipped.

Suggested fix
 public ResponseEntity<Void> patch(
         `@PathVariable` Integer squadNumber,
-        `@RequestBody` PlayerPatchDTO playerPatchDTO) {
+        `@RequestBody` `@Valid` PlayerPatchDTO playerPatchDTO) {
+    if (playerPatchDTO == null) {
+        return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
+    }
     if (playerPatchDTO.getSquadNumber() != null || playerPatchDTO.getId() != null) {
         return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
     }

As per coding guidelines, "Check that @Valid is used for request validation".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/PlayersController.java`
around lines 250 - 254, In the patch method, add request validation and a
null-body guard: annotate the PlayerPatchDTO parameter with `@Valid` (e.g., public
ResponseEntity<Void> patch(`@PathVariable` Integer squadNumber, `@Valid`
`@RequestBody` PlayerPatchDTO playerPatchDTO)) and update the initial conditional
to check for null before dereferencing (e.g., if (playerPatchDTO == null ||
playerPatchDTO.getSquadNumber() != null || playerPatchDTO.getId() != null)
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();). Ensure the
controller imports javax.validation.Valid (or jakarta.validation.Valid) so DTO
constraints on PlayerPatchDTO are enforced.

Comment on lines +35 to +45
private UUID id;
private Integer squadNumber;
private String firstName;
private String middleName;
private String lastName;
private LocalDate dateOfBirth;
private String position;
private String abbrPosition;
private String team;
private String league;
private Boolean starting11;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add Bean Validation constraints to PlayerPatchDTO fields.

The DTO currently accepts any provided values without JSR-380 validation, which can let invalid patches reach persistence logic. Keep fields nullable, but add the same field-level constraints used by PlayerDTO so validation triggers only when a field is present.

As per coding guidelines, "Use Bean Validation (JSR-380) for input validation in DTOs".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/ar/com/nanotaboada/java/samples/spring/boot/models/PlayerPatchDTO.java`
around lines 35 - 45, PlayerPatchDTO lacks Bean Validation annotations—add the
same field-level JSR-380 constraints used in PlayerDTO but using constraints
that permit null (so patch fields remain optional): annotate squadNumber with
`@Min/`@Max, dateOfBirth with `@Past`, string fields (firstName, middleName,
lastName, position, abbrPosition, team, league) with `@Size`(max=...) and any
`@Pattern`(...) used in PlayerDTO for formats, and apply any ID/UUID format
constraint used in PlayerDTO to id; do not add `@NotNull/`@NotBlank (since fields
must remain nullable). Import javax.validation.constraints.* and mirror the
exact size/regex/min-max values from PlayerDTO when updating class
PlayerPatchDTO.

Comment on lines +235 to +243
if (squadNumber == null) {
log.warn("Cannot patch player - squad number is null");
return false;
}

return playersRepository.findBySquadNumber(squadNumber)
.map(existing -> {
if (playerPatchDTO.getFirstName() != null) existing.setFirstName(playerPatchDTO.getFirstName());
if (playerPatchDTO.getMiddleName() != null) existing.setMiddleName(playerPatchDTO.getMiddleName());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard playerPatchDTO against null before field access.

playerPatchDTO is dereferenced without a null check. If a null payload reaches this method, it will throw at runtime instead of returning a controlled result.

Suggested fix
 public boolean patch(Integer squadNumber, PlayerPatchDTO playerPatchDTO) {
     log.debug("Patching player with squad number: {}", squadNumber);

-    if (squadNumber == null) {
-        log.warn("Cannot patch player - squad number is null");
+    if (squadNumber == null || playerPatchDTO == null) {
+        log.warn("Cannot patch player - squad number or payload is null");
         return false;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (squadNumber == null) {
log.warn("Cannot patch player - squad number is null");
return false;
}
return playersRepository.findBySquadNumber(squadNumber)
.map(existing -> {
if (playerPatchDTO.getFirstName() != null) existing.setFirstName(playerPatchDTO.getFirstName());
if (playerPatchDTO.getMiddleName() != null) existing.setMiddleName(playerPatchDTO.getMiddleName());
if (squadNumber == null || playerPatchDTO == null) {
log.warn("Cannot patch player - squad number or payload is null");
return false;
}
return playersRepository.findBySquadNumber(squadNumber)
.map(existing -> {
if (playerPatchDTO.getFirstName() != null) existing.setFirstName(playerPatchDTO.getFirstName());
if (playerPatchDTO.getMiddleName() != null) existing.setMiddleName(playerPatchDTO.getMiddleName());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/PlayersService.java`
around lines 235 - 243, The method currently dereferences playerPatchDTO without
null-checking (see squadNumber check and the
playersRepository.findBySquadNumber(...).map(existing -> { ... }) block), so add
an early guard for playerPatchDTO == null at the start of the method (similar to
the squadNumber null check) to log a warning and return false; then proceed to
use playerPatchDTO safely in the mapping where existing.setFirstName(...),
existing.setMiddleName(...), etc. are called. Ensure the log uses the same
logger (log) and behavior as the squadNumber guard to keep semantics consistent.

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.

Implement PATCH method for partial updates

1 participant