From edb5b285fc07729c01f6f05362c79623a94c731f Mon Sep 17 00:00:00 2001 From: dmatveyenka Date: Fri, 27 Jan 2023 14:00:28 +0100 Subject: [PATCH 1/8] added homework --- pom.xml | 6 + src/main/java/com/dmdev/dao/Dao.java | 14 +- .../{UserDao.java => SubscriptionDao.java} | 142 +++++++++--------- .../com/dmdev/dto/CreateSubscriptionDto.java | 15 ++ .../java/com/dmdev/dto/CreateUserDto.java | 15 -- src/main/java/com/dmdev/dto/UserDto.java | 20 --- .../java/com/dmdev/entity/BaseEntity.java | 6 + src/main/java/com/dmdev/entity/Gender.java | 15 -- src/main/java/com/dmdev/entity/Provider.java | 18 +++ src/main/java/com/dmdev/entity/Role.java | 15 -- src/main/java/com/dmdev/entity/Status.java | 5 + .../java/com/dmdev/entity/Subscription.java | 23 +++ src/main/java/com/dmdev/entity/User.java | 22 --- .../exception/SubscriptionException.java | 8 + .../dmdev/exception/ValidationException.java | 6 +- .../mapper/CreateSubscriptionMapper.java | 30 ++++ .../com/dmdev/mapper/CreateUserMapper.java | 32 ---- .../java/com/dmdev/mapper/UserMapper.java | 29 ---- .../dmdev/service/SubscriptionService.java | 63 ++++++++ .../java/com/dmdev/service/UserService.java | 47 ------ .../com/dmdev/util/LocalDateFormatter.java | 29 ---- .../java/com/dmdev/util/PropertiesUtil.java | 2 +- .../CreateSubscriptionValidator.java | 38 +++++ .../dmdev/validator/CreateUserValidator.java | 34 ----- src/main/java/com/dmdev/validator/Error.java | 2 +- .../com/dmdev/validator/ValidationResult.java | 4 +- .../integration/IntegrationTestBase.java | 41 +++-- 27 files changed, 318 insertions(+), 363 deletions(-) rename src/main/java/com/dmdev/dao/{UserDao.java => SubscriptionDao.java} (53%) create mode 100644 src/main/java/com/dmdev/dto/CreateSubscriptionDto.java delete mode 100644 src/main/java/com/dmdev/dto/CreateUserDto.java delete mode 100644 src/main/java/com/dmdev/dto/UserDto.java create mode 100644 src/main/java/com/dmdev/entity/BaseEntity.java delete mode 100644 src/main/java/com/dmdev/entity/Gender.java create mode 100644 src/main/java/com/dmdev/entity/Provider.java delete mode 100644 src/main/java/com/dmdev/entity/Role.java create mode 100644 src/main/java/com/dmdev/entity/Status.java create mode 100644 src/main/java/com/dmdev/entity/Subscription.java delete mode 100644 src/main/java/com/dmdev/entity/User.java create mode 100644 src/main/java/com/dmdev/exception/SubscriptionException.java create mode 100644 src/main/java/com/dmdev/mapper/CreateSubscriptionMapper.java delete mode 100644 src/main/java/com/dmdev/mapper/CreateUserMapper.java delete mode 100644 src/main/java/com/dmdev/mapper/UserMapper.java create mode 100644 src/main/java/com/dmdev/service/SubscriptionService.java delete mode 100644 src/main/java/com/dmdev/service/UserService.java delete mode 100644 src/main/java/com/dmdev/util/LocalDateFormatter.java create mode 100644 src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java delete mode 100644 src/main/java/com/dmdev/validator/CreateUserValidator.java diff --git a/pom.xml b/pom.xml index 30ce822..2adffac 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,12 @@ + + org.apache.commons + commons-lang3 + 3.12.0 + + org.postgresql postgresql diff --git a/src/main/java/com/dmdev/dao/Dao.java b/src/main/java/com/dmdev/dao/Dao.java index 433ef4a..fcacd86 100644 --- a/src/main/java/com/dmdev/dao/Dao.java +++ b/src/main/java/com/dmdev/dao/Dao.java @@ -1,9 +1,17 @@ package com.dmdev.dao; +import com.dmdev.entity.BaseEntity; + import java.util.List; import java.util.Optional; -public interface Dao { +public interface Dao> { + + default T upsert(T entity) { + return entity.getId() != null + ? update(entity) + : insert(entity); + } List findAll(); @@ -11,7 +19,7 @@ public interface Dao { boolean delete(K id); - void update(T entity); + T update(T entity); - T save(T entity); + T insert(T entity); } diff --git a/src/main/java/com/dmdev/dao/UserDao.java b/src/main/java/com/dmdev/dao/SubscriptionDao.java similarity index 53% rename from src/main/java/com/dmdev/dao/UserDao.java rename to src/main/java/com/dmdev/dao/SubscriptionDao.java index 11ad1f5..cc91b1a 100644 --- a/src/main/java/com/dmdev/dao/UserDao.java +++ b/src/main/java/com/dmdev/dao/SubscriptionDao.java @@ -1,77 +1,72 @@ package com.dmdev.dao; -import com.dmdev.entity.Gender; -import com.dmdev.entity.Role; -import com.dmdev.entity.User; +import com.dmdev.entity.Provider; +import com.dmdev.entity.Status; +import com.dmdev.entity.Subscription; import com.dmdev.util.ConnectionManager; -import lombok.NoArgsConstructor; import lombok.SneakyThrows; -import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Optional; import static java.sql.Statement.RETURN_GENERATED_KEYS; -import static lombok.AccessLevel.PRIVATE; -@NoArgsConstructor(access = PRIVATE) -public class UserDao implements Dao { +public class SubscriptionDao implements Dao { - private static final UserDao INSTANCE = new UserDao(); + private static final SubscriptionDao INSTANCE = new SubscriptionDao(); private static final String GET_ALL_SQL = """ SELECT id, + user_id, name, - birthday, - email, - password, - role, - gender - FROM users + provider, + expiration_date, + status + FROM subscription """; private static final String GET_BY_ID_SQL = GET_ALL_SQL + " WHERE id = ?"; - private static final String GET_BY_EMAIL_AND_PASSWORD_SQL = GET_ALL_SQL + " WHERE email = ? AND password = ?"; + private static final String GET_BY_USER_ID_SQL = GET_ALL_SQL + " WHERE user_id = ?"; + private static final String DELETE_BY_ID_SQL = "DELETE FROM subscription WHERE id = ?"; private static final String SAVE_SQL = - "INSERT INTO users (name, birthday, email, password, role, gender) VALUES (?, ?, ?, ?, ?, ?)"; - private static final String DELETE_BY_ID_SQL = "DELETE FROM users WHERE id = ?"; + "INSERT INTO subscription (user_id, name, provider, expiration_date, status) VALUES (?, ?, ?, ?, ?)"; private static final String UPDATE_BY_ID_SQL = """ - UPDATE users - SET name = ?, - birthday = ?, - email = ?, - password = ?, - role = ?, - gender = ? + UPDATE subscription + SET user_id = ?, + name = ?, + provider = ?, + expiration_date = ?, + status = ? WHERE id = ? """; - public static UserDao getInstance() { + public static SubscriptionDao getInstance() { return INSTANCE; } @Override @SneakyThrows - public List findAll() { + public List findAll() { try (var connection = ConnectionManager.get(); var preparedStatement = connection.prepareStatement(GET_ALL_SQL)) { var resultSet = preparedStatement.executeQuery(); - List users = new ArrayList<>(); + List subscriptions = new ArrayList<>(); while (resultSet.next()) { - users.add(buildEntity(resultSet)); + subscriptions.add(buildEntity(resultSet)); } - return users; + return subscriptions; } } @Override @SneakyThrows - public Optional findById(Integer id) { + public Optional findById(Integer id) { try (var connection = ConnectionManager.get(); var preparedStatement = connection.prepareStatement(GET_BY_ID_SQL)) { preparedStatement.setObject(1, id); @@ -85,76 +80,77 @@ public Optional findById(Integer id) { @Override @SneakyThrows - public User save(User entity) { + public boolean delete(Integer id) { try (var connection = ConnectionManager.get(); - var preparedStatement = connection.prepareStatement(SAVE_SQL, RETURN_GENERATED_KEYS)) { - prepareStatementToUpsert(preparedStatement, entity); - - preparedStatement.executeUpdate(); - - var generatedKeys = preparedStatement.getGeneratedKeys(); - generatedKeys.next(); - entity.setId(generatedKeys.getObject("id", Integer.class)); + var preparedStatement = connection.prepareStatement(DELETE_BY_ID_SQL)) { + preparedStatement.setObject(1, id); - return entity; + return preparedStatement.executeUpdate() > 0; } } + @Override @SneakyThrows - public Optional findByEmailAndPassword(String email, String password) { + public Subscription update(Subscription entity) { try (var connection = ConnectionManager.get(); - var preparedStatement = connection.prepareStatement(GET_BY_EMAIL_AND_PASSWORD_SQL)) { - preparedStatement.setString(1, email); - preparedStatement.setString(2, password); + var preparedStatement = connection.prepareStatement(UPDATE_BY_ID_SQL)) { + prepareStatementToUpsert(preparedStatement, entity); + preparedStatement.setObject(6, entity.getId()); - var resultSet = preparedStatement.executeQuery(); - return resultSet.next() - ? Optional.of(buildEntity(resultSet)) - : Optional.empty(); + preparedStatement.executeUpdate(); + return entity; } } @Override @SneakyThrows - public boolean delete(Integer id) { + public Subscription insert(Subscription entity) { try (var connection = ConnectionManager.get(); - var preparedStatement = connection.prepareStatement(DELETE_BY_ID_SQL)) { - preparedStatement.setObject(1, id); + var preparedStatement = connection.prepareStatement(SAVE_SQL, RETURN_GENERATED_KEYS)) { + prepareStatementToUpsert(preparedStatement, entity); - return preparedStatement.executeUpdate() > 0; + preparedStatement.executeUpdate(); + + var generatedKeys = preparedStatement.getGeneratedKeys(); + generatedKeys.next(); + entity.setId(generatedKeys.getObject("id", Integer.class)); + + return entity; } } - @Override @SneakyThrows - public void update(User entity) { + public List findByUserId(Integer userId) { try (var connection = ConnectionManager.get(); - var preparedStatement = connection.prepareStatement(UPDATE_BY_ID_SQL)) { - prepareStatementToUpsert(preparedStatement, entity); - preparedStatement.setObject(7, entity.getId()); + var preparedStatement = connection.prepareStatement(GET_BY_USER_ID_SQL)) { + preparedStatement.setObject(1, userId); - preparedStatement.executeUpdate(); + var resultSet = preparedStatement.executeQuery(); + List subscriptions = new ArrayList<>(); + while (resultSet.next()) { + subscriptions.add(buildEntity(resultSet)); + } + + return subscriptions; } } - private User buildEntity(ResultSet resultSet) throws SQLException { - return User.builder() + private Subscription buildEntity(ResultSet resultSet) throws SQLException { + return Subscription.builder() .id(resultSet.getObject("id", Integer.class)) + .userId(resultSet.getObject("user_id", Integer.class)) .name(resultSet.getObject("name", String.class)) - .birthday(resultSet.getObject("birthday", Date.class).toLocalDate()) - .email(resultSet.getObject("email", String.class)) - .password(resultSet.getObject("password", String.class)) - .role(Role.find(resultSet.getObject("role", String.class)).orElse(null)) - .gender(Gender.find(resultSet.getObject("gender", String.class)).orElse(null)) + .provider(Provider.valueOf(resultSet.getObject("provider", String.class))) + .expirationDate(resultSet.getObject("expiration_date", Timestamp.class).toInstant()) + .status(Status.valueOf(resultSet.getObject("status", String.class))) .build(); } - private void prepareStatementToUpsert(PreparedStatement preparedStatement, User entity) throws SQLException { - preparedStatement.setObject(1, entity.getName()); - preparedStatement.setObject(2, entity.getBirthday()); - preparedStatement.setObject(3, entity.getEmail()); - preparedStatement.setObject(4, entity.getPassword()); - preparedStatement.setObject(5, entity.getRole() != null ? entity.getRole().name() : null); - preparedStatement.setObject(6, entity.getGender() != null ? entity.getGender().name() : null); + private void prepareStatementToUpsert(PreparedStatement preparedStatement, Subscription entity) throws SQLException { + preparedStatement.setObject(1, entity.getUserId()); + preparedStatement.setObject(2, entity.getName()); + preparedStatement.setObject(3, entity.getProvider().name()); + preparedStatement.setObject(4, Timestamp.from(entity.getExpirationDate())); + preparedStatement.setObject(5, entity.getStatus().name()); } } diff --git a/src/main/java/com/dmdev/dto/CreateSubscriptionDto.java b/src/main/java/com/dmdev/dto/CreateSubscriptionDto.java new file mode 100644 index 0000000..f752b76 --- /dev/null +++ b/src/main/java/com/dmdev/dto/CreateSubscriptionDto.java @@ -0,0 +1,15 @@ +package com.dmdev.dto; + +import lombok.Builder; +import lombok.Value; + +import java.time.Instant; + +@Value +@Builder +public class CreateSubscriptionDto { + Integer userId; + String name; + String provider; + Instant expirationDate; +} diff --git a/src/main/java/com/dmdev/dto/CreateUserDto.java b/src/main/java/com/dmdev/dto/CreateUserDto.java deleted file mode 100644 index 6524693..0000000 --- a/src/main/java/com/dmdev/dto/CreateUserDto.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.dmdev.dto; - -import lombok.Builder; -import lombok.Value; - -@Value -@Builder -public class CreateUserDto { - String name; - String birthday; - String email; - String password; - String role; - String gender; -} diff --git a/src/main/java/com/dmdev/dto/UserDto.java b/src/main/java/com/dmdev/dto/UserDto.java deleted file mode 100644 index 63a98d8..0000000 --- a/src/main/java/com/dmdev/dto/UserDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.dmdev.dto; - -import com.dmdev.entity.Gender; -import com.dmdev.entity.Role; -import lombok.Builder; -import lombok.Value; - -import java.time.LocalDate; - -@Value -@Builder -public class UserDto { - Integer id; - String name; - LocalDate birthday; - String email; - String image; - Role role; - Gender gender; -} diff --git a/src/main/java/com/dmdev/entity/BaseEntity.java b/src/main/java/com/dmdev/entity/BaseEntity.java new file mode 100644 index 0000000..c9ce703 --- /dev/null +++ b/src/main/java/com/dmdev/entity/BaseEntity.java @@ -0,0 +1,6 @@ +package com.dmdev.entity; + +public interface BaseEntity { + + K getId(); +} diff --git a/src/main/java/com/dmdev/entity/Gender.java b/src/main/java/com/dmdev/entity/Gender.java deleted file mode 100644 index 1a18192..0000000 --- a/src/main/java/com/dmdev/entity/Gender.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.dmdev.entity; - -import java.util.Arrays; -import java.util.Optional; - -public enum Gender { - MALE, - FEMALE; - - public static Optional find(String gender) { - return Arrays.stream(values()) - .filter(it -> it.name().equals(gender)) - .findFirst(); - } -} diff --git a/src/main/java/com/dmdev/entity/Provider.java b/src/main/java/com/dmdev/entity/Provider.java new file mode 100644 index 0000000..fb4c56a --- /dev/null +++ b/src/main/java/com/dmdev/entity/Provider.java @@ -0,0 +1,18 @@ +package com.dmdev.entity; + +import java.util.Arrays; +import java.util.Optional; + +public enum Provider { + GOOGLE, APPLE; + + public static Provider findByName(String name) { + return findByNameOpt(name).orElseThrow(); + } + + public static Optional findByNameOpt(String name) { + return Arrays.stream(values()) + .filter(provider -> provider.name().equalsIgnoreCase(name)) + .findFirst(); + } +} diff --git a/src/main/java/com/dmdev/entity/Role.java b/src/main/java/com/dmdev/entity/Role.java deleted file mode 100644 index 6c2d48b..0000000 --- a/src/main/java/com/dmdev/entity/Role.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.dmdev.entity; - -import java.util.Arrays; -import java.util.Optional; - -public enum Role { - USER, - ADMIN; - - public static Optional find(String role) { - return Arrays.stream(values()) - .filter(it -> it.name().equals(role)) - .findFirst(); - } -} diff --git a/src/main/java/com/dmdev/entity/Status.java b/src/main/java/com/dmdev/entity/Status.java new file mode 100644 index 0000000..1789ac8 --- /dev/null +++ b/src/main/java/com/dmdev/entity/Status.java @@ -0,0 +1,5 @@ +package com.dmdev.entity; + +public enum Status { + ACTIVE, CANCELED, EXPIRED +} diff --git a/src/main/java/com/dmdev/entity/Subscription.java b/src/main/java/com/dmdev/entity/Subscription.java new file mode 100644 index 0000000..7d77a9a --- /dev/null +++ b/src/main/java/com/dmdev/entity/Subscription.java @@ -0,0 +1,23 @@ +package com.dmdev.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.time.Instant; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Subscription implements BaseEntity { + private Integer id; + private Integer userId; + private String name; + private Provider provider; + private Instant expirationDate; + private Status status; +} diff --git a/src/main/java/com/dmdev/entity/User.java b/src/main/java/com/dmdev/entity/User.java deleted file mode 100644 index c21da9a..0000000 --- a/src/main/java/com/dmdev/entity/User.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.dmdev.entity; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDate; - -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class User { - private Integer id; - private String name; - private LocalDate birthday; - private String email; - private String password; - private Role role; - private Gender gender; -} diff --git a/src/main/java/com/dmdev/exception/SubscriptionException.java b/src/main/java/com/dmdev/exception/SubscriptionException.java new file mode 100644 index 0000000..fa091ce --- /dev/null +++ b/src/main/java/com/dmdev/exception/SubscriptionException.java @@ -0,0 +1,8 @@ +package com.dmdev.exception; + +public class SubscriptionException extends RuntimeException { + + public SubscriptionException(String message) { + super(message); + } +} diff --git a/src/main/java/com/dmdev/exception/ValidationException.java b/src/main/java/com/dmdev/exception/ValidationException.java index 07a5f84..acc805e 100644 --- a/src/main/java/com/dmdev/exception/ValidationException.java +++ b/src/main/java/com/dmdev/exception/ValidationException.java @@ -2,15 +2,13 @@ import com.dmdev.validator.Error; import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.util.List; +@RequiredArgsConstructor public class ValidationException extends RuntimeException { @Getter private final List errors; - - public ValidationException(List errors) { - this.errors = errors; - } } diff --git a/src/main/java/com/dmdev/mapper/CreateSubscriptionMapper.java b/src/main/java/com/dmdev/mapper/CreateSubscriptionMapper.java new file mode 100644 index 0000000..8c8f078 --- /dev/null +++ b/src/main/java/com/dmdev/mapper/CreateSubscriptionMapper.java @@ -0,0 +1,30 @@ +package com.dmdev.mapper; + +import com.dmdev.dto.CreateSubscriptionDto; +import com.dmdev.entity.Provider; +import com.dmdev.entity.Status; +import com.dmdev.entity.Subscription; +import lombok.NoArgsConstructor; + +import static lombok.AccessLevel.PRIVATE; + +@NoArgsConstructor(access = PRIVATE) +public class CreateSubscriptionMapper implements Mapper { + + private static final CreateSubscriptionMapper INSTANCE = new CreateSubscriptionMapper(); + + public static CreateSubscriptionMapper getInstance() { + return INSTANCE; + } + + @Override + public Subscription map(CreateSubscriptionDto object) { + return Subscription.builder() + .userId(object.getUserId()) + .name(object.getName()) + .provider(Provider.findByNameOpt(object.getProvider()).orElse(null)) + .expirationDate(object.getExpirationDate()) + .status(Status.ACTIVE) + .build(); + } +} diff --git a/src/main/java/com/dmdev/mapper/CreateUserMapper.java b/src/main/java/com/dmdev/mapper/CreateUserMapper.java deleted file mode 100644 index ce27132..0000000 --- a/src/main/java/com/dmdev/mapper/CreateUserMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.dmdev.mapper; - -import com.dmdev.dto.CreateUserDto; -import com.dmdev.entity.Gender; -import com.dmdev.entity.Role; -import com.dmdev.entity.User; -import com.dmdev.util.LocalDateFormatter; -import lombok.NoArgsConstructor; - -import static lombok.AccessLevel.PRIVATE; - -@NoArgsConstructor(access = PRIVATE) -public class CreateUserMapper implements Mapper { - - private static final CreateUserMapper INSTANCE = new CreateUserMapper(); - - public static CreateUserMapper getInstance() { - return INSTANCE; - } - - @Override - public User map(CreateUserDto object) { - return User.builder() - .name(object.getName()) - .birthday(LocalDateFormatter.format(object.getBirthday())) - .email(object.getEmail()) - .password(object.getPassword()) - .gender(Gender.find(object.getGender()).orElse(null)) - .role(Role.find(object.getRole()).orElse(null)) - .build(); - } -} diff --git a/src/main/java/com/dmdev/mapper/UserMapper.java b/src/main/java/com/dmdev/mapper/UserMapper.java deleted file mode 100644 index 3d2e33c..0000000 --- a/src/main/java/com/dmdev/mapper/UserMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.dmdev.mapper; - -import com.dmdev.dto.UserDto; -import com.dmdev.entity.User; -import lombok.NoArgsConstructor; - -import static lombok.AccessLevel.PRIVATE; - -@NoArgsConstructor(access = PRIVATE) -public class UserMapper implements Mapper { - - private static final UserMapper INSTANCE = new UserMapper(); - - public static UserMapper getInstance() { - return INSTANCE; - } - - @Override - public UserDto map(User object) { - return UserDto.builder() - .id(object.getId()) - .name(object.getName()) - .birthday(object.getBirthday()) - .email(object.getEmail()) - .role(object.getRole()) - .gender(object.getGender()) - .build(); - } -} diff --git a/src/main/java/com/dmdev/service/SubscriptionService.java b/src/main/java/com/dmdev/service/SubscriptionService.java new file mode 100644 index 0000000..9ecddeb --- /dev/null +++ b/src/main/java/com/dmdev/service/SubscriptionService.java @@ -0,0 +1,63 @@ +package com.dmdev.service; + +import com.dmdev.dao.SubscriptionDao; +import com.dmdev.dto.CreateSubscriptionDto; +import com.dmdev.entity.Provider; +import com.dmdev.entity.Status; +import com.dmdev.entity.Subscription; +import com.dmdev.exception.SubscriptionException; +import com.dmdev.exception.ValidationException; +import com.dmdev.mapper.CreateSubscriptionMapper; +import com.dmdev.validator.CreateSubscriptionValidator; +import lombok.RequiredArgsConstructor; + +import java.time.Clock; +import java.time.Instant; + +@RequiredArgsConstructor +public class SubscriptionService { + + private final SubscriptionDao subscriptionDao; + private final CreateSubscriptionMapper createSubscriptionMapper; + private final CreateSubscriptionValidator createSubscriptionValidator; + private final Clock clock; + + public Subscription upsert(CreateSubscriptionDto dto) { + var validationResult = createSubscriptionValidator.validate(dto); + if (validationResult.hasErrors()) { + throw new ValidationException(validationResult.getErrors()); + } + + Subscription subscription = subscriptionDao.findByUserId(dto.getUserId()).stream() + .filter(existingSubscription -> existingSubscription.getName().equals(dto.getName())) + .filter(existingSubscription -> existingSubscription.getProvider() == Provider.findByName(dto.getProvider())) + .findFirst() + .map(existingSubscription -> existingSubscription + .setExpirationDate(dto.getExpirationDate()) + .setStatus(Status.ACTIVE)) + .orElseGet(() -> createSubscriptionMapper.map(dto)); + + return subscriptionDao.upsert(subscription); + } + + public void cancel(Integer subscriptionId) { + var subscription = subscriptionDao.findById(subscriptionId) + .orElseThrow(IllegalArgumentException::new); + if (subscription.getStatus() != Status.ACTIVE) { + throw new SubscriptionException(String.format("Only active subscription %d can be canceled", subscriptionId)); + } + subscription.setStatus(Status.CANCELED); + subscriptionDao.update(subscription); + } + + public void expire(Integer subscriptionId) { + var subscription = subscriptionDao.findById(subscriptionId) + .orElseThrow(IllegalArgumentException::new); + if (subscription.getStatus() == Status.EXPIRED) { + throw new SubscriptionException(String.format("Subscription %d has already expired", subscriptionId)); + } + subscription.setStatus(Status.EXPIRED); + subscription.setExpirationDate(Instant.now(clock)); + subscriptionDao.update(subscription); + } +} diff --git a/src/main/java/com/dmdev/service/UserService.java b/src/main/java/com/dmdev/service/UserService.java deleted file mode 100644 index b5abbe3..0000000 --- a/src/main/java/com/dmdev/service/UserService.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.dmdev.service; - -import com.dmdev.dao.UserDao; -import com.dmdev.dto.CreateUserDto; -import com.dmdev.dto.UserDto; -import com.dmdev.exception.ValidationException; -import com.dmdev.mapper.CreateUserMapper; -import com.dmdev.mapper.UserMapper; -import com.dmdev.validator.CreateUserValidator; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; - -import java.util.Optional; - -import static lombok.AccessLevel.PRIVATE; - -@NoArgsConstructor(access = PRIVATE) -public class UserService { - - private static final UserService INSTANCE = new UserService(); - - private final CreateUserValidator createUserValidator = CreateUserValidator.getInstance(); - private final UserDao userDao = UserDao.getInstance(); - private final CreateUserMapper createUserMapper = CreateUserMapper.getInstance(); - private final UserMapper userMapper = UserMapper.getInstance(); - - public static UserService getInstance() { - return INSTANCE; - } - - public Optional login(String email, String password) { - return userDao.findByEmailAndPassword(email, password) - .map(userMapper::map); - } - - @SneakyThrows - public UserDto create(CreateUserDto userDto) { - var validationResult = createUserValidator.validate(userDto); - if (!validationResult.isValid()) { - throw new ValidationException(validationResult.getErrors()); - } - var userEntity = createUserMapper.map(userDto); - userDao.save(userEntity); - - return userMapper.map(userEntity); - } -} diff --git a/src/main/java/com/dmdev/util/LocalDateFormatter.java b/src/main/java/com/dmdev/util/LocalDateFormatter.java deleted file mode 100644 index 33e8d2b..0000000 --- a/src/main/java/com/dmdev/util/LocalDateFormatter.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.dmdev.util; - -import lombok.experimental.UtilityClass; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.Optional; - -@UtilityClass -public class LocalDateFormatter { - - private static final String PATTERN = "yyyy-MM-dd"; - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(PATTERN); - - public LocalDate format(String date) { - return LocalDate.parse(date, FORMATTER); - } - - public boolean isValid(String date) { - try { - return Optional.ofNullable(date) - .map(LocalDateFormatter::format) - .isPresent(); - } catch (DateTimeParseException exception) { - return false; - } - } -} diff --git a/src/main/java/com/dmdev/util/PropertiesUtil.java b/src/main/java/com/dmdev/util/PropertiesUtil.java index d83e928..a46867f 100644 --- a/src/main/java/com/dmdev/util/PropertiesUtil.java +++ b/src/main/java/com/dmdev/util/PropertiesUtil.java @@ -6,7 +6,7 @@ import java.util.Properties; @UtilityClass -public final class PropertiesUtil { +public class PropertiesUtil { private static final Properties properties = new Properties(); diff --git a/src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java b/src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java new file mode 100644 index 0000000..c06e54d --- /dev/null +++ b/src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java @@ -0,0 +1,38 @@ +package com.dmdev.validator; + +import com.dmdev.dto.CreateSubscriptionDto; +import com.dmdev.entity.Provider; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import java.time.Instant; + +import static lombok.AccessLevel.PRIVATE; + +@NoArgsConstructor(access = PRIVATE) +public class CreateSubscriptionValidator implements Validator { + + private static final CreateSubscriptionValidator INSTANCE = new CreateSubscriptionValidator(); + + public static CreateSubscriptionValidator getInstance() { + return INSTANCE; + } + + @Override + public ValidationResult validate(CreateSubscriptionDto object) { + var validationResult = new ValidationResult(); + if (object.getUserId() == null) { + validationResult.add(Error.of(100, "userId is invalid")); + } + if (StringUtils.isBlank(object.getName())) { + validationResult.add(Error.of(101, "name is invalid")); + } + if (Provider.findByNameOpt(object.getProvider()).isEmpty()) { + validationResult.add(Error.of(102, "provider is invalid")); + } + if (object.getExpirationDate() == null || object.getExpirationDate().isBefore(Instant.now())) { + validationResult.add(Error.of(103, "expirationDate is invalid")); + } + return validationResult; + } +} diff --git a/src/main/java/com/dmdev/validator/CreateUserValidator.java b/src/main/java/com/dmdev/validator/CreateUserValidator.java deleted file mode 100644 index 1d53779..0000000 --- a/src/main/java/com/dmdev/validator/CreateUserValidator.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.dmdev.validator; - -import com.dmdev.dto.CreateUserDto; -import com.dmdev.entity.Gender; -import com.dmdev.entity.Role; -import com.dmdev.util.LocalDateFormatter; -import lombok.NoArgsConstructor; - -import static lombok.AccessLevel.PRIVATE; - -@NoArgsConstructor(access = PRIVATE) -public class CreateUserValidator implements Validator { - - private static final CreateUserValidator INSTANCE = new CreateUserValidator(); - - public static CreateUserValidator getInstance() { - return INSTANCE; - } - - @Override - public ValidationResult validate(CreateUserDto object) { - var validationResult = new ValidationResult(); - if (!LocalDateFormatter.isValid(object.getBirthday())) { - validationResult.add(Error.of("invalid.birthday", "Birthday is invalid")); - } - if (Gender.find(object.getGender()).isEmpty()) { - validationResult.add(Error.of("invalid.gender", "Gender is invalid")); - } - if (Role.find(object.getRole()).isEmpty()) { - validationResult.add(Error.of("invalid.role", "Role is invalid")); - } - return validationResult; - } -} diff --git a/src/main/java/com/dmdev/validator/Error.java b/src/main/java/com/dmdev/validator/Error.java index acd146a..78686ac 100644 --- a/src/main/java/com/dmdev/validator/Error.java +++ b/src/main/java/com/dmdev/validator/Error.java @@ -4,6 +4,6 @@ @Value(staticConstructor = "of") public class Error { - String code; + Integer code; String message; } diff --git a/src/main/java/com/dmdev/validator/ValidationResult.java b/src/main/java/com/dmdev/validator/ValidationResult.java index c6f2106..e73b58a 100644 --- a/src/main/java/com/dmdev/validator/ValidationResult.java +++ b/src/main/java/com/dmdev/validator/ValidationResult.java @@ -14,7 +14,7 @@ public void add(Error error) { this.errors.add(error); } - public boolean isValid() { - return errors.isEmpty(); + public boolean hasErrors() { + return !errors.isEmpty(); } } diff --git a/src/test/java/com/dmdev/integration/IntegrationTestBase.java b/src/test/java/com/dmdev/integration/IntegrationTestBase.java index 2188cc1..86d1a3b 100644 --- a/src/test/java/com/dmdev/integration/IntegrationTestBase.java +++ b/src/test/java/com/dmdev/integration/IntegrationTestBase.java @@ -1,41 +1,40 @@ package com.dmdev.integration; import com.dmdev.util.ConnectionManager; -import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import java.sql.SQLException; + public abstract class IntegrationTestBase { - private static final String CLEAN_SQL = "DROP TABLE IF EXISTS users;"; + private static final String CLEAN_SQL = "DELETE FROM subscription;"; private static final String CREATE_SQL = """ - CREATE TABLE IF NOT EXISTS users + CREATE TABLE IF NOT EXISTS subscription ( id INT AUTO_INCREMENT PRIMARY KEY , - name VARCHAR(64), - birthday DATE NOT NULL , - email VARCHAR(64) NOT NULL UNIQUE , - password VARCHAR(64) NOT NULL , - role VARCHAR(32) NOT NULL , - gender VARCHAR(16) + user_id INT NOT NULL , + name VARCHAR(64) NOT NULL , + provider VARCHAR(16) NOT NULL , + expiration_date DATETIME NOT NULL , + status VARCHAR(16) NOT NULL , + UNIQUE (user_id, name) ); """; - private static final String INSERT_SQL = """ - INSERT INTO users (name, birthday, email, password, role, gender) - VALUES ('Ivan', '1990-01-10', 'ivan@gmail.com', '111', 'ADMIN', 'MALE'), - ('Petr', '1995-10-19', 'petr@gmail.com', '123', 'USER', 'MALE'), - ('Sveta', '2001-12-23', 'sveta@gmail.com', '321', 'USER', 'FEMALE'), - ('Vlad', '1984-03-14', 'vlad@gmail.com', '456', 'USER', 'MALE'), - ('Kate', '1989-08-09', 'kate@gmail.com', '777', 'ADMIN', 'FEMALE'); - """; + + @BeforeAll + static void prepareDatabase() throws SQLException { + try (var connection = ConnectionManager.get(); + var statement = connection.createStatement()) { + statement.execute(CREATE_SQL); + } + } @BeforeEach - @SneakyThrows - void prepareDatabase() { + void cleanData() throws SQLException { try (var connection = ConnectionManager.get(); var statement = connection.createStatement()) { statement.execute(CLEAN_SQL); - statement.execute(CREATE_SQL); - statement.execute(INSERT_SQL); } } } From b011cb16e49fa816660cd3ce7284dea9ae97133d Mon Sep 17 00:00:00 2001 From: dmatveyenka Date: Sun, 29 Jan 2023 16:10:46 +0100 Subject: [PATCH 2/8] added homework --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cd42861..5c90bfc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # junit5-trainer Задание: -- Покрыть Unit и Integration тестами 100% кода +- Покрыть Unit и Integration тестами 80-90% кода Технические детали: - Unit и Integration тесты должны запускаться в разных фазах жизненного цикла From 9e4e5a4d9da946b9a26adbb3b403dd56212dcfb0 Mon Sep 17 00:00:00 2001 From: Andrey Agarkov Date: Mon, 5 Feb 2024 09:26:57 +0100 Subject: [PATCH 3/8] Unit Test for parse(date) added --- pom.xml | 7 +++++++ .../com/dmdev/mapper/CreateUserMapper.java | 2 +- .../com/dmdev/util/LocalDateFormatter.java | 4 ++-- .../dmdev/util/LocalDateFormatterTest.java | 21 +++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/dmdev/util/LocalDateFormatterTest.java diff --git a/pom.xml b/pom.xml index 30ce822..fad27d3 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,13 @@ 5.8.2 test + + + org.assertj + assertj-core + 3.25.2 + test + com.h2database h2 diff --git a/src/main/java/com/dmdev/mapper/CreateUserMapper.java b/src/main/java/com/dmdev/mapper/CreateUserMapper.java index ce27132..c72140a 100644 --- a/src/main/java/com/dmdev/mapper/CreateUserMapper.java +++ b/src/main/java/com/dmdev/mapper/CreateUserMapper.java @@ -22,7 +22,7 @@ public static CreateUserMapper getInstance() { public User map(CreateUserDto object) { return User.builder() .name(object.getName()) - .birthday(LocalDateFormatter.format(object.getBirthday())) + .birthday(LocalDateFormatter.parse(object.getBirthday())) .email(object.getEmail()) .password(object.getPassword()) .gender(Gender.find(object.getGender()).orElse(null)) diff --git a/src/main/java/com/dmdev/util/LocalDateFormatter.java b/src/main/java/com/dmdev/util/LocalDateFormatter.java index 33e8d2b..7b326c7 100644 --- a/src/main/java/com/dmdev/util/LocalDateFormatter.java +++ b/src/main/java/com/dmdev/util/LocalDateFormatter.java @@ -13,14 +13,14 @@ public class LocalDateFormatter { private static final String PATTERN = "yyyy-MM-dd"; private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(PATTERN); - public LocalDate format(String date) { + public LocalDate parse(String date) { return LocalDate.parse(date, FORMATTER); } public boolean isValid(String date) { try { return Optional.ofNullable(date) - .map(LocalDateFormatter::format) + .map(LocalDateFormatter::parse) .isPresent(); } catch (DateTimeParseException exception) { return false; diff --git a/src/test/java/com/dmdev/util/LocalDateFormatterTest.java b/src/test/java/com/dmdev/util/LocalDateFormatterTest.java new file mode 100644 index 0000000..c7c0f41 --- /dev/null +++ b/src/test/java/com/dmdev/util/LocalDateFormatterTest.java @@ -0,0 +1,21 @@ +package com.dmdev.util; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +class LocalDateFormatterTest { + + @Test + void whenValidDate_thenParsedSuccessfully() { + LocalDate actual = LocalDateFormatter.parse("2009-03-25"); + LocalDate expected = LocalDate.of(2009, 3, 25); + assertThat(expected).isEqualTo(actual); + } + + @Test + void isValid() { + } +} \ No newline at end of file From 9672d3ff13d054b1473842863969f5425139036a Mon Sep 17 00:00:00 2001 From: Andrey Agarkov Date: Mon, 5 Feb 2024 09:38:59 +0100 Subject: [PATCH 4/8] Unit Test for parse(date) added (Invalid date case) --- src/test/java/com/dmdev/util/LocalDateFormatterTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/com/dmdev/util/LocalDateFormatterTest.java b/src/test/java/com/dmdev/util/LocalDateFormatterTest.java index c7c0f41..b4f03eb 100644 --- a/src/test/java/com/dmdev/util/LocalDateFormatterTest.java +++ b/src/test/java/com/dmdev/util/LocalDateFormatterTest.java @@ -5,6 +5,7 @@ import java.time.LocalDate; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class LocalDateFormatterTest { @@ -15,6 +16,12 @@ void whenValidDate_thenParsedSuccessfully() { assertThat(expected).isEqualTo(actual); } + @Test + void whenInValidDate_thenExceptionThrown() { + assertThatThrownBy(() -> LocalDateFormatter.parse("209-03-25")); + assertThatThrownBy(() -> LocalDateFormatter.parse("")); + } + @Test void isValid() { } From 5253adb8800ed357b39ae543380fddd3dda723d7 Mon Sep 17 00:00:00 2001 From: Andrey Agarkov Date: Mon, 5 Feb 2024 10:46:01 +0100 Subject: [PATCH 5/8] Parameterized Unit Test for isValid(date) --- pom.xml | 9 ++++++++- .../dmdev/util/LocalDateFormatterTest.java | 20 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index fad27d3..b5a0f39 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ 17 17 1.18.22 + 5.10.1 @@ -33,7 +34,7 @@ org.junit.jupiter junit-jupiter-engine - 5.8.2 + ${junit5.version} test @@ -43,6 +44,12 @@ 3.25.2 test + + org.junit.jupiter + junit-jupiter-params + ${junit5.version} + test + com.h2database h2 diff --git a/src/test/java/com/dmdev/util/LocalDateFormatterTest.java b/src/test/java/com/dmdev/util/LocalDateFormatterTest.java index b4f03eb..a6dc612 100644 --- a/src/test/java/com/dmdev/util/LocalDateFormatterTest.java +++ b/src/test/java/com/dmdev/util/LocalDateFormatterTest.java @@ -1,11 +1,16 @@ package com.dmdev.util; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.time.LocalDate; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.params.provider.Arguments.arguments; class LocalDateFormatterTest { @@ -22,7 +27,18 @@ void whenInValidDate_thenExceptionThrown() { assertThatThrownBy(() -> LocalDateFormatter.parse("")); } - @Test - void isValid() { + @ParameterizedTest + @MethodSource("getDatesTestCases") + void checkIfDateStringsValidOrNot(String date, boolean expected) { + boolean actual = LocalDateFormatter.isValid(date); + assertThat(actual).isEqualTo(expected); + } + + static Stream getDatesTestCases() { + return Stream.of( + arguments("2009-01-01", true), + arguments("2009-13-13", false), + arguments("", false) + ); } } \ No newline at end of file From a9b6f3c19f55d450379dc1ffcc2ce3ed55d772d8 Mon Sep 17 00:00:00 2001 From: Andrey Agarkov Date: Mon, 5 Feb 2024 13:48:33 +0100 Subject: [PATCH 6/8] Unit Tests for CreateUserValidator --- .../validator/CreateUserValidatorTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/test/java/com/dmdev/validator/CreateUserValidatorTest.java diff --git a/src/test/java/com/dmdev/validator/CreateUserValidatorTest.java b/src/test/java/com/dmdev/validator/CreateUserValidatorTest.java new file mode 100644 index 0000000..387a6c3 --- /dev/null +++ b/src/test/java/com/dmdev/validator/CreateUserValidatorTest.java @@ -0,0 +1,97 @@ +package com.dmdev.validator; + +import com.dmdev.dto.CreateUserDto; +import com.dmdev.entity.Gender; +import com.dmdev.entity.Role; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CreateUserValidatorTest { + + private final CreateUserValidator validator = CreateUserValidator.getInstance(); + + @Test + void whenDtoIsValid_thenValidationSuccessful() { + CreateUserDto dto = CreateUserDto.builder() + .name("Mike Pierson") + .birthday("2001-01-01") + .email("test@gmail.com") + .password("test") + .role(Role.USER.name()) + .gender(Gender.MALE.name()) + .build(); + + ValidationResult actual = validator.validate(dto); + + assertThat(actual.isValid()).isTrue(); + } + + @Test + void whenBirthdayIsInvalid_thenValidationFails() { + CreateUserDto dto = CreateUserDto.builder() + .name("Mike Pierson") + .birthday("201-01-01") + .email("test@gmail.com") + .password("test") + .role(Role.USER.name()) + .gender(Gender.MALE.name()) + .build(); + + ValidationResult actual = validator.validate(dto); + + assertThat(actual.isValid()).isFalse(); + assertThat(actual.getErrors().get(0).getMessage()).isEqualTo("Birthday is invalid"); + } + + @Test + void whenRoleIsInvalid_thenValidationFails() { + CreateUserDto dto = CreateUserDto.builder() + .name("Mike Pierson") + .birthday("2001-01-01") + .email("test@gmail.com") + .password("test") + .role("invalid role") + .gender(Gender.MALE.name()) + .build(); + + ValidationResult actual = validator.validate(dto); + + assertThat(actual.isValid()).isFalse(); + assertThat(actual.getErrors().get(0).getMessage()).isEqualTo("Role is invalid"); + } + + @Test + void whenGenderIsInvalid_thenValidationFails() { + CreateUserDto dto = CreateUserDto.builder() + .name("Mike Pierson") + .birthday("2001-01-01") + .email("test@gmail.com") + .password("test") + .role(Role.USER.name()) + .gender("invalid gender") + .build(); + + ValidationResult actual = validator.validate(dto); + + assertThat(actual.isValid()).isFalse(); + assertThat(actual.getErrors().get(0).getMessage()).isEqualTo("Gender is invalid"); + } + + @Test + void whenCheckedFieldsAreInvalid_thenValidationFails() { + CreateUserDto dto = CreateUserDto.builder() + .name("Mike Pierson") + .birthday("invalid birthday") + .email("test@gmail.com") + .password("test") + .role("invalid role") + .gender("invalid gender") + .build(); + + ValidationResult actual = validator.validate(dto); + + assertThat(actual.isValid()).isFalse(); + assertThat(actual.getErrors().stream().map(e -> e.getCode()).toList()).contains("invalid.birthday", "invalid.gender", "invalid.role"); + } +} \ No newline at end of file From 960a6192791fe6dab40b0d015deae0d127bcf9db Mon Sep 17 00:00:00 2001 From: Andrey Agarkov Date: Mon, 5 Feb 2024 14:13:08 +0100 Subject: [PATCH 7/8] Unit Test for CreateUserMapper --- .../dmdev/mapper/CreateUserMapperTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/test/java/com/dmdev/mapper/CreateUserMapperTest.java diff --git a/src/test/java/com/dmdev/mapper/CreateUserMapperTest.java b/src/test/java/com/dmdev/mapper/CreateUserMapperTest.java new file mode 100644 index 0000000..da86b92 --- /dev/null +++ b/src/test/java/com/dmdev/mapper/CreateUserMapperTest.java @@ -0,0 +1,42 @@ +package com.dmdev.mapper; + +import com.dmdev.dto.CreateUserDto; +import com.dmdev.entity.Gender; +import com.dmdev.entity.Role; +import com.dmdev.entity.User; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +class CreateUserMapperTest { + + private static final CreateUserMapper mapper = CreateUserMapper.getInstance(); + + @Test + void whenDtoIsValid_thenUserIsValid() { + + CreateUserDto validDto = CreateUserDto.builder() + .name("Mike Pierson") + .birthday("2001-01-01") + .email("test@gmail.com") + .password("test") + .role(Role.USER.name()) + .gender(Gender.MALE.name()) + .build(); + + User actualUser = mapper.map(validDto); + + User validUser = User.builder() + .name("Mike Pierson") + .birthday(LocalDate.of(2001, 1, 1)) + .email("test@gmail.com") + .password("test") + .role(Role.USER) + .gender(Gender.MALE) + .build(); + + assertThat(actualUser).isEqualTo(validUser); + } +} \ No newline at end of file From 9d18f5cf1d2c654dbc50c14704dc2553dac99135 Mon Sep 17 00:00:00 2001 From: Andrey Agarkov Date: Mon, 5 Feb 2024 17:06:58 +0100 Subject: [PATCH 8/8] Fixes after merge --- .../java/com/dmdev/dto/CreateUserDto.java | 15 ++++++++ src/main/java/com/dmdev/entity/Gender.java | 15 ++++++++ src/main/java/com/dmdev/entity/Role.java | 15 ++++++++ src/main/java/com/dmdev/entity/User.java | 22 ++++++++++++ .../CreateSubscriptionValidator.java | 1 + .../dmdev/validator/CreateUserValidator.java | 34 +++++++++++++++++++ .../com/dmdev/validator/ValidationResult.java | 4 +++ .../validator/CreateUserValidatorTest.java | 2 +- 8 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/dmdev/dto/CreateUserDto.java create mode 100644 src/main/java/com/dmdev/entity/Gender.java create mode 100644 src/main/java/com/dmdev/entity/Role.java create mode 100644 src/main/java/com/dmdev/entity/User.java create mode 100644 src/main/java/com/dmdev/validator/CreateUserValidator.java diff --git a/src/main/java/com/dmdev/dto/CreateUserDto.java b/src/main/java/com/dmdev/dto/CreateUserDto.java new file mode 100644 index 0000000..8c1a3a4 --- /dev/null +++ b/src/main/java/com/dmdev/dto/CreateUserDto.java @@ -0,0 +1,15 @@ +package com.dmdev.dto; + +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class CreateUserDto { + String name; + String birthday; + String email; + String password; + String role; + String gender; +} \ No newline at end of file diff --git a/src/main/java/com/dmdev/entity/Gender.java b/src/main/java/com/dmdev/entity/Gender.java new file mode 100644 index 0000000..7f49cc3 --- /dev/null +++ b/src/main/java/com/dmdev/entity/Gender.java @@ -0,0 +1,15 @@ +package com.dmdev.entity; + +import java.util.Arrays; +import java.util.Optional; + +public enum Gender { + MALE, + FEMALE; + + public static Optional find(String gender) { + return Arrays.stream(values()) + .filter(it -> it.name().equals(gender)) + .findFirst(); + } +} \ No newline at end of file diff --git a/src/main/java/com/dmdev/entity/Role.java b/src/main/java/com/dmdev/entity/Role.java new file mode 100644 index 0000000..c0ea6a8 --- /dev/null +++ b/src/main/java/com/dmdev/entity/Role.java @@ -0,0 +1,15 @@ +package com.dmdev.entity; + +import java.util.Arrays; +import java.util.Optional; + +public enum Role { + USER, + ADMIN; + + public static Optional find(String role) { + return Arrays.stream(values()) + .filter(it -> it.name().equals(role)) + .findFirst(); + } +} \ No newline at end of file diff --git a/src/main/java/com/dmdev/entity/User.java b/src/main/java/com/dmdev/entity/User.java new file mode 100644 index 0000000..a7b1f29 --- /dev/null +++ b/src/main/java/com/dmdev/entity/User.java @@ -0,0 +1,22 @@ +package com.dmdev.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class User { + private Integer id; + private String name; + private LocalDate birthday; + private String email; + private String password; + private Role role; + private Gender gender; +} \ No newline at end of file diff --git a/src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java b/src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java index c06e54d..09c0b2e 100644 --- a/src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java +++ b/src/main/java/com/dmdev/validator/CreateSubscriptionValidator.java @@ -33,6 +33,7 @@ public ValidationResult validate(CreateSubscriptionDto object) { if (object.getExpirationDate() == null || object.getExpirationDate().isBefore(Instant.now())) { validationResult.add(Error.of(103, "expirationDate is invalid")); } + return validationResult; } } diff --git a/src/main/java/com/dmdev/validator/CreateUserValidator.java b/src/main/java/com/dmdev/validator/CreateUserValidator.java new file mode 100644 index 0000000..f5f5e2e --- /dev/null +++ b/src/main/java/com/dmdev/validator/CreateUserValidator.java @@ -0,0 +1,34 @@ +package com.dmdev.validator; + +import com.dmdev.dto.CreateUserDto; +import com.dmdev.entity.Gender; +import com.dmdev.entity.Role; +import com.dmdev.util.LocalDateFormatter; +import lombok.NoArgsConstructor; + +import static lombok.AccessLevel.PRIVATE; + +@NoArgsConstructor(access = PRIVATE) +public class CreateUserValidator implements Validator { + + private static final CreateUserValidator INSTANCE = new CreateUserValidator(); + + public static CreateUserValidator getInstance() { + return INSTANCE; + } + + @Override + public ValidationResult validate(CreateUserDto object) { + var validationResult = new ValidationResult(); + if (!LocalDateFormatter.isValid(object.getBirthday())) { + validationResult.add(Error.of(104, "Birthday is invalid")); + } + if (Gender.find(object.getGender()).isEmpty()) { + validationResult.add(Error.of(105, "Gender is invalid")); + } + if (Role.find(object.getRole()).isEmpty()) { + validationResult.add(Error.of(106, "Role is invalid")); + } + return validationResult; + } +} \ No newline at end of file diff --git a/src/main/java/com/dmdev/validator/ValidationResult.java b/src/main/java/com/dmdev/validator/ValidationResult.java index e73b58a..78ba497 100644 --- a/src/main/java/com/dmdev/validator/ValidationResult.java +++ b/src/main/java/com/dmdev/validator/ValidationResult.java @@ -17,4 +17,8 @@ public void add(Error error) { public boolean hasErrors() { return !errors.isEmpty(); } + + public boolean isValid() { + return errors.isEmpty(); + } } diff --git a/src/test/java/com/dmdev/validator/CreateUserValidatorTest.java b/src/test/java/com/dmdev/validator/CreateUserValidatorTest.java index 387a6c3..c9c3611 100644 --- a/src/test/java/com/dmdev/validator/CreateUserValidatorTest.java +++ b/src/test/java/com/dmdev/validator/CreateUserValidatorTest.java @@ -92,6 +92,6 @@ void whenCheckedFieldsAreInvalid_thenValidationFails() { ValidationResult actual = validator.validate(dto); assertThat(actual.isValid()).isFalse(); - assertThat(actual.getErrors().stream().map(e -> e.getCode()).toList()).contains("invalid.birthday", "invalid.gender", "invalid.role"); + assertThat(actual.getErrors().stream().map(e -> e.getCode()).toList()).contains(104, 105, 106); } } \ No newline at end of file