diff --git a/pom.xml b/pom.xml index e823d390..72be188b 100644 --- a/pom.xml +++ b/pom.xml @@ -40,10 +40,9 @@ org.springframework.boot spring-boot-starter-security - org.springframework.boot - spring-boot-starter-jdbc + spring-boot-starter-data-jpa org.postgresql diff --git a/src/main/java/com/emobile/springtodo/config/AppConfig.java b/src/main/java/com/emobile/springtodo/config/AppConfig.java index d5f1ebc2..858e0b08 100644 --- a/src/main/java/com/emobile/springtodo/config/AppConfig.java +++ b/src/main/java/com/emobile/springtodo/config/AppConfig.java @@ -54,7 +54,7 @@ public JdbcTemplate jdbcTemplate(DataSource dataSource) { } @Bean - public PlatformTransactionManager txManager() { + public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } diff --git a/src/main/java/com/emobile/springtodo/mapper/TaskMapper.java b/src/main/java/com/emobile/springtodo/mapper/TaskMapper.java index 84977410..ccc8fa9a 100644 --- a/src/main/java/com/emobile/springtodo/mapper/TaskMapper.java +++ b/src/main/java/com/emobile/springtodo/mapper/TaskMapper.java @@ -6,6 +6,7 @@ import com.emobile.springtodo.model.dto.task.TaskSaveRequest; import com.emobile.springtodo.model.entity.Task; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; @@ -45,8 +46,7 @@ public interface TaskMapper { * @param model {@link Task} for mapping. * @return mapped {@link TaskResponse}. */ -// @Mapping(target = "createdAt", qualifiedByName = "OffsetDateTimeToString") -// @Mapping(target = "updatedAt", qualifiedByName = "OffsetDateTimeToString") + @Mapping(source = "author.id", target = "authorId") TaskResponse modelToResponse(Task model); diff --git a/src/main/java/com/emobile/springtodo/model/entity/Task.java b/src/main/java/com/emobile/springtodo/model/entity/Task.java index 88ad60b8..36c61fc3 100644 --- a/src/main/java/com/emobile/springtodo/model/entity/Task.java +++ b/src/main/java/com/emobile/springtodo/model/entity/Task.java @@ -1,5 +1,15 @@ package com.emobile.springtodo.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; @@ -22,35 +32,54 @@ @Getter @FieldNameConstants @Builder +@Entity +@Table(name = "tasks") public class Task implements Serializable { @Serial private static final long serialVersionUID = 1L; /** * Long Task id. */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = Fields.id) private Long id; /** * Task name. */ + @Column(name = Fields.name) private String name; /** * Task description. */ + @Column(name = Fields.description) private String description; /** * Task {@link TaskStatus} status. */ + @Enumerated(EnumType.STRING) + @Column(name = Fields.status) private TaskStatus status; /** * Task creation time without timezone. */ + @Column(name = "created_at") private LocalDateTime createdAt; /** * Task update time without timezone. */ + @Column(name = "updated_at") private LocalDateTime updatedAt; /** - * The User id who owns the Task. + * The User who owns the Task. */ - private Long authorId; + @EqualsAndHashCode.Exclude + @ManyToOne + @JoinColumn(name = "author_id", referencedColumnName = "id") + private User author; + + @EqualsAndHashCode.Include + public Long getAuthorId() { + return author.getId(); + } } diff --git a/src/main/java/com/emobile/springtodo/model/entity/User.java b/src/main/java/com/emobile/springtodo/model/entity/User.java index 9ab52043..e3a56866 100644 --- a/src/main/java/com/emobile/springtodo/model/entity/User.java +++ b/src/main/java/com/emobile/springtodo/model/entity/User.java @@ -1,6 +1,7 @@ package com.emobile.springtodo.model.entity; import com.emobile.springtodo.model.security.RoleType; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; @@ -24,32 +25,52 @@ @Getter @FieldNameConstants @Builder +@Entity +@NamedEntityGraph( + name = "User.withTasks", + attributeNodes = @NamedAttributeNode("taskList") +) +@Table(name = "users", + indexes = { + @Index(columnList = User.Fields.username, unique = true), + @Index(columnList = User.Fields.email, unique = true) + }) public class User implements Serializable { @Serial private static final long serialVersionUID = 1L; /** * Long User id. */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = Fields.id) private Long id; /** * User name. */ + @Column(name = Fields.username) private String username; /** * User password. */ + @Column(name = Fields.password) private String password; /** * User email. */ + @Column(name = Fields.email) private String email; /** * User authorization role type. */ + @Enumerated(EnumType.STRING) + @Column(name = Fields.role) private RoleType role; /** * User tasks. */ + @ToString.Exclude + @OneToMany(mappedBy = Task.Fields.author, orphanRemoval = true) @Builder.Default private List taskList = new ArrayList<>(); } diff --git a/src/main/java/com/emobile/springtodo/model/util/PageInfo.java b/src/main/java/com/emobile/springtodo/model/util/PageInfo.java index 46f65ff0..0836f048 100644 --- a/src/main/java/com/emobile/springtodo/model/util/PageInfo.java +++ b/src/main/java/com/emobile/springtodo/model/util/PageInfo.java @@ -33,5 +33,5 @@ public record PageInfo( /** * Minimum size of the number field. */ - private static final int MIN_NUMBER_SIZE = 1; + private static final int MIN_NUMBER_SIZE = 0; } diff --git a/src/main/java/com/emobile/springtodo/repository/CrudRepository.java b/src/main/java/com/emobile/springtodo/repository/CrudRepository.java deleted file mode 100644 index 79cb3d45..00000000 --- a/src/main/java/com/emobile/springtodo/repository/CrudRepository.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.emobile.springtodo.repository; - -import com.emobile.springtodo.model.util.Page; -import com.emobile.springtodo.model.util.PageInfo; - -import java.util.Optional; - -/** - * Default CRUD interface repository for working with entity {@link T}. - * - * @param entity for work - */ -public interface CrudRepository { - /** - * Find all {@link T} objects from db - * with {@code pageNumber} and {@code pageSize} from {@code pageInfo}. - * - * @return {@link Page} with {@link T} list. - */ - Page findAll(PageInfo pageinfo); - - /** - * Search object {@link T} in db. - * - * @param id id searched {@link T} object - * @return {@link Optional} if exist, {@link Optional#empty()} if not - */ - Optional findById(Long id); - - /** - * Save object model of type {@link T}. - * - * @param model object of type {@link T} to save - * @return object of type {@link T} that was saved - */ - T save(T model); - - /** - * Update object model of type {@link T} - * by {@code T.id} value. - * - * @param model object of type {@link T} to update - * @return object of type {@link T} that was updated - */ - T update(T model); - - /** - * Delete object with {@code T.id} - * equals {@code id} from database. - * - * @param id id of the object to be deleted - * @return {@code true} if success - */ - boolean deleteById(Long id); -} diff --git a/src/main/java/com/emobile/springtodo/repository/TaskRepository.java b/src/main/java/com/emobile/springtodo/repository/TaskRepository.java deleted file mode 100644 index a1b9b6ee..00000000 --- a/src/main/java/com/emobile/springtodo/repository/TaskRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.emobile.springtodo.repository; - -import com.emobile.springtodo.model.entity.Task; - -/** - * Default interface repository for working with entity {@link Task}. - */ -public interface TaskRepository extends CrudRepository { - /** - * Delete all {@link Task} objects in database. - * - * @return {@code true} if success - */ - boolean deleteAll(); - - /** - * Delete all {@link Task} objects in database - * for user with {@code userId}. - * - * @return {@code true} if success - */ - boolean deleteAllByUserId(Long authorId); -} diff --git a/src/main/java/com/emobile/springtodo/repository/UserRepository.java b/src/main/java/com/emobile/springtodo/repository/UserRepository.java deleted file mode 100644 index a6693088..00000000 --- a/src/main/java/com/emobile/springtodo/repository/UserRepository.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.emobile.springtodo.repository; - -import com.emobile.springtodo.model.entity.User; - -import java.util.Optional; - -/** - * Default interface repository for working with entity {@link User}. - */ -public interface UserRepository extends CrudRepository { - /** - * Find {@link User} - * with {@code User.username} equals {@code username}. - * - * @param username username searched {@link User} - * @return {@link Optional} if exist, {@link Optional#empty()} if not - */ - Optional findByUsername(String username); - - /** - * Check duplicate {@code username}. - * For save {@link User}. - * - * @param username username searched {@link User} - * @return {@code true} if exist, {@code false} if not - */ - boolean existsByUsername(String username); - - /** - * Check duplicate {@code username}. - * For update {@link User}. - * - * @param username username searched {@link User} - * @return {@code true} if exist, {@code false} if not - */ - boolean existsByUsernameAndIdNot(String username, Long currentUserId); - - /** - * Check duplicate {@code email}. - * For save {@link User}. - * - * @param email username searched {@link User} - * @return {@code true} if exist, {@code false} if not - */ - boolean existsByEmail(String email); - - /** - * Check duplicate {@code email}. - * For update {@link User}. - * - * @param email username searched {@link User} - * @return {@code true} if exist, {@code false} if not - */ - boolean existsByEmailAndIdNot(String email, Long currentUserId); -} diff --git a/src/main/java/com/emobile/springtodo/repository/impl/TaskRepositoryImpl.java b/src/main/java/com/emobile/springtodo/repository/impl/TaskRepositoryImpl.java deleted file mode 100644 index d4821f6e..00000000 --- a/src/main/java/com/emobile/springtodo/repository/impl/TaskRepositoryImpl.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.emobile.springtodo.repository.impl; - -import com.emobile.springtodo.aop.LazyLogger; -import com.emobile.springtodo.exception.NullResultSetException; -import com.emobile.springtodo.model.entity.Task; -import com.emobile.springtodo.model.entity.TaskStatus; -import com.emobile.springtodo.model.util.Page; -import com.emobile.springtodo.model.util.PageInfo; -import com.emobile.springtodo.repository.TaskRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -import static com.emobile.springtodo.util.JdbcUtil.optionalExtractor; - -/** - * Repository for working with entity {@link Task}. - */ -@RequiredArgsConstructor -@Repository -public class TaskRepositoryImpl implements TaskRepository { - /** - * For work with db. - */ - private final JdbcTemplate jdbcTemplate; - - /** - * Find all {@link Task} objects from db - * with {@code pageNumber} and {@code pageSize} from {@code pageInfo}.
- * LIMIT - {@code pageSize}.
- * OFFSET - ({@code pageNumber} -1) * {@code pageSize}. - * - * @return {@link Page} with {@link Task} list. - * @see #getMapper() - */ - @Override - @LazyLogger - public Page findAll(PageInfo pageInfo) { - final String sql = """ - SELECT * FROM tasks - LIMIT ? OFFSET ? - """; - int beginInd = (pageInfo.pageNumber() - 1) * pageInfo.pageSize(); - return new Page<>( - jdbcTemplate.query(sql, - getMapper(), - pageInfo.pageSize(), - beginInd - ) - ); - } - - /** - * Search object {@link Task} in db. - * - * @param id id searched {@link Task} object - * @return {@link Optional} if exist, {@link Optional#empty()} if not - * @see #getMapper() - */ - @Override - @LazyLogger - public Optional findById(Long id) { - final String sql = """ - SELECT * - FROM tasks - WHERE id=? - """; - return jdbcTemplate.query( - sql, - optionalExtractor(getMapper()), - id - ); - } - - /** - * Save object model of type {@link Task}. - * - * @param model object of type {@link Task} to save - * @return object of type {@link Task} that was saved - * @throws NullResultSetException if returning value from db is null - */ - @Override - @LazyLogger - public Task save(Task model) { - final String sql = """ - INSERT INTO tasks(name, description, status, - created_at, updated_at, author_id) - VALUES (?,?,?,?,?,?) - RETURNING * - """; - return jdbcTemplate.query(sql, - optionalExtractor(getMapper()), - model.getName(), - model.getDescription(), - model.getStatus().name(), - model.getCreatedAt(), - model.getUpdatedAt(), - model.getAuthorId() - ).orElseThrow(NullResultSetException.create( - "taskRepository save() query exception" - )); - } - - /** - * Update object model of type {@link Task} - * by {@code T.id} value. - * - * @param model object of type {@link Task} to update - * @return object of type {@link Task} that was updated - * @throws NullResultSetException if returning value from db is null - */ - @Override - @LazyLogger - public Task update(Task model) { - final String sql = """ - UPDATE tasks - SET name=?, description=?, status=?, updated_at=?, author_id =? - WHERE id=? - RETURNING * - """; - return jdbcTemplate.query(sql, - optionalExtractor(getMapper()), - model.getName(), - model.getDescription(), - model.getStatus().name(), - model.getUpdatedAt(), - model.getAuthorId(), - model.getId() - ).orElseThrow(NullResultSetException.create( - "taskRepository update() query exception" - )); - } - - /** - * Delete object with {@code Task.id} - * equals {@code id} from database. - * - * @param id id of the object to be deleted - * @return {@code true} if success - */ - @Override - @LazyLogger - public boolean deleteById(Long id) { - final String sql = """ - DELETE - FROM tasks - WHERE id=? - """; - int result = jdbcTemplate.update(sql, id); - return result != 0; - } - - /** - * Delete all {@link Task} objects in database. - * - * @return {@code true} if success - */ - @Override - @LazyLogger - public boolean deleteAll() { - final String sql = "TRUNCATE tasks"; - int result = jdbcTemplate.update(sql); - return result != 0; - } - - /** - * Delete all {@link Task} objects in database - * for user with {@code userId}. - * - * @return {@code true} if success - */ - @Override - @LazyLogger - public boolean deleteAllByUserId(Long userId) { - final String sql = """ - DELETE - FROM tasks - WHERE author_id=? - """; - int result = jdbcTemplate.update(sql, userId); - return result != 0; - } - - /** - * {@link Task} {@link RowMapper}. - * - * @return mapper for {@link Task} entity - */ - private RowMapper getMapper() { - return (rs, rn) -> Task.builder() - .id(rs.getLong(Task.Fields.id)) - .name(rs.getString(Task.Fields.name)) - .description(rs.getString(Task.Fields.description)) - .status(TaskStatus.valueOf(rs.getString(Task.Fields.status))) - .createdAt(rs.getTimestamp("created_at") - .toLocalDateTime()) - .updatedAt(rs.getTimestamp("updated_at") - .toLocalDateTime()) - .authorId(rs.getLong("author_id")) - .build(); - } -} diff --git a/src/main/java/com/emobile/springtodo/repository/impl/UserRepositoryImpl.java b/src/main/java/com/emobile/springtodo/repository/impl/UserRepositoryImpl.java deleted file mode 100644 index 1b8aad8d..00000000 --- a/src/main/java/com/emobile/springtodo/repository/impl/UserRepositoryImpl.java +++ /dev/null @@ -1,366 +0,0 @@ -package com.emobile.springtodo.repository.impl; - -import com.emobile.springtodo.aop.LazyLogger; -import com.emobile.springtodo.exception.NullResultSetException; -import com.emobile.springtodo.model.entity.Task; -import com.emobile.springtodo.model.entity.TaskStatus; -import com.emobile.springtodo.model.entity.User; -import com.emobile.springtodo.model.security.RoleType; -import com.emobile.springtodo.model.util.Page; -import com.emobile.springtodo.model.util.PageInfo; -import com.emobile.springtodo.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.stereotype.Repository; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static com.emobile.springtodo.util.JdbcUtil.optionalExtractor; - -/** - * Repository for working with entity {@link User}. - */ -@RequiredArgsConstructor -@Repository -public class UserRepositoryImpl implements UserRepository { - /** - * For work with db. - */ - private final JdbcTemplate jdbcTemplate; - - /** - * Find all {@link User} objects from db - * with {@code pageNumber} and {@code pageSize} from {@code pageInfo}.
- * LIMIT - {@code pageSize}.
- * OFFSET - ({@code pageNumber} -1) * {@code pageSize}. - * - * @return {@link Page} with {@link User} list. - * @see #getUserMapper() - */ - @Override - @LazyLogger - public Page findAll(PageInfo pageInfo) { - final String sql = """ - SELECT * FROM users - LIMIT ? OFFSET ? - """; - int beginInd = (pageInfo.pageNumber() - 1) * pageInfo.pageSize(); - return new Page<>( - jdbcTemplate.query( - sql, - getUserMapper(), - pageInfo.pageSize(), - beginInd - ) - ); - } - - /** - * Search object {@link User} in db. - * - * @param id id searched {@link User} object - * @return {@link Optional} if exist, {@link Optional#empty()} if not - * @see #getUserTaskMapper() - */ - @Override - @LazyLogger - public Optional findById(Long id) { - final String sql = """ - SELECT u.*, t.* - FROM users u - LEFT JOIN tasks t on u.id = t.author_id - WHERE u.id= ? - """; - return jdbcTemplate.query( - sql, - optionalExtractor(getUserTaskMapper()), - id - ); - } - - /** - * Find {@link User} - * with {@code User.username} equals {@code username}. - * - * @param username username searched {@link User} - * @return {@link Optional} if exist, {@link Optional#empty()} if not - */ - @Override - @LazyLogger - public Optional findByUsername(String username) { - final String sql = """ - SELECT u.*, t.* - FROM users u - LEFT JOIN tasks t on u.id = t.author_id - WHERE u.username= ? - """; - return jdbcTemplate.query( - sql, - optionalExtractor(getUserTaskMapper()), - username - ); - } - - /** - * Save object model of type {@link User}. - * - * @param model object of type {@link User} to save - * @return object of type {@link User} that was saved - * @throws NullResultSetException if returning value from db is null - */ - @Override - @LazyLogger - public User save(User model) { - final String sql = """ - INSERT INTO users(username, password, email, role) - VALUES (?,?,?,?) - RETURNING * - """; - return jdbcTemplate.query(sql, - optionalExtractor(getUserMapper()), - model.getUsername(), - model.getPassword(), - model.getEmail(), - model.getRole().name()) - .orElseThrow(NullResultSetException.create( - "userRepository save() query exception" - )); - } - - /** - * Update object model of type {@link User} - * by {@code T.id} value. - * - * @param model object of type {@link User} to update - * @return object of type {@link User} that was updated - * @throws NullResultSetException if returning value from db is null - */ - @Override - @LazyLogger - public User update(User model) { - final String sql = """ - UPDATE users - SET username=?, password=?, email=?, role=? - WHERE id=? - RETURNING * - """; - return jdbcTemplate.query(sql, - optionalExtractor(getUserMapper()), - model.getUsername(), - model.getPassword(), - model.getEmail(), - model.getRole().name(), - model.getId()) - .orElseThrow(NullResultSetException.create( - "userRepository update() query exception" - )); - } - - /** - * Delete object with {@code User.id} - * equals {@code id} from database. - * - * @param id id of the object to be deleted - * @return {@code true} if success - */ - @Override - @LazyLogger - public boolean deleteById(Long id) { - final String sql = """ - DELETE - FROM users - WHERE id=? - """; - int result = jdbcTemplate.update(sql, id); - return result != 0; - } - - /** - * Check duplicate {@code username}. - * For save {@link User}. - * - * @param username username searched {@link User} - * @return {@code true} if exist, {@code false} if not - * @see #getExistsMapper() - */ - @Override - @LazyLogger - public boolean existsByUsername(String username) { - final String sql = """ - SELECT EXISTS (SELECT * - FROM users - WHERE username = ?) - AS result - """; - return jdbcTemplate.queryForObject(sql, getExistsMapper(), username); - } - - /** - * Check duplicate {@code username}. - * For update {@link User}. - * - * @param username username searched {@link User} - * @return {@code true} if exist, {@code false} if not - * @see #getExistsMapper() - */ - @Override - @LazyLogger - public boolean existsByUsernameAndIdNot(String username, Long currentUserId) { - final String sql = """ - SELECT EXISTS (SELECT * - FROM users - WHERE username = ? - AND id != ?) - AS result - """; - return jdbcTemplate.queryForObject( - sql, - getExistsMapper(), - username, - currentUserId - ); - } - - /** - * Check duplicate {@code email}. - * For save {@link User}. - * - * @param email username searched {@link User} - * @return {@code true} if exist, {@code false} if not - * @see #getExistsMapper() - */ - @Override - @LazyLogger - public boolean existsByEmail(String email) { - final String sql = """ - SELECT EXISTS (SELECT * - FROM users - WHERE email = ?) - AS result - """; - return jdbcTemplate.queryForObject(sql, getExistsMapper(), email); - } - - /** - * Check duplicate {@code email}. - * For update {@link User}. - * - * @param email username searched {@link User} - * @return {@code true} if exist, {@code false} if not - * @see #getExistsMapper() - */ - @Override - @LazyLogger - public boolean existsByEmailAndIdNot(String email, Long currentUserId) { - final String sql = """ - SELECT EXISTS (SELECT * - FROM users - WHERE email = ? - AND id != ?) - AS result - """; - return jdbcTemplate.queryForObject( - sql, - getExistsMapper(), - email, - currentUserId - ); - } - - /** - * {@link User} {@link RowMapper}. - * - * @return mapper for {@link User} entity - */ - private RowMapper getUserMapper() { - return (rs, rn) -> User.builder() - .id(rs.getLong(User.Fields.id)) - .username(rs.getString(User.Fields.username)) - .password(rs.getString(User.Fields.password)) - .email(rs.getString(User.Fields.email)) - .role(RoleType.valueOf(rs.getString(User.Fields.role))) - .build(); - } - - /** - * Грустно и костыльно, но работает. - *
- * {code rs.getLong("author_id")} for wasNull() check. - * - * @return mapper for {@link User} with filled {@code taskList} fields - * @see #fillUserFields(ResultSet, List) - * @see #fillTaskFields(ResultSet) - */ - private RowMapper getUserTaskMapper() { - return (rs, rn) -> { - List taskList = new ArrayList<>(); - User user = fillUserFields(rs, taskList); - rs.getLong("author_id"); - if (!rs.wasNull()) { - do { - Task task = fillTaskFields(rs); - taskList.add(task); - } while (rs.next()); - } - return user; - }; - } - - /** - * fill {@link User} entity. - * {@code id} columnIndex - 1. - * - * @param rs resultSet for map - * @param taskList list ref - * @return {@link User} with filled fields - * @throws SQLException if resultSet get exception - */ - private User fillUserFields(ResultSet rs, List taskList) throws SQLException { - return User.builder() - .id(rs.getLong(1)) - .username(rs.getString(User.Fields.username)) - .password(rs.getString(User.Fields.password)) - .email(rs.getString(User.Fields.email)) - .role(RoleType.valueOf(rs.getString(User.Fields.role))) - .taskList(taskList) - .build(); - } - - /** - * fill {@link Task} entity. - * {@code id} columnIndex - 6. - * - * @param rs resultSet for map - * @return {@link Task} with filled fields - * @throws SQLException if resultSet get exception - */ - private Task fillTaskFields(ResultSet rs) throws SQLException { - return Task.builder() - .id(rs.getLong(6)) - .name(rs.getString(Task.Fields.name)) - .description(rs.getString(Task.Fields.description)) - .status(TaskStatus.valueOf(rs.getString(Task.Fields.status))) - .createdAt(rs.getTimestamp("created_at") - .toLocalDateTime()) - .updatedAt(rs.getTimestamp("updated_at") - .toLocalDateTime()) - .authorId(rs.getLong("author_id")) - .build(); - } - - /** - * {@link RowMapper} for exist check. - * - * @return mapper for exist check - */ - private RowMapper getExistsMapper() { - return (resultSet, rowNum) -> - resultSet.getString("result") - .equals("t"); - } -} diff --git a/src/main/java/com/emobile/springtodo/repository/jpa/TaskRepository.java b/src/main/java/com/emobile/springtodo/repository/jpa/TaskRepository.java new file mode 100644 index 00000000..50c7e5bf --- /dev/null +++ b/src/main/java/com/emobile/springtodo/repository/jpa/TaskRepository.java @@ -0,0 +1,10 @@ +package com.emobile.springtodo.repository.jpa; + +import com.emobile.springtodo.model.entity.Task; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TaskRepository extends JpaRepository { + void deleteAllByAuthor_Id(Long authorId); +} diff --git a/src/main/java/com/emobile/springtodo/repository/jpa/UserRepository.java b/src/main/java/com/emobile/springtodo/repository/jpa/UserRepository.java new file mode 100644 index 00000000..249a1e35 --- /dev/null +++ b/src/main/java/com/emobile/springtodo/repository/jpa/UserRepository.java @@ -0,0 +1,24 @@ +package com.emobile.springtodo.repository.jpa; + +import com.emobile.springtodo.model.entity.User; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserRepository extends JpaRepository { + @EntityGraph(value = "User.withTasks", type = EntityGraph.EntityGraphType.FETCH) + Optional findById(Long id); + + Optional findByUsername(String username); + + boolean existsByUsername(String username); + + boolean existsByUsernameAndIdNot(String username, Long currentUserId); + + boolean existsByEmail(String email); + + boolean existsByEmailAndIdNot(String email, Long currentUserId); +} diff --git a/src/main/java/com/emobile/springtodo/service/impl/TaskServiceImpl.java b/src/main/java/com/emobile/springtodo/service/impl/TaskServiceImpl.java index 71594d79..3336cde3 100644 --- a/src/main/java/com/emobile/springtodo/service/impl/TaskServiceImpl.java +++ b/src/main/java/com/emobile/springtodo/service/impl/TaskServiceImpl.java @@ -4,10 +4,12 @@ import com.emobile.springtodo.config.property.cache.AppCacheProperties; import com.emobile.springtodo.exception.EntityNotFoundException; import com.emobile.springtodo.model.entity.Task; +import com.emobile.springtodo.model.entity.User; import com.emobile.springtodo.model.security.AppUserDetails; import com.emobile.springtodo.model.util.PageInfo; -import com.emobile.springtodo.repository.TaskRepository; +import com.emobile.springtodo.repository.jpa.TaskRepository; import com.emobile.springtodo.service.TaskService; +import com.emobile.springtodo.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; @@ -16,6 +18,7 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.context.annotation.Lazy; +import org.springframework.data.domain.PageRequest; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -42,6 +45,14 @@ public class TaskServiceImpl implements TaskService { * {@link Task} Repository. */ private final TaskRepository taskRepository; + /** + * {@link User} Service. + * To fill author field. + * + * @see #enrich(Task) + * @see #enrich(Task, Task) + */ + private final UserService userService; /** * Time management object. */ @@ -64,7 +75,9 @@ public void setSelf(TaskService self) { @Transactional(readOnly = true) @LazyLogger public List findAll(PageInfo pageInfo) { - return taskRepository.findAll(pageInfo).content(); + return taskRepository.findAll( + PageRequest.of(pageInfo.pageNumber(), pageInfo.pageSize()) + ).getContent(); } /** @@ -129,7 +142,7 @@ public Task save(Task model) { @LazyLogger public Task update(Long id, Task model) { model = enrich(model, self.findById(id)); - return taskRepository.update(model); + return taskRepository.save(model); } /** @@ -145,7 +158,7 @@ private Task enrich(Task model) { .status(model.getStatus()) .createdAt(LocalDateTime.now(clock)) .updatedAt(LocalDateTime.now(clock)) - .authorId(getCurrentUserId()) + .author(userService.findById(getCurrentUserId())) .build(); } @@ -180,7 +193,7 @@ private Task enrich(Task model, Task taskToUpdate) { : model.getStatus()) .createdAt(taskToUpdate.getCreatedAt()) .updatedAt(LocalDateTime.now(clock)) - .authorId(taskToUpdate.getAuthorId()) + .author(userService.findById(taskToUpdate.getAuthorId())) .build(); } @@ -236,6 +249,6 @@ public void deleteAll() { @Transactional @LazyLogger public void deleteAllByUserId(Long userId) { - taskRepository.deleteAllByUserId(userId); + taskRepository.deleteAllByAuthor_Id(userId); } } diff --git a/src/main/java/com/emobile/springtodo/service/impl/UserServiceImpl.java b/src/main/java/com/emobile/springtodo/service/impl/UserServiceImpl.java index 23687951..665539e6 100644 --- a/src/main/java/com/emobile/springtodo/service/impl/UserServiceImpl.java +++ b/src/main/java/com/emobile/springtodo/service/impl/UserServiceImpl.java @@ -4,11 +4,9 @@ import com.emobile.springtodo.config.property.cache.AppCacheProperties; import com.emobile.springtodo.exception.AlreadyExitsException; import com.emobile.springtodo.exception.EntityNotFoundException; -import com.emobile.springtodo.model.entity.Task; import com.emobile.springtodo.model.entity.User; import com.emobile.springtodo.model.util.PageInfo; -import com.emobile.springtodo.repository.UserRepository; -import com.emobile.springtodo.service.TaskService; +import com.emobile.springtodo.repository.jpa.UserRepository; import com.emobile.springtodo.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @@ -18,6 +16,7 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.context.annotation.Lazy; +import org.springframework.data.domain.PageRequest; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -42,11 +41,6 @@ public class UserServiceImpl implements UserService { * {@link User} Repository. */ private final UserRepository userRepository; - /** - * {@link Task} Service. - * To delete all task deleted user. - */ - private final TaskService taskService; /** * Default PasswordEncoder. * Needed to define and update the field password in {@link User}. @@ -73,7 +67,9 @@ public void setSelf(UserService self) { @Transactional(readOnly = true) @LazyLogger public List findAll(PageInfo pageInfo) { - return userRepository.findAll(pageInfo).content(); + return userRepository.findAll( + PageRequest.of(pageInfo.pageNumber(), pageInfo.pageSize()) + ).getContent(); } /** @@ -157,7 +153,7 @@ public User update(Long id, User model) { model = enrich(model, self.findById(id)); self.checkDuplicateUsername(model.getUsername(), model.getId()); self.checkDuplicateEmail(model.getEmail(), model.getId()); - return userRepository.update(model); + return userRepository.save(model); } /** @@ -298,13 +294,14 @@ public void checkDuplicateEmail(String email, Long currentUserId) { @Caching(evict = { @CacheEvict(value = AppCacheProperties.Types.USER_BY_NAME, allEntries = true), @CacheEvict(value = AppCacheProperties.Types.USER_BY_ID, key = "#id"), - @CacheEvict(value = AppCacheProperties.Types.USERS, allEntries = true) + @CacheEvict(value = AppCacheProperties.Types.USERS, allEntries = true), + @CacheEvict(value = AppCacheProperties.Types.TASKS, allEntries = true), + @CacheEvict(value = AppCacheProperties.Types.TASK_BY_ID, allEntries = true) }) @Transactional @LazyLogger public void deleteById(Long id) { self.findById(id); - taskService.deleteAllByUserId(id); userRepository.deleteById(id); } } diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index c2fee0c1..fccc94e2 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -6,6 +6,9 @@ spring: url: ${SERVER_URL:jdbc:postgresql://localhost:5432/spring_todo_db} username: ${SERVER_USERNAME:postgres} password: ${SERVER_PASS:1322} + jpa: + show-sql: true + open-in-view: true data: redis: port: ${REDIS_PORT:6379} diff --git a/src/test/java/com/emobile/springtodo/repository/impl/TaskRepositoryImplTest.java b/src/test/java/com/emobile/springtodo/repository/jpa/TaskRepositoryTest.java similarity index 87% rename from src/test/java/com/emobile/springtodo/repository/impl/TaskRepositoryImplTest.java rename to src/test/java/com/emobile/springtodo/repository/jpa/TaskRepositoryTest.java index cd45bfda..321da653 100644 --- a/src/test/java/com/emobile/springtodo/repository/impl/TaskRepositoryImplTest.java +++ b/src/test/java/com/emobile/springtodo/repository/jpa/TaskRepositoryTest.java @@ -1,18 +1,20 @@ -package com.emobile.springtodo.repository.impl; +package com.emobile.springtodo.repository.jpa; import com.emobile.springtodo.model.entity.Task; import com.emobile.springtodo.model.entity.TaskStatus; import com.emobile.springtodo.model.entity.User; import com.emobile.springtodo.model.security.RoleType; -import com.emobile.springtodo.model.util.Page; -import com.emobile.springtodo.model.util.PageInfo; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.test.context.DynamicPropertyRegistry; @@ -33,9 +35,12 @@ @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @Testcontainers -@DisplayName("TaskRepositoryImplTest Tests") -class TaskRepositoryImplTest { - private TaskRepositoryImpl repository; +@DisplayName("TaskRepositoryTest Tests") +class TaskRepositoryTest { + @Autowired + private TaskRepository repository; + @Autowired + EntityManager entityManager; @Autowired private JdbcTemplate jdbcTemplate; private static final LocalDateTime MILLENNIUM = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0, 0); @@ -58,26 +63,19 @@ static void afterAll() { postgreContainer.stop(); } - @BeforeEach - void setUp() { - repository = new TaskRepositoryImpl(jdbcTemplate); - jdbcTemplate.update("TRUNCATE tasks CASCADE"); - jdbcTemplate.update("TRUNCATE users CASCADE"); - } - @Test @DisplayName("findAll test: get all user data.") void givenPageInfoWhenGetAllThenListUser() { - PageInfo pageInfo = new PageInfo(5, 1); - Task test1 = new Task(2L, "name", "des", - TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, 3L); - Task test2 = new Task(4L, "name", "des", - TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, 2L); - Page expected = new Page<>(List.of(test1, test2)); + Pageable pageInfo = PageRequest.of(0, 5); User testAuthor = new User(3L, "user", "pass1", "email1@co.m", RoleType.ROLE_USER, List.of()); User testAuthor2 = new User(2L, "user2", "pass1", "email2@co.m", RoleType.ROLE_USER, List.of()); + Task test1 = new Task(2L, "name", "des", + TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, testAuthor); + Task test2 = new Task(4L, "name", "des", + TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, testAuthor2); + PageImpl expected = new PageImpl<>(List.of(test1, test2), pageInfo, 2); addToDb(testAuthor); addToDb(testAuthor2); addToDb(test1); @@ -92,10 +90,10 @@ void givenPageInfoWhenGetAllThenListUser() { @DisplayName("findById test: get user data by id.") void givenExistingIdWhenGetByIdThenUser() { Long taskId = 2L; - Task test1 = new Task(taskId, "name", "des", - TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, 4L); User testAuthor = new User(4L, "user", "pass1", "email1@co.m", RoleType.ROLE_USER, List.of()); + Task test1 = new Task(taskId, "name", "des", + TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, testAuthor); addToDb(testAuthor); addToDb(test1); @@ -118,16 +116,17 @@ void givenNotExistingIdWhenGetByIdThenThrow() { @Test @DisplayName("update test: send task data to repository.") void givenTaskWhenUpdateThenUpdatedTask() { - Task test1 = new Task(2L, "name", "des", - TaskStatus.TODO, BEFORE_MILLENNIUM, BEFORE_MILLENNIUM, 3L); - Task expected = new Task(2L, "name2", "des2", - TaskStatus.DONE, BEFORE_MILLENNIUM, MILLENNIUM, 3L); User testAuthor = new User(3L, "user", "pass1", "email1@co.m", RoleType.ROLE_USER, List.of()); + Task test1 = new Task(2L, "name", "des", + TaskStatus.TODO, BEFORE_MILLENNIUM, BEFORE_MILLENNIUM, testAuthor); + Task expected = new Task(2L, "name2", "des2", + TaskStatus.DONE, BEFORE_MILLENNIUM, MILLENNIUM, testAuthor); addToDb(testAuthor); addToDb(test1); - repository.update(expected); + repository.save(expected); + entityManager.flush(); assertTrue(existsInDb(expected)); } @@ -136,14 +135,16 @@ void givenTaskWhenUpdateThenUpdatedTask() { @DisplayName("delete test: delete task data message to repository.") void givenTaskIdWhenDeleteThenVoid() { Long taskId = 2L; - Task test1 = new Task(taskId, "name", "des", - TaskStatus.TODO, BEFORE_MILLENNIUM, BEFORE_MILLENNIUM, 3L); User testAuthor = new User(3L, "user", "pass1", "email1@co.m", RoleType.ROLE_USER, List.of()); + Task test1 = new Task(taskId, "name", "des", + TaskStatus.TODO, BEFORE_MILLENNIUM, BEFORE_MILLENNIUM, testAuthor); addToDb(testAuthor); addToDb(test1); repository.deleteById(taskId); + entityManager.flush(); + assertFalse(existsInDb(test1)); } @@ -166,7 +167,7 @@ INSERT INTO tasks(id, name, description, status, created_at, updated_at, author_ jdbcTemplate.update(sql, task.getId(), task.getName(), task.getDescription(), task.getStatus().name(), task.getCreatedAt(), - task.getUpdatedAt(), task.getAuthorId() + task.getUpdatedAt(), task.getAuthor().getId() ); } @@ -185,7 +186,7 @@ SELECT EXISTS (SELECT * return jdbcTemplate.queryForObject(sql, getExistsMapper(), task.getName(), task.getDescription(), task.getStatus().name(), task.getCreatedAt(), - task.getUpdatedAt(), task.getAuthorId() + task.getUpdatedAt(), task.getAuthor().getId() ); } diff --git a/src/test/java/com/emobile/springtodo/repository/impl/UserRepositoryImplTest.java b/src/test/java/com/emobile/springtodo/repository/jpa/UserRepositoryTest.java similarity index 86% rename from src/test/java/com/emobile/springtodo/repository/impl/UserRepositoryImplTest.java rename to src/test/java/com/emobile/springtodo/repository/jpa/UserRepositoryTest.java index d3a928cd..87a526b5 100644 --- a/src/test/java/com/emobile/springtodo/repository/impl/UserRepositoryImplTest.java +++ b/src/test/java/com/emobile/springtodo/repository/jpa/UserRepositoryTest.java @@ -1,18 +1,20 @@ -package com.emobile.springtodo.repository.impl; +package com.emobile.springtodo.repository.jpa; import com.emobile.springtodo.model.entity.Task; import com.emobile.springtodo.model.entity.TaskStatus; import com.emobile.springtodo.model.entity.User; import com.emobile.springtodo.model.security.RoleType; -import com.emobile.springtodo.model.util.Page; -import com.emobile.springtodo.model.util.PageInfo; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.test.context.DynamicPropertyRegistry; @@ -33,9 +35,12 @@ @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @Testcontainers -@DisplayName("UserRepositoryImpl Tests") -class UserRepositoryImplTest { - private UserRepositoryImpl repository; +@DisplayName("UserRepository Tests") +class UserRepositoryTest { + @Autowired + private UserRepository repository; + @Autowired + EntityManager entityManager; @Autowired private JdbcTemplate jdbcTemplate; private static final LocalDateTime MILLENNIUM = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0, 0); @@ -57,21 +62,15 @@ static void afterAll() { postgreContainer.stop(); } - @BeforeEach - void setUp() { - repository = new UserRepositoryImpl(jdbcTemplate); - jdbcTemplate.update("TRUNCATE users CASCADE"); - } - @Test @DisplayName("findAll test: get all user data.") void givenPageInfoWhenGetAllThenListUser() { - PageInfo pageInfo = new PageInfo(5, 1); + Pageable pageInfo = PageRequest.of(0, 5); User test1 = new User(2L, "test1", "pass1", "email1@co.m", RoleType.ROLE_USER, List.of()); User test2 = new User(3L, "test2", "pass2", "email2@co.m", RoleType.ROLE_ADMIN, List.of()); - Page expected = new Page<>(List.of(test1, test2)); + Page expected = new PageImpl<>(List.of(test1, test2), pageInfo, 2); addToDb(test1); addToDb(test2); @@ -84,9 +83,11 @@ void givenPageInfoWhenGetAllThenListUser() { @DisplayName("findById test: get user data by id.") void givenExistingIdWhenGetByIdThenUser() { Long userId = 2L; - Task testTask = new Task(2L, "name", "des", - TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, userId); User test1 = new User(userId, "test1", "pass1", + "email1@co.m", RoleType.ROLE_USER, List.of()); + Task testTask = new Task(2L, "name", "des", + TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, test1); + User expected = new User(userId, "test1", "pass1", "email1@co.m", RoleType.ROLE_USER, List.of(testTask)); addToDb(test1); addToDb(testTask); @@ -94,7 +95,7 @@ void givenExistingIdWhenGetByIdThenUser() { Optional actual = repository.findById(userId); assertTrue(actual.isPresent()); - assertEquals(test1, actual.get()); + assertEquals(expected, actual.get()); } @Test @@ -111,9 +112,11 @@ void givenNotExistingIdWhenGetByIdThenThrow() { @DisplayName("findByUsername test: get user data by name.") void givenExistingNameWhenGetByIdThenUser() { String userUsername = "user"; - Task testTask = new Task(2L, "name", "des", - TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, 2L); User test1 = new User(2L, userUsername, "pass1", + "email1@co.m", RoleType.ROLE_USER, List.of()); + Task testTask = new Task(2L, "name", "des", + TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, test1); + User expected = new User(2L, userUsername, "pass1", "email1@co.m", RoleType.ROLE_USER, List.of(testTask)); addToDb(test1); addToDb(testTask); @@ -121,7 +124,7 @@ void givenExistingNameWhenGetByIdThenUser() { Optional actual = repository.findByUsername(userUsername); assertTrue(actual.isPresent()); - assertEquals(test1, actual.get()); + assertEquals(expected, actual.get()); } @Test @@ -141,6 +144,7 @@ void givenUserWhenSendUserToDbThenSavedUser() { "email2@co.m", RoleType.ROLE_USER, List.of()); repository.save(userToSave); + entityManager.flush(); assertTrue(existsInDb(userToSave)); } @@ -154,7 +158,8 @@ void givenUserWhenUpdateThenUpdatedUser() { "email2@co.m", RoleType.ROLE_USER, List.of()); addToDb(test1); - repository.update(expected); + repository.save(expected); + entityManager.flush(); assertTrue(existsInDb(expected)); } @@ -168,6 +173,8 @@ void givenUserIdWhenDeleteThenVoid() { addToDb(test1); repository.deleteById(userId); + entityManager.flush(); + assertFalse(existsInDb(test1)); } diff --git a/src/test/java/com/emobile/springtodo/service/impl/TaskServiceImplTest.java b/src/test/java/com/emobile/springtodo/service/impl/TaskServiceImplTest.java index 13eea52f..23d328b6 100644 --- a/src/test/java/com/emobile/springtodo/service/impl/TaskServiceImplTest.java +++ b/src/test/java/com/emobile/springtodo/service/impl/TaskServiceImplTest.java @@ -6,15 +6,17 @@ import com.emobile.springtodo.model.entity.User; import com.emobile.springtodo.model.security.AppUserDetails; import com.emobile.springtodo.model.security.RoleType; -import com.emobile.springtodo.model.util.Page; import com.emobile.springtodo.model.util.PageInfo; -import com.emobile.springtodo.repository.TaskRepository; +import com.emobile.springtodo.repository.jpa.TaskRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; @@ -43,6 +45,8 @@ class TaskServiceImplTest { private TaskServiceImpl taskService; @Mock private TaskRepository taskRepository; + @Mock + private UserServiceImpl userService; private static final LocalDateTime MILLENNIUM = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0, 0); private static final LocalDateTime BEFORE_MILLENNIUM = MILLENNIUM.minusDays(5); private final Clock clock = Clock.fixed(MILLENNIUM.toInstant(ZoneOffset.UTC), ZoneOffset.UTC); @@ -50,33 +54,41 @@ class TaskServiceImplTest { @BeforeEach void setUp() { - taskService = new TaskServiceImpl(taskRepository, clock); + taskService = new TaskServiceImpl(taskRepository, userService, clock); taskService.setSelf(taskService); } @Test - @DisplayName("findAll test: get all user data.") - void givenWhenGetAllThenListUser() { - List userList = new ArrayList<>(List.of( + @DisplayName("findAll test: get all task data.") + void givenWhenGetAllThenListTask() { + List taskList = new ArrayList<>(List.of( new Task(), new Task() )); - PageInfo pageInfo = new PageInfo(0, 10); + Pageable pageInfo = PageRequest.of(0, 10); when(taskRepository.findAll(pageInfo)) - .thenReturn(new Page<>(userList)); + .thenReturn(new PageImpl<>(taskList)); - List actual = taskService.findAll(pageInfo); + List actual = taskService.findAll(new PageInfo(10, 0)); - assertEquals(userList.size(), actual.size()); + assertEquals(taskList.size(), actual.size()); verify(taskRepository, times(1)) .findAll(pageInfo); } @Test - @DisplayName("findById test: get user data by id.") - void givenExistingIdWhenGetByIdThenUser() { - Long userId = 1L; + @DisplayName("findById test: get task data by id.") + void givenExistingIdWhenGetByIdThenTask() { + Long taskId = 1L; + User defaultUser = new User( + 2L, + "user", + "pass", + "email", + RoleType.ROLE_USER, + Collections.emptyList() + ); Task defaultTask = new Task( 1L, "name", @@ -84,13 +96,13 @@ void givenExistingIdWhenGetByIdThenUser() { TaskStatus.DONE, LocalDateTime.of(2, 2, 2, 2, 2), LocalDateTime.of(2, 2, 2, 2, 2), - 2L + defaultUser ); - when(taskRepository.findById(userId)) + when(taskRepository.findById(taskId)) .thenReturn(Optional.of(defaultTask)); - Task actual = taskService.findById(userId); + Task actual = taskService.findById(taskId); assertEquals(defaultTask, actual); verify(taskRepository, times(1)) @@ -98,15 +110,15 @@ void givenExistingIdWhenGetByIdThenUser() { } @Test - @DisplayName("findById test: try to get user data by not existing id.") + @DisplayName("findById test: try to get task data by not existing id.") void givenNotExistingIdWhenGetByIdThenThrow() { - Long userId = 1L; + Long taskId = 1L; - when(taskRepository.findById(userId)) + when(taskRepository.findById(taskId)) .thenReturn(Optional.empty()); assertThrows(EntityNotFoundException.class, - () -> taskService.findById(userId), + () -> taskService.findById(taskId), " index is incorrect." ); verify(taskRepository, times(1)) @@ -114,8 +126,8 @@ void givenNotExistingIdWhenGetByIdThenThrow() { } @Test - @DisplayName("save test: send user data to repository.") - void givenUserWhenSendUserToDbThenSavedUser() { + @DisplayName("save test: send task data to repository.") + void givenTaskWhenSendTaskToDbThenSavedTask() { Long userPrincipalId = 10L; Task taskToSave = new Task( null, @@ -126,15 +138,6 @@ void givenUserWhenSendUserToDbThenSavedUser() { null, null ); - Task expected = new Task( - null, - "name", - "description", - TaskStatus.DONE, - MILLENNIUM, - MILLENNIUM, - userPrincipalId - ); User defaultUser = new User( userPrincipalId, "username", @@ -143,6 +146,15 @@ void givenUserWhenSendUserToDbThenSavedUser() { RoleType.ROLE_USER, Collections.emptyList() ); + Task expected = new Task( + null, + "name", + "description", + TaskStatus.DONE, + MILLENNIUM, + MILLENNIUM, + defaultUser + ); AppUserDetails principal = new AppUserDetails(defaultUser); Authentication auth = new UsernamePasswordAuthenticationToken( @@ -154,6 +166,8 @@ void givenUserWhenSendUserToDbThenSavedUser() { when(securityContext.getAuthentication()).thenReturn(auth); when(taskRepository.save(expected)) .thenReturn(expected); + when(userService.findById(userPrincipalId)) + .thenReturn(defaultUser); Task actual = taskService.save(taskToSave); @@ -163,9 +177,17 @@ void givenUserWhenSendUserToDbThenSavedUser() { } @Test - @DisplayName("update test: with partially filled User data.") - void givenPartiallyFilledUserUpdateThenUpdatedUser() { - Long userId = 1L; + @DisplayName("update test: with partially filled Task data.") + void givenPartiallyFilledTaskUpdateThenUpdatedTask() { + Long taskId = 1L; + User defaultUser = new User( + 2L, + "user", + "pass", + "email", + RoleType.ROLE_USER, + Collections.emptyList() + ); Task partiallyFilledTask = new Task( null, "name", @@ -173,7 +195,7 @@ void givenPartiallyFilledUserUpdateThenUpdatedUser() { TaskStatus.TODO, null, null, - 2L + defaultUser ); Task taskToUpdate = new Task( 1L, @@ -182,7 +204,7 @@ void givenPartiallyFilledUserUpdateThenUpdatedUser() { TaskStatus.DONE, BEFORE_MILLENNIUM, BEFORE_MILLENNIUM, - 2L + defaultUser ); Task expected = new Task( 1L, @@ -191,28 +213,38 @@ void givenPartiallyFilledUserUpdateThenUpdatedUser() { TaskStatus.TODO, BEFORE_MILLENNIUM, MILLENNIUM, - 2L + defaultUser ); - when(taskRepository.findById(userId)) + when(taskRepository.findById(taskId)) .thenReturn(Optional.of(taskToUpdate)); - when(taskRepository.update(expected)) + when(taskRepository.save(expected)) .thenReturn(expected); + when(userService.findById(2L)) + .thenReturn(defaultUser); - Task actual = taskService.update(userId, partiallyFilledTask); + Task actual = taskService.update(taskId, partiallyFilledTask); assertEquals(expected, actual); verify(taskRepository, times(1)) - .update(expected); + .save(expected); verify(taskRepository, times(1)) .findById(1L); } @Test - @DisplayName("update test: with filled User data.") - void givenFilledUserAndUserIdWhenUpdateThenUpdatedUser() { - Long userId = 1L; + @DisplayName("update test: with filled Task data.") + void givenFilledTaskAndTaskIdWhenUpdateThenUpdatedTask() { + Long taskId = 1L; + User defaultUser = new User( + 2L, + "user", + "pass", + "email", + RoleType.ROLE_USER, + Collections.emptyList() + ); Task taskToUpdate = new Task( 1L, "name", @@ -220,7 +252,7 @@ void givenFilledUserAndUserIdWhenUpdateThenUpdatedUser() { TaskStatus.DONE, BEFORE_MILLENNIUM, BEFORE_MILLENNIUM, - 2L + defaultUser ); Task expected = new Task( 1L, @@ -229,28 +261,38 @@ void givenFilledUserAndUserIdWhenUpdateThenUpdatedUser() { TaskStatus.DONE, BEFORE_MILLENNIUM, MILLENNIUM, - 2L + defaultUser ); - when(taskRepository.findById(userId)) + when(taskRepository.findById(taskId)) .thenReturn(Optional.of(expected)); - when(taskRepository.update(expected)) + when(taskRepository.save(expected)) .thenReturn(expected); + when(userService.findById(2L)) + .thenReturn(defaultUser); - Task actual = taskService.update(userId, taskToUpdate); + Task actual = taskService.update(taskId, taskToUpdate); assertEquals(expected, actual); verify(taskRepository, times(1)) - .update(expected); + .save(expected); verify(taskRepository, times(1)) .findById(1L); } @Test - @DisplayName("update test: try update with not existed user id.") - void givenUserAndNotExistedUserIdWhenUpdateThenUpdatedUser() { - Long notExistedUserId = 1L; + @DisplayName("update test: try update with not existed task id.") + void givenTaskAndNotExistedTaskIdWhenUpdateThenUpdatedTask() { + Long notExistedTaskId = 1L; LocalDateTime creationTime = LocalDateTime.of(1999, Month.DECEMBER, 10, 0, 0, 0); + User defaultUser = new User( + 2L, + "user", + "pass", + "email", + RoleType.ROLE_USER, + Collections.emptyList() + ); Task taskToUpdate = new Task( 1L, "name", @@ -258,25 +300,25 @@ void givenUserAndNotExistedUserIdWhenUpdateThenUpdatedUser() { TaskStatus.DONE, creationTime, creationTime, - 2L + defaultUser ); - when(taskRepository.findById(notExistedUserId)) + when(taskRepository.findById(notExistedTaskId)) .thenReturn(Optional.empty()); assertThrows(EntityNotFoundException.class, - () -> taskService.update(notExistedUserId, taskToUpdate), - "UserId is incorrect." + () -> taskService.update(notExistedTaskId, taskToUpdate), + "TaskId is incorrect." ); verify(taskRepository, times(0)) - .update(any()); + .save(any()); verify(taskRepository, times(1)) .findById(any()); } @Test - @DisplayName("delete test: delete user data message to repository.") - void givenExistedUserIdWhenDeleteThenVoid() { + @DisplayName("delete test: delete task data message to repository.") + void givenExistedTaskIdWhenDeleteThenVoid() { Long existedTaskId = 1L; when(taskRepository.findById(existedTaskId)) @@ -291,16 +333,16 @@ void givenExistedUserIdWhenDeleteThenVoid() { @Test @DisplayName("delete test: delete task data message to repository.") - void givenNotExistedUserIdWhenDeleteThenVoid() { - Long notExistedUserId = 1L; + void givenNotExistedTaskIdWhenDeleteThenVoid() { + Long notExistedTaskId = 1L; assertThrows(EntityNotFoundException.class, - () -> taskService.deleteById(notExistedUserId), - "UserId is incorrect." + () -> taskService.deleteById(notExistedTaskId), + "TaskId is incorrect." ); verify(taskRepository, times(1)) - .findById(notExistedUserId); + .findById(notExistedTaskId); verify(taskRepository, times(0)) - .deleteById(notExistedUserId); + .deleteById(notExistedTaskId); } } \ No newline at end of file diff --git a/src/test/java/com/emobile/springtodo/service/impl/UserServiceImplTest.java b/src/test/java/com/emobile/springtodo/service/impl/UserServiceImplTest.java index 45c0a174..a3df32f2 100644 --- a/src/test/java/com/emobile/springtodo/service/impl/UserServiceImplTest.java +++ b/src/test/java/com/emobile/springtodo/service/impl/UserServiceImplTest.java @@ -3,9 +3,8 @@ import com.emobile.springtodo.exception.EntityNotFoundException; import com.emobile.springtodo.model.entity.User; import com.emobile.springtodo.model.security.RoleType; -import com.emobile.springtodo.model.util.Page; import com.emobile.springtodo.model.util.PageInfo; -import com.emobile.springtodo.repository.UserRepository; +import com.emobile.springtodo.repository.jpa.UserRepository; import com.emobile.springtodo.service.TaskService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -13,6 +12,9 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.security.crypto.password.PasswordEncoder; import java.util.ArrayList; @@ -34,14 +36,12 @@ class UserServiceImplTest { @Mock private UserRepository userRepository; @Mock - private TaskService taskService; - @Mock private PasswordEncoder passwordEncoder; @BeforeEach void setUp() { - userService = new UserServiceImpl(userRepository, taskService, passwordEncoder); + userService = new UserServiceImpl(userRepository, passwordEncoder); userService.setSelf(userService); } @@ -52,12 +52,12 @@ void givenWhenGetAllThenListUser() { new User(), new User() )); - PageInfo pageInfo = new PageInfo(0, 10); + Pageable pageInfo = PageRequest.of(0, 10); when(userRepository.findAll(pageInfo)) - .thenReturn(new Page<>(userList)); + .thenReturn(new PageImpl<>(userList)); - List actual = userService.findAll(pageInfo); + List actual = userService.findAll(new PageInfo(10, 0)); assertEquals(userList.size(), actual.size()); verify(userRepository, times(1)) @@ -199,7 +199,7 @@ void givenPartiallyFilledUserUpdateThenUpdatedUser() { when(userRepository.findById(userId)) .thenReturn(Optional.of(userToUpdate)); - when(userRepository.update(expected)) + when(userRepository.save(expected)) .thenReturn(expected); when(passwordEncoder.encode(defaultPass)) .thenReturn(defaultPass); @@ -208,7 +208,7 @@ void givenPartiallyFilledUserUpdateThenUpdatedUser() { assertEquals(expected, actual); verify(userRepository, times(1)) - .update(expected); + .save(expected); verify(userRepository, times(1)) .findById(1L); } @@ -229,7 +229,7 @@ void givenFilledUserAndUserIdWhenUpdateThenUpdatedUser() { when(userRepository.findById(userId)) .thenReturn(Optional.of(userToUpdate)); - when(userRepository.update(userToUpdate)) + when(userRepository.save(userToUpdate)) .thenReturn(userToUpdate); when(passwordEncoder.encode(defaultPass)) .thenReturn(defaultPass); @@ -238,7 +238,7 @@ void givenFilledUserAndUserIdWhenUpdateThenUpdatedUser() { assertEquals(userToUpdate, actual); verify(userRepository, times(1)) - .update(userToUpdate); + .save(userToUpdate); verify(userRepository, times(1)) .findById(1L); } @@ -265,7 +265,7 @@ void givenUserAndNotExistedUserIdWhenUpdateThenUpdatedUser() { "UserId is incorrect." ); verify(userRepository, times(0)) - .update(any()); + .save(any()); verify(userRepository, times(1)) .findById(any()); }