Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/main/java/io/spring/Util.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
package io.spring;

public class Util {
/**
* String utility methods for common operations.
*/
public final class Util {

private Util() {
// Utility class - prevent instantiation
}

/**
* Checks if a string is null or empty.
*
* @param value the string to check
* @return true if the string is null or empty, false otherwise
*/
public static boolean isEmpty(String value) {
return value == null || value.isEmpty();
}
Expand Down
12 changes: 2 additions & 10 deletions src/main/java/io/spring/api/ArticleApi.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.spring.api;

import static io.spring.api.ResponseFactory.articleResponse;

import io.spring.api.exception.NoAuthorizationException;
import io.spring.api.exception.ResourceNotFoundException;
import io.spring.application.ArticleQueryService;
Expand All @@ -10,8 +12,6 @@
import io.spring.core.article.ArticleRepository;
import io.spring.core.service.AuthorizationService;
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;
Expand Down Expand Up @@ -77,12 +77,4 @@ public ResponseEntity deleteArticle(
})
.orElseThrow(ResourceNotFoundException::new);
}

private Map<String, Object> articleResponse(ArticleData articleData) {
return new HashMap<String, Object>() {
{
put("article", articleData);
}
};
}
}
9 changes: 3 additions & 6 deletions src/main/java/io/spring/api/ArticlesApi.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package io.spring.api;

import static io.spring.api.ResponseFactory.articleResponse;

import io.spring.application.ArticleQueryService;
import io.spring.application.Page;
import io.spring.application.article.ArticleCommandService;
import io.spring.application.article.NewArticleParam;
import io.spring.core.article.Article;
import io.spring.core.user.User;
import java.util.HashMap;
import javax.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -30,11 +31,7 @@ public ResponseEntity createArticle(
@Valid @RequestBody NewArticleParam newArticleParam, @AuthenticationPrincipal User user) {
Article article = articleCommandService.createArticle(newArticleParam, user);
return ResponseEntity.ok(
new HashMap<String, Object>() {
{
put("article", articleQueryService.findById(article.getId(), user).get());
}
});
articleResponse(articleQueryService.findById(article.getId(), user).get()));
}

@GetMapping(path = "feed")
Expand Down
20 changes: 4 additions & 16 deletions src/main/java/io/spring/api/CommentsApi.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.spring.api;

import static io.spring.api.ResponseFactory.commentResponse;
import static io.spring.api.ResponseFactory.commentsResponse;

import com.fasterxml.jackson.annotation.JsonRootName;
import io.spring.api.exception.NoAuthorizationException;
import io.spring.api.exception.ResourceNotFoundException;
Expand All @@ -11,9 +14,7 @@
import io.spring.core.comment.CommentRepository;
import io.spring.core.service.AuthorizationService;
import io.spring.core.user.User;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -56,12 +57,7 @@ public ResponseEntity getComments(
Article article =
articleRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new);
List<CommentData> comments = commentQueryService.findByArticleId(article.getId(), user);
return ResponseEntity.ok(
new HashMap<String, Object>() {
{
put("comments", comments);
}
});
return ResponseEntity.ok(commentsResponse(comments));
}

@RequestMapping(path = "{id}", method = RequestMethod.DELETE)
Expand All @@ -83,14 +79,6 @@ public ResponseEntity deleteComment(
})
.orElseThrow(ResourceNotFoundException::new);
}

private Map<String, Object> commentResponse(CommentData commentData) {
return new HashMap<String, Object>() {
{
put("comment", commentData);
}
};
}
}

@Getter
Expand Down
22 changes: 8 additions & 14 deletions src/main/java/io/spring/api/CurrentUserApi.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package io.spring.api;

import static io.spring.api.ResponseFactory.userResponse;
import static io.spring.api.security.TokenExtractor.extractToken;

import io.spring.application.UserQueryService;
import io.spring.application.data.UserData;
import io.spring.application.data.UserWithToken;
import io.spring.application.user.UpdateUserCommand;
import io.spring.application.user.UpdateUserParam;
import io.spring.application.user.UserService;
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;
Expand All @@ -33,26 +34,19 @@ 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 = extractToken(authorization).orElse("");
return ResponseEntity.ok(userResponse(new UserWithToken(userData, token)));
}

@PutMapping
public ResponseEntity updateProfile(
@AuthenticationPrincipal User currentUser,
@RequestHeader("Authorization") String token,
@RequestHeader("Authorization") String authorization,
@Valid @RequestBody UpdateUserParam updateUserParam) {

userService.updateUser(new UpdateUserCommand(currentUser, updateUserParam));
UserData userData = userQueryService.findById(currentUser.getId()).get();
return ResponseEntity.ok(userResponse(new UserWithToken(userData, token.split(" ")[1])));
}

private Map<String, Object> userResponse(UserWithToken userWithToken) {
return new HashMap<String, Object>() {
{
put("user", userWithToken);
}
};
String token = extractToken(authorization).orElse("");
return ResponseEntity.ok(userResponse(new UserWithToken(userData, token)));
}
}
43 changes: 17 additions & 26 deletions src/main/java/io/spring/api/ProfileApi.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package io.spring.api;

import static io.spring.api.ResponseFactory.profileResponse;

import io.spring.api.exception.ResourceNotFoundException;
import io.spring.application.ProfileQueryService;
import io.spring.application.data.ProfileData;
import io.spring.core.user.FollowRelation;
import io.spring.core.user.User;
import io.spring.core.user.UserRepository;
import java.util.HashMap;
import java.util.Optional;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -30,7 +31,7 @@ public ResponseEntity getProfile(
@PathVariable("username") String username, @AuthenticationPrincipal User user) {
return profileQueryService
.findByUsername(username, user)
.map(this::profileResponse)
.map(profile -> ResponseEntity.ok(profileResponse(profile)))
.orElseThrow(ResourceNotFoundException::new);
}

Expand All @@ -43,36 +44,26 @@ public ResponseEntity follow(
target -> {
FollowRelation followRelation = new FollowRelation(user.getId(), target.getId());
userRepository.saveRelation(followRelation);
return profileResponse(profileQueryService.findByUsername(username, user).get());
return ResponseEntity.ok(
profileResponse(profileQueryService.findByUsername(username, user).get()));
})
.orElseThrow(ResourceNotFoundException::new);
}

@DeleteMapping(path = "follow")
public ResponseEntity unfollow(
@PathVariable("username") String username, @AuthenticationPrincipal User user) {
Optional<User> userOptional = userRepository.findByUsername(username);
if (userOptional.isPresent()) {
User target = userOptional.get();
return userRepository
.findRelation(user.getId(), target.getId())
.map(
relation -> {
userRepository.removeRelation(relation);
return profileResponse(profileQueryService.findByUsername(username, user).get());
})
.orElseThrow(ResourceNotFoundException::new);
} else {
throw new ResourceNotFoundException();
}
}

private ResponseEntity profileResponse(ProfileData profile) {
return ResponseEntity.ok(
new HashMap<String, Object>() {
{
put("profile", profile);
}
});
User target = userRepository.findByUsername(username)
.orElseThrow(ResourceNotFoundException::new);

return userRepository
.findRelation(user.getId(), target.getId())
.map(
relation -> {
userRepository.removeRelation(relation);
return ResponseEntity.ok(
profileResponse(profileQueryService.findByUsername(username, user).get()));
})
.orElseThrow(ResourceNotFoundException::new);
}
}
39 changes: 39 additions & 0 deletions src/main/java/io/spring/api/ResponseFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.spring.api;

import java.util.Collections;
import java.util.Map;

/**
* Factory class for creating standardized API response structures.
* Centralizes response creation to follow DRY principle.
*/
public final class ResponseFactory {

private ResponseFactory() {
// Utility class - prevent instantiation
}

public static Map<String, Object> singletonMap(String key, Object value) {
return Collections.singletonMap(key, value);
}

public static Map<String, Object> userResponse(Object userData) {
return singletonMap("user", userData);
}

public static Map<String, Object> articleResponse(Object articleData) {
return singletonMap("article", articleData);
}

public static Map<String, Object> profileResponse(Object profileData) {
return singletonMap("profile", profileData);
}

public static Map<String, Object> commentResponse(Object commentData) {
return singletonMap("comment", commentData);
}

public static Map<String, Object> commentsResponse(Object commentsList) {
return singletonMap("comments", commentsList);
}
}
27 changes: 11 additions & 16 deletions src/main/java/io/spring/api/UsersApi.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.spring.api;

import static io.spring.api.ResponseFactory.userResponse;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

import com.fasterxml.jackson.annotation.JsonRootName;
Expand All @@ -12,8 +13,6 @@
import io.spring.core.service.JwtService;
import io.spring.core.user.User;
import io.spring.core.user.UserRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.validation.Valid;
import javax.validation.constraints.Email;
Expand Down Expand Up @@ -46,23 +45,19 @@ public ResponseEntity createUser(@Valid @RequestBody RegisterParam registerParam

@RequestMapping(path = "/users/login", method = POST)
public ResponseEntity userLogin(@Valid @RequestBody LoginParam loginParam) {
Optional<User> optional = userRepository.findByEmail(loginParam.getEmail());
if (optional.isPresent()
&& passwordEncoder.matches(loginParam.getPassword(), optional.get().getPassword())) {
UserData userData = userQueryService.findById(optional.get().getId()).get();
return ResponseEntity.ok(
userResponse(new UserWithToken(userData, jwtService.toToken(optional.get()))));
} else {
Optional<User> userOptional = userRepository.findByEmail(loginParam.getEmail());
if (!userOptional.isPresent()) {
throw new InvalidAuthenticationException();
}
}

private Map<String, Object> userResponse(UserWithToken userWithToken) {
return new HashMap<String, Object>() {
{
put("user", userWithToken);
}
};
User user = userOptional.get();
if (!passwordEncoder.matches(loginParam.getPassword(), user.getPassword())) {
throw new InvalidAuthenticationException();
}

UserData userData = userQueryService.findById(user.getId()).get();
return ResponseEntity.ok(
userResponse(new UserWithToken(userData, jwtService.toToken(user))));
}
}

Expand Down
22 changes: 6 additions & 16 deletions src/main/java/io/spring/api/security/JwtTokenFilter.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package io.spring.api.security;

import static io.spring.api.security.TokenExtractor.extractToken;

import io.spring.core.service.JwtService;
import io.spring.core.user.UserRepository;
import java.io.IOException;
import java.util.Collections;
import java.util.Optional;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
Expand All @@ -17,15 +18,17 @@

@SuppressWarnings("SpringJavaAutowiringInspection")
public class JwtTokenFilter extends OncePerRequestFilter {

private static final String AUTHORIZATION_HEADER = "Authorization";

@Autowired private UserRepository userRepository;
@Autowired private JwtService jwtService;
private final String header = "Authorization";

@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
getTokenString(request.getHeader(header))
extractToken(request.getHeader(AUTHORIZATION_HEADER))
.flatMap(token -> jwtService.getSubFromToken(token))
.ifPresent(
id -> {
Expand All @@ -46,17 +49,4 @@ protected void doFilterInternal(

filterChain.doFilter(request, response);
}

private Optional<String> 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]);
}
}
}
}
Loading