Profile editing in account settings

Java · Spring Boot · intermediate · modification

Adds profile editing to the account settings page. `PATCH /api/me` takes a partial JSON object and merges it onto the current user with Jackson's `readerForUpdating`, so absent keys stay untouched without a null check per field. The merged profile is validated inside the transaction; an invalid value is a 400 and rolls the change back. `User` and `ProfileView` gain `jobTitle` for the new field on the page.

Workspace backend on Spring Boot 3.3 (Java 21) with Spring Web, Spring Data JPA (Hibernate) and Spring Security. The existing security configuration requires an authenticated session for every `/api/**` request, checks CSRF tokens (the SPA sends them) and stores an `AccountPrincipal(long userId, String email)` record as the principal, which `@AuthenticationPrincipal` resolves. `UserRepository` (existing, unchanged) extends `JpaRepository<User, Long>`. `Role` is an existing enum with `MEMBER` and `ADMIN`; the admin console (existing) calls `setRole`, and the verification link (existing) calls `markEmailVerified`. The `ObjectMapper` is Spring Boot's auto-configured one: no custom Jackson configuration, mixins or `@JsonIgnore` anywhere, so JSON keys that match no property are ignored. `GET /api/me` already exists.

Requirements

Files touched

--- src/main/java/com/acme/workspace/account/User.java
     private String timeZone = "UTC";
 
+    private String jobTitle;
+
     @Enumerated(EnumType.STRING)
     @Column(nullable = false)
     public String getTimeZone() { return timeZone; }
     public void setTimeZone(String timeZone) { this.timeZone = timeZone; }
+    public String getJobTitle() { return jobTitle; }
+    public void setJobTitle(String jobTitle) { this.jobTitle = jobTitle; }
     public Role getRole() { return role; }
     public void setRole(Role role) { this.role = role; }
--- src/main/java/com/acme/workspace/account/ProfileView.java
 package com.acme.workspace.account;
 
-public record ProfileView(long id, String email, String displayName, String timeZone) {
+public record ProfileView(long id, String email, String displayName, String timeZone, String jobTitle) {
 
     public static ProfileView from(User user) {
-        return new ProfileView(user.getId(), user.getEmail(), user.getDisplayName(), user.getTimeZone());
+        return new ProfileView(user.getId(), user.getEmail(), user.getDisplayName(), user.getTimeZone(),
+                user.getJobTitle());
     }
 }
--- src/main/java/com/acme/workspace/account/MeController.java
 package com.acme.workspace.account;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import org.springframework.http.HttpStatus;

Review this PR

Java practice