From 235e50bb8e45ddc61445264e7adc281b8df5a0e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:19:39 +0000 Subject: [PATCH 1/7] Initial plan From fa32a164f5088c22347ba9b42d783f1cb0a22494 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:30:43 +0000 Subject: [PATCH 2/7] Refactor: Replace anonymous HashMap with explicit initialization Co-authored-by: gologic-ben <19367774+gologic-ben@users.noreply.github.com> --- src/main/java/io/spring/api/ArticleApi.java | 8 ++---- src/main/java/io/spring/api/ArticlesApi.java | 10 +++---- src/main/java/io/spring/api/CommentsApi.java | 17 ++++------- .../java/io/spring/api/CurrentUserApi.java | 8 ++---- src/main/java/io/spring/api/UsersApi.java | 8 ++---- .../io/spring/graphql/ArticleDatafetcher.java | 28 +++++++------------ 6 files changed, 29 insertions(+), 50 deletions(-) diff --git a/src/main/java/io/spring/api/ArticleApi.java b/src/main/java/io/spring/api/ArticleApi.java index c80afa31d..9fc0529f1 100644 --- a/src/main/java/io/spring/api/ArticleApi.java +++ b/src/main/java/io/spring/api/ArticleApi.java @@ -79,10 +79,8 @@ public ResponseEntity deleteArticle( } private Map articleResponse(ArticleData articleData) { - return new HashMap() { - { - put("article", articleData); - } - }; + Map response = new HashMap<>(); + response.put("article", articleData); + return response; } } diff --git a/src/main/java/io/spring/api/ArticlesApi.java b/src/main/java/io/spring/api/ArticlesApi.java index 50584bd6d..b7dd18f09 100644 --- a/src/main/java/io/spring/api/ArticlesApi.java +++ b/src/main/java/io/spring/api/ArticlesApi.java @@ -7,6 +7,7 @@ import io.spring.core.article.Article; import io.spring.core.user.User; import java.util.HashMap; +import java.util.Map; import javax.validation.Valid; import lombok.AllArgsConstructor; import org.springframework.http.ResponseEntity; @@ -29,12 +30,9 @@ public class ArticlesApi { public ResponseEntity createArticle( @Valid @RequestBody NewArticleParam newArticleParam, @AuthenticationPrincipal User user) { Article article = articleCommandService.createArticle(newArticleParam, user); - return ResponseEntity.ok( - new HashMap() { - { - put("article", articleQueryService.findById(article.getId(), user).get()); - } - }); + Map response = new HashMap<>(); + response.put("article", articleQueryService.findById(article.getId(), user).get()); + return ResponseEntity.ok(response); } @GetMapping(path = "feed") diff --git a/src/main/java/io/spring/api/CommentsApi.java b/src/main/java/io/spring/api/CommentsApi.java index c5f7e77e9..59997aed6 100644 --- a/src/main/java/io/spring/api/CommentsApi.java +++ b/src/main/java/io/spring/api/CommentsApi.java @@ -56,12 +56,9 @@ public ResponseEntity getComments( Article article = articleRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); List comments = commentQueryService.findByArticleId(article.getId(), user); - return ResponseEntity.ok( - new HashMap() { - { - put("comments", comments); - } - }); + Map response = new HashMap<>(); + response.put("comments", comments); + return ResponseEntity.ok(response); } @RequestMapping(path = "{id}", method = RequestMethod.DELETE) @@ -85,11 +82,9 @@ public ResponseEntity deleteComment( } private Map commentResponse(CommentData commentData) { - return new HashMap() { - { - put("comment", commentData); - } - }; + Map response = new HashMap<>(); + response.put("comment", commentData); + return response; } } diff --git a/src/main/java/io/spring/api/CurrentUserApi.java b/src/main/java/io/spring/api/CurrentUserApi.java index e096aec0b..b7aabb93c 100644 --- a/src/main/java/io/spring/api/CurrentUserApi.java +++ b/src/main/java/io/spring/api/CurrentUserApi.java @@ -49,10 +49,8 @@ public ResponseEntity updateProfile( } private Map userResponse(UserWithToken userWithToken) { - return new HashMap() { - { - put("user", userWithToken); - } - }; + Map response = new HashMap<>(); + response.put("user", userWithToken); + return response; } } diff --git a/src/main/java/io/spring/api/UsersApi.java b/src/main/java/io/spring/api/UsersApi.java index d91321e82..113eaf792 100644 --- a/src/main/java/io/spring/api/UsersApi.java +++ b/src/main/java/io/spring/api/UsersApi.java @@ -58,11 +58,9 @@ public ResponseEntity userLogin(@Valid @RequestBody LoginParam loginParam) { } private Map userResponse(UserWithToken userWithToken) { - return new HashMap() { - { - put("user", userWithToken); - } - }; + Map response = new HashMap<>(); + response.put("user", userWithToken); + return response; } } diff --git a/src/main/java/io/spring/graphql/ArticleDatafetcher.java b/src/main/java/io/spring/graphql/ArticleDatafetcher.java index 37c82939a..b96e28857 100644 --- a/src/main/java/io/spring/graphql/ArticleDatafetcher.java +++ b/src/main/java/io/spring/graphql/ArticleDatafetcher.java @@ -28,6 +28,7 @@ import io.spring.graphql.types.ArticlesConnection; import io.spring.graphql.types.Profile; import java.util.HashMap; +import java.util.Map; import java.util.stream.Collectors; import lombok.AllArgsConstructor; import org.joda.time.format.ISODateTimeFormat; @@ -307,13 +308,10 @@ public DataFetcherResult
getArticle(DataFetchingEnvironment dfe) { .findById(article.getId(), current) .orElseThrow(ResourceNotFoundException::new); Article articleResult = buildArticleResult(articleData); + Map context = new HashMap<>(); + context.put(articleData.getSlug(), articleData); return DataFetcherResult.
newResult() - .localContext( - new HashMap() { - { - put(articleData.getSlug(), articleData); - } - }) + .localContext(context) .data(articleResult) .build(); } @@ -328,13 +326,10 @@ public DataFetcherResult
getCommentArticle( .findById(comment.getArticleId(), current) .orElseThrow(ResourceNotFoundException::new); Article articleResult = buildArticleResult(articleData); + Map context = new HashMap<>(); + context.put(articleData.getSlug(), articleData); return DataFetcherResult.
newResult() - .localContext( - new HashMap() { - { - put(articleData.getSlug(), articleData); - } - }) + .localContext(context) .data(articleResult) .build(); } @@ -345,13 +340,10 @@ public DataFetcherResult
findArticleBySlug(@InputArgument("slug") Strin ArticleData articleData = articleQueryService.findBySlug(slug, current).orElseThrow(ResourceNotFoundException::new); Article articleResult = buildArticleResult(articleData); + Map context = new HashMap<>(); + context.put(articleData.getSlug(), articleData); return DataFetcherResult.
newResult() - .localContext( - new HashMap() { - { - put(articleData.getSlug(), articleData); - } - }) + .localContext(context) .data(articleResult) .build(); } From bf8e216501e920458913d867c93840e4c5ff3c97 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:33:01 +0000 Subject: [PATCH 3/7] Refactor: Rename isEmpty to isNullOrEmpty for clarity Co-authored-by: gologic-ben <19367774+gologic-ben@users.noreply.github.com> --- src/main/java/io/spring/Util.java | 8 +++++++- src/main/java/io/spring/core/article/Article.java | 6 +++--- src/main/java/io/spring/core/user/User.java | 10 +++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/spring/Util.java b/src/main/java/io/spring/Util.java index d2512acca..09c17cbf5 100644 --- a/src/main/java/io/spring/Util.java +++ b/src/main/java/io/spring/Util.java @@ -1,7 +1,13 @@ package io.spring; public class Util { - public static boolean isEmpty(String value) { + public static boolean isNullOrEmpty(String value) { return value == null || value.isEmpty(); } + + /** @deprecated Use {@link #isNullOrEmpty(String)} instead */ + @Deprecated + public static boolean isEmpty(String value) { + return isNullOrEmpty(value); + } } diff --git a/src/main/java/io/spring/core/article/Article.java b/src/main/java/io/spring/core/article/Article.java index f23c2c6d5..859ba0bee 100644 --- a/src/main/java/io/spring/core/article/Article.java +++ b/src/main/java/io/spring/core/article/Article.java @@ -49,16 +49,16 @@ public Article( } public void update(String title, String description, String body) { - if (!Util.isEmpty(title)) { + if (!Util.isNullOrEmpty(title)) { this.title = title; this.slug = toSlug(title); this.updatedAt = new DateTime(); } - if (!Util.isEmpty(description)) { + if (!Util.isNullOrEmpty(description)) { this.description = description; this.updatedAt = new DateTime(); } - if (!Util.isEmpty(body)) { + if (!Util.isNullOrEmpty(body)) { this.body = body; this.updatedAt = new DateTime(); } diff --git a/src/main/java/io/spring/core/user/User.java b/src/main/java/io/spring/core/user/User.java index 3044d5034..88bcce2dc 100644 --- a/src/main/java/io/spring/core/user/User.java +++ b/src/main/java/io/spring/core/user/User.java @@ -27,23 +27,23 @@ public User(String email, String username, String password, String bio, String i } public void update(String email, String username, String password, String bio, String image) { - if (!Util.isEmpty(email)) { + if (!Util.isNullOrEmpty(email)) { this.email = email; } - if (!Util.isEmpty(username)) { + if (!Util.isNullOrEmpty(username)) { this.username = username; } - if (!Util.isEmpty(password)) { + if (!Util.isNullOrEmpty(password)) { this.password = password; } - if (!Util.isEmpty(bio)) { + if (!Util.isNullOrEmpty(bio)) { this.bio = bio; } - if (!Util.isEmpty(image)) { + if (!Util.isNullOrEmpty(image)) { this.image = image; } } From 40d9fda796da6d9362fe1bbdfcfa62509e4e29dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:35:59 +0000 Subject: [PATCH 4/7] Refactor: Extract duplicate validation and connection building logic Co-authored-by: gologic-ben <19367774+gologic-ben@users.noreply.github.com> --- .../io/spring/graphql/ArticleDatafetcher.java | 150 +++++------------- 1 file changed, 39 insertions(+), 111 deletions(-) diff --git a/src/main/java/io/spring/graphql/ArticleDatafetcher.java b/src/main/java/io/spring/graphql/ArticleDatafetcher.java index b96e28857..72b2e3328 100644 --- a/src/main/java/io/spring/graphql/ArticleDatafetcher.java +++ b/src/main/java/io/spring/graphql/ArticleDatafetcher.java @@ -47,9 +47,7 @@ public DataFetcherResult getFeed( @InputArgument("last") Integer last, @InputArgument("before") String before, DgsDataFetchingEnvironment dfe) { - if (first == null && last == null) { - throw new IllegalArgumentException("first 和 last 必须只存在一个"); - } + validatePaginationArguments(first, last); User current = SecurityUtil.getCurrentUser().orElse(null); @@ -65,25 +63,7 @@ public DataFetcherResult getFeed( current, new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); - ArticlesConnection articlesConnection = - ArticlesConnection.newBuilder() - .pageInfo(pageInfo) - .edges( - articles.getData().stream() - .map( - a -> - ArticleEdge.newBuilder() - .cursor(a.getCursor().toString()) - .node(buildArticleResult(a)) - .build()) - .collect(Collectors.toList())) - .build(); - return DataFetcherResult.newResult() - .data(articlesConnection) - .localContext( - articles.getData().stream().collect(Collectors.toMap(ArticleData::getSlug, a -> a))) - .build(); + return buildArticlesConnectionResult(articles); } @DgsData(parentType = PROFILE.TYPE_NAME, field = PROFILE.Feed) @@ -93,9 +73,7 @@ public DataFetcherResult userFeed( @InputArgument("last") Integer last, @InputArgument("before") String before, DgsDataFetchingEnvironment dfe) { - if (first == null && last == null) { - throw new IllegalArgumentException("first 和 last 必须只存在一个"); - } + validatePaginationArguments(first, last); Profile profile = dfe.getSource(); User target = @@ -115,25 +93,7 @@ public DataFetcherResult userFeed( target, new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); - ArticlesConnection articlesConnection = - ArticlesConnection.newBuilder() - .pageInfo(pageInfo) - .edges( - articles.getData().stream() - .map( - a -> - ArticleEdge.newBuilder() - .cursor(a.getCursor().toString()) - .node(buildArticleResult(a)) - .build()) - .collect(Collectors.toList())) - .build(); - return DataFetcherResult.newResult() - .data(articlesConnection) - .localContext( - articles.getData().stream().collect(Collectors.toMap(ArticleData::getSlug, a -> a))) - .build(); + return buildArticlesConnectionResult(articles); } @DgsData(parentType = PROFILE.TYPE_NAME, field = PROFILE.Favorites) @@ -143,9 +103,7 @@ public DataFetcherResult userFavorites( @InputArgument("last") Integer last, @InputArgument("before") String before, DgsDataFetchingEnvironment dfe) { - if (first == null && last == null) { - throw new IllegalArgumentException("first 和 last 必须只存在一个"); - } + validatePaginationArguments(first, last); User current = SecurityUtil.getCurrentUser().orElse(null); Profile profile = dfe.getSource(); @@ -168,26 +126,7 @@ public DataFetcherResult userFavorites( new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); - - ArticlesConnection articlesConnection = - ArticlesConnection.newBuilder() - .pageInfo(pageInfo) - .edges( - articles.getData().stream() - .map( - a -> - ArticleEdge.newBuilder() - .cursor(a.getCursor().toString()) - .node(buildArticleResult(a)) - .build()) - .collect(Collectors.toList())) - .build(); - return DataFetcherResult.newResult() - .data(articlesConnection) - .localContext( - articles.getData().stream().collect(Collectors.toMap(ArticleData::getSlug, a -> a))) - .build(); + return buildArticlesConnectionResult(articles); } @DgsData(parentType = PROFILE.TYPE_NAME, field = PROFILE.Articles) @@ -197,9 +136,7 @@ public DataFetcherResult userArticles( @InputArgument("last") Integer last, @InputArgument("before") String before, DgsDataFetchingEnvironment dfe) { - if (first == null && last == null) { - throw new IllegalArgumentException("first 和 last 必须只存在一个"); - } + validatePaginationArguments(first, last); User current = SecurityUtil.getCurrentUser().orElse(null); Profile profile = dfe.getSource(); @@ -222,25 +159,7 @@ public DataFetcherResult userArticles( new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); - ArticlesConnection articlesConnection = - ArticlesConnection.newBuilder() - .pageInfo(pageInfo) - .edges( - articles.getData().stream() - .map( - a -> - ArticleEdge.newBuilder() - .cursor(a.getCursor().toString()) - .node(buildArticleResult(a)) - .build()) - .collect(Collectors.toList())) - .build(); - return DataFetcherResult.newResult() - .data(articlesConnection) - .localContext( - articles.getData().stream().collect(Collectors.toMap(ArticleData::getSlug, a -> a))) - .build(); + return buildArticlesConnectionResult(articles); } @DgsData(parentType = DgsConstants.QUERY_TYPE, field = QUERY.Articles) @@ -253,9 +172,7 @@ public DataFetcherResult getArticles( @InputArgument("favoritedBy") String favoritedBy, @InputArgument("withTag") String withTag, DgsDataFetchingEnvironment dfe) { - if (first == null && last == null) { - throw new IllegalArgumentException("first 和 last 必须只存在一个"); - } + validatePaginationArguments(first, last); User current = SecurityUtil.getCurrentUser().orElse(null); @@ -277,25 +194,7 @@ public DataFetcherResult getArticles( new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); - ArticlesConnection articlesConnection = - ArticlesConnection.newBuilder() - .pageInfo(pageInfo) - .edges( - articles.getData().stream() - .map( - a -> - ArticleEdge.newBuilder() - .cursor(a.getCursor().toString()) - .node(buildArticleResult(a)) - .build()) - .collect(Collectors.toList())) - .build(); - return DataFetcherResult.newResult() - .data(articlesConnection) - .localContext( - articles.getData().stream().collect(Collectors.toMap(ArticleData::getSlug, a -> a))) - .build(); + return buildArticlesConnectionResult(articles); } @DgsData(parentType = ARTICLEPAYLOAD.TYPE_NAME, field = ARTICLEPAYLOAD.Article) @@ -373,4 +272,33 @@ private Article buildArticleResult(ArticleData articleData) { .updatedAt(ISODateTimeFormat.dateTime().withZoneUTC().print(articleData.getUpdatedAt())) .build(); } + + private void validatePaginationArguments(Integer first, Integer last) { + if (first == null && last == null) { + throw new IllegalArgumentException("first 和 last 必须只存在一个"); + } + } + + private DataFetcherResult buildArticlesConnectionResult( + CursorPager articles) { + graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); + ArticlesConnection articlesConnection = + ArticlesConnection.newBuilder() + .pageInfo(pageInfo) + .edges( + articles.getData().stream() + .map( + a -> + ArticleEdge.newBuilder() + .cursor(a.getCursor().toString()) + .node(buildArticleResult(a)) + .build()) + .collect(Collectors.toList())) + .build(); + return DataFetcherResult.newResult() + .data(articlesConnection) + .localContext( + articles.getData().stream().collect(Collectors.toMap(ArticleData::getSlug, a -> a))) + .build(); + } } From 602ddf3fed20271fb5b8e4717f88b3e31819f6d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:40:24 +0000 Subject: [PATCH 5/7] Refactor: Extract authorization header token parsing to utility class Co-authored-by: gologic-ben <19367774+gologic-ben@users.noreply.github.com> --- .../java/io/spring/api/CurrentUserApi.java | 7 ++--- .../security/AuthorizationHeaderParser.java | 26 +++++++++++++++++++ .../spring/api/security/JwtTokenFilter.java | 11 +------- .../java/io/spring/graphql/MeDatafetcher.java | 3 ++- 4 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 src/main/java/io/spring/api/security/AuthorizationHeaderParser.java diff --git a/src/main/java/io/spring/api/CurrentUserApi.java b/src/main/java/io/spring/api/CurrentUserApi.java index b7aabb93c..5e7c225bc 100644 --- a/src/main/java/io/spring/api/CurrentUserApi.java +++ b/src/main/java/io/spring/api/CurrentUserApi.java @@ -33,8 +33,8 @@ public ResponseEntity currentUser( @AuthenticationPrincipal User currentUser, @RequestHeader(value = "Authorization") String authorization) { UserData userData = userQueryService.findById(currentUser.getId()).get(); - return ResponseEntity.ok( - userResponse(new UserWithToken(userData, authorization.split(" ")[1]))); + String token = io.spring.api.security.AuthorizationHeaderParser.extractTokenOrThrow(authorization); + return ResponseEntity.ok(userResponse(new UserWithToken(userData, token))); } @PutMapping @@ -45,7 +45,8 @@ public ResponseEntity updateProfile( userService.updateUser(new UpdateUserCommand(currentUser, updateUserParam)); UserData userData = userQueryService.findById(currentUser.getId()).get(); - return ResponseEntity.ok(userResponse(new UserWithToken(userData, token.split(" ")[1]))); + String extractedToken = io.spring.api.security.AuthorizationHeaderParser.extractTokenOrThrow(token); + return ResponseEntity.ok(userResponse(new UserWithToken(userData, extractedToken))); } private Map userResponse(UserWithToken userWithToken) { diff --git a/src/main/java/io/spring/api/security/AuthorizationHeaderParser.java b/src/main/java/io/spring/api/security/AuthorizationHeaderParser.java new file mode 100644 index 000000000..fcf8aa828 --- /dev/null +++ b/src/main/java/io/spring/api/security/AuthorizationHeaderParser.java @@ -0,0 +1,26 @@ +package io.spring.api.security; + +import java.util.Optional; + +public class AuthorizationHeaderParser { + private static final String BEARER_PREFIX = "Bearer "; + private static final int TOKEN_INDEX = 1; + + public static Optional extractToken(String authorizationHeader) { + if (authorizationHeader == null) { + return Optional.empty(); + } + + String[] parts = authorizationHeader.split(" "); + if (parts.length < 2) { + return Optional.empty(); + } + + return Optional.ofNullable(parts[TOKEN_INDEX]); + } + + public static String extractTokenOrThrow(String authorizationHeader) { + return extractToken(authorizationHeader) + .orElseThrow(() -> new IllegalArgumentException("Invalid authorization header")); + } +} diff --git a/src/main/java/io/spring/api/security/JwtTokenFilter.java b/src/main/java/io/spring/api/security/JwtTokenFilter.java index 1b5c50146..78abd7782 100644 --- a/src/main/java/io/spring/api/security/JwtTokenFilter.java +++ b/src/main/java/io/spring/api/security/JwtTokenFilter.java @@ -48,15 +48,6 @@ protected void doFilterInternal( } private Optional getTokenString(String header) { - if (header == null) { - return Optional.empty(); - } else { - String[] split = header.split(" "); - if (split.length < 2) { - return Optional.empty(); - } else { - return Optional.ofNullable(split[1]); - } - } + return AuthorizationHeaderParser.extractToken(header); } } diff --git a/src/main/java/io/spring/graphql/MeDatafetcher.java b/src/main/java/io/spring/graphql/MeDatafetcher.java index 939859677..c3972d51f 100644 --- a/src/main/java/io/spring/graphql/MeDatafetcher.java +++ b/src/main/java/io/spring/graphql/MeDatafetcher.java @@ -36,7 +36,8 @@ public DataFetcherResult getMe( io.spring.core.user.User user = (io.spring.core.user.User) authentication.getPrincipal(); UserData userData = userQueryService.findById(user.getId()).orElseThrow(ResourceNotFoundException::new); - UserWithToken userWithToken = new UserWithToken(userData, authorization.split(" ")[1]); + String token = io.spring.api.security.AuthorizationHeaderParser.extractTokenOrThrow(authorization); + UserWithToken userWithToken = new UserWithToken(userData, token); User result = User.newBuilder() .email(userWithToken.getEmail()) From ab3e1116d22291395b93116d7c6ac82894a53a80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:41:47 +0000 Subject: [PATCH 6/7] Refactor: Improve Page class with immutability and named constants Co-authored-by: gologic-ben <19367774+gologic-ben@users.noreply.github.com> --- src/main/java/io/spring/application/Page.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/spring/application/Page.java b/src/main/java/io/spring/application/Page.java index d273e994f..77ed027e6 100644 --- a/src/main/java/io/spring/application/Page.java +++ b/src/main/java/io/spring/application/Page.java @@ -1,31 +1,36 @@ package io.spring.application; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.Getter; -@NoArgsConstructor -@Data +@Getter public class Page { + private static final int DEFAULT_OFFSET = 0; + private static final int DEFAULT_LIMIT = 20; private static final int MAX_LIMIT = 100; - private int offset = 0; - private int limit = 20; + private static final int MIN_OFFSET = 0; + private static final int MIN_LIMIT = 1; + + private final int offset; + private final int limit; + + public Page() { + this.offset = DEFAULT_OFFSET; + this.limit = DEFAULT_LIMIT; + } public Page(int offset, int limit) { - setOffset(offset); - setLimit(limit); + this.offset = validateOffset(offset); + this.limit = validateLimit(limit); } - private void setOffset(int offset) { - if (offset > 0) { - this.offset = offset; - } + private int validateOffset(int offset) { + return Math.max(offset, MIN_OFFSET); } - private void setLimit(int limit) { - if (limit > MAX_LIMIT) { - this.limit = MAX_LIMIT; - } else if (limit > 0) { - this.limit = limit; + private int validateLimit(int limit) { + if (limit < MIN_LIMIT) { + return DEFAULT_LIMIT; } + return Math.min(limit, MAX_LIMIT); } } From cf7fa02725c50b203cf65151830409e20bfb5e74 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:44:32 +0000 Subject: [PATCH 7/7] Refactor: Simplify validator and eliminate duplicate timestamp updates Co-authored-by: gologic-ben <19367774+gologic-ben@users.noreply.github.com> --- .../spring/application/user/UserService.java | 45 ++++++++++--------- .../java/io/spring/core/article/Article.java | 10 ++++- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/spring/application/user/UserService.java b/src/main/java/io/spring/application/user/UserService.java index 48c6735b8..b4cbfa52f 100644 --- a/src/main/java/io/spring/application/user/UserService.java +++ b/src/main/java/io/spring/application/user/UserService.java @@ -77,30 +77,31 @@ public boolean isValid(UpdateUserCommand value, ConstraintValidatorContext conte String inputUsername = value.getParam().getUsername(); final User targetUser = value.getTargetUser(); - boolean isEmailValid = - userRepository.findByEmail(inputEmail).map(user -> user.equals(targetUser)).orElse(true); - boolean isUsernameValid = - userRepository - .findByUsername(inputUsername) - .map(user -> user.equals(targetUser)) - .orElse(true); + boolean isEmailValid = isFieldAvailableForUser(inputEmail, targetUser, userRepository::findByEmail); + boolean isUsernameValid = isFieldAvailableForUser(inputUsername, targetUser, userRepository::findByUsername); + if (isEmailValid && isUsernameValid) { return true; - } else { - context.disableDefaultConstraintViolation(); - if (!isEmailValid) { - context - .buildConstraintViolationWithTemplate("email already exist") - .addPropertyNode("email") - .addConstraintViolation(); - } - if (!isUsernameValid) { - context - .buildConstraintViolationWithTemplate("username already exist") - .addPropertyNode("username") - .addConstraintViolation(); - } - return false; } + + context.disableDefaultConstraintViolation(); + if (!isEmailValid) { + addConstraintViolation(context, "email", "email already exist"); + } + if (!isUsernameValid) { + addConstraintViolation(context, "username", "username already exist"); + } + return false; + } + + private boolean isFieldAvailableForUser(String fieldValue, User targetUser, java.util.function.Function> finder) { + return finder.apply(fieldValue).map(user -> user.equals(targetUser)).orElse(true); + } + + private void addConstraintViolation(ConstraintValidatorContext context, String property, String message) { + context + .buildConstraintViolationWithTemplate(message) + .addPropertyNode(property) + .addConstraintViolation(); } } diff --git a/src/main/java/io/spring/core/article/Article.java b/src/main/java/io/spring/core/article/Article.java index 859ba0bee..a1f040435 100644 --- a/src/main/java/io/spring/core/article/Article.java +++ b/src/main/java/io/spring/core/article/Article.java @@ -49,17 +49,23 @@ public Article( } public void update(String title, String description, String body) { + boolean updated = false; + if (!Util.isNullOrEmpty(title)) { this.title = title; this.slug = toSlug(title); - this.updatedAt = new DateTime(); + updated = true; } if (!Util.isNullOrEmpty(description)) { this.description = description; - this.updatedAt = new DateTime(); + updated = true; } if (!Util.isNullOrEmpty(body)) { this.body = body; + updated = true; + } + + if (updated) { this.updatedAt = new DateTime(); } }