diff --git a/checkstyle.xml b/checkstyle.xml index 230e67eb..74f37d8b 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -250,4 +250,8 @@ + + + + diff --git a/pom.xml b/pom.xml index 870ae852..ed574e85 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,16 @@ org.springframework.boot spring-boot-starter-validation + + org.aspectj + aspectjweaver + 1.9.20 + + + + org.springframework.boot + spring-boot-starter-aop + diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/src/main/java/ru/practicum/shareit/ShareItApp.java index a00ad567..fe48b8ff 100644 --- a/src/main/java/ru/practicum/shareit/ShareItApp.java +++ b/src/main/java/ru/practicum/shareit/ShareItApp.java @@ -6,8 +6,7 @@ @SpringBootApplication public class ShareItApp { - public static void main(String[] args) { - SpringApplication.run(ShareItApp.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(ShareItApp.class, args); + } } diff --git a/src/main/java/ru/practicum/shareit/aspects/LoggingAspect.java b/src/main/java/ru/practicum/shareit/aspects/LoggingAspect.java new file mode 100644 index 00000000..a056794c --- /dev/null +++ b/src/main/java/ru/practicum/shareit/aspects/LoggingAspect.java @@ -0,0 +1,114 @@ +package ru.practicum.shareit.aspects; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; +import ru.practicum.shareit.constants.MyConstants; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.user.dto.UserDto; + +import java.util.List; + +@Component +@Aspect +@Slf4j +public class LoggingAspect { + + @AfterReturning(pointcut = MyConstants.ADD_USER_POINTCUT, returning = "userDto") + public void afterReturningAddUserAdvices(UserDto userDto) { + log.info("Пользователь " + userDto + "добавлен."); + } + + @AfterThrowing(pointcut = MyConstants.ADD_USER_POINTCUT, throwing = "exception") + public void afterThrowingAddUserAdvices(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } + + @AfterReturning(pointcut = MyConstants.REMOVE_USER_POINTCUT, returning = "response") + public void afterReturningRemoveUserByIdAdvices(String response) { + log.info(response); + } + + @AfterThrowing(pointcut = MyConstants.REMOVE_USER_POINTCUT, throwing = "exception") + public void afterThrowingRemoveUserByIdAdvices(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } + + @AfterReturning(pointcut = MyConstants.GET_ALL_USERS_POINTCUT) + public void afterReturningGetAllUserAdvice() { + log.info("Список всех пользователей получен."); + } + + @AfterReturning(pointcut = MyConstants.UPDATE_USER_POINTCUT, returning = "userDto") + public void afterReturningUpdateUserAdvice(UserDto userDto) { + log.info("Пользователь с id:" + userDto.getId() + " обновлён."); + } + + @AfterThrowing(pointcut = MyConstants.UPDATE_USER_POINTCUT, throwing = "exception") + public void afterThrowingUpdateUserAdvice(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } + + @AfterReturning(pointcut = MyConstants.ADD_ITEM_POINTCUT, returning = "itemDto") + public void afterReturningAddItemAdvice(ItemDto itemDto) { + log.info("Item: " + itemDto.getName() + " с id:" + itemDto.getId() + " добавлен."); + } + + @AfterThrowing(pointcut = MyConstants.ADD_ITEM_POINTCUT, throwing = "exception") + public void afterThrowingAddItemAdvice(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } + + @AfterReturning(pointcut = MyConstants.UPDATE_ITEM_POINTCUT, returning = "itemDto") + public void afterReturningUpdateItemAdvice(ItemDto itemDto) { + log.info("Item с id:" + itemDto.getId() + " обновлён."); + } + + @AfterThrowing(pointcut = MyConstants.UPDATE_ITEM_POINTCUT, throwing = "exception") + public void afterThrowingUpdateItemAdvice(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } + + @AfterReturning(pointcut = MyConstants.GET_ITEM_BY_ID_POINTCUT, returning = "itemDto") + public void afterReturningGetItemByIdAdvice(ItemDto itemDto) { + log.info("Получен Item: " + itemDto); + } + + @AfterThrowing(pointcut = MyConstants.GET_ITEM_BY_ID_POINTCUT, throwing = "exception") + public void afterThrowingGetItemByIdAdvice(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } + + @AfterReturning(pointcut = MyConstants.GET_ALL_ITEM_POINTCUT) + public void afterReturningGetAllItemForOwnerAdvice() { + log.info("Список всех Item получен."); + } + + @AfterThrowing(pointcut = MyConstants.GET_ALL_ITEM_POINTCUT, throwing = "exception") + public void afterThrowingGetAllItemForOwnerAdvice(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } + + @AfterReturning(pointcut = MyConstants.SEARCH_ITEM_POINTCUT, returning = "itemDtoList") + public void afterReturningSearchItemAdvice(List itemDtoList) { + log.info("Список Item по запросу получен: " + itemDtoList); + } + + @AfterThrowing(pointcut = MyConstants.SEARCH_ITEM_POINTCUT, throwing = "exception") + public void afterThrowingSearchItemAdvice(JoinPoint joinPoint, Throwable exception) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); + } +} + diff --git a/src/main/java/ru/practicum/shareit/booking/Booking.java b/src/main/java/ru/practicum/shareit/booking/Booking.java index 2d9c6668..8d8aa113 100644 --- a/src/main/java/ru/practicum/shareit/booking/Booking.java +++ b/src/main/java/ru/practicum/shareit/booking/Booking.java @@ -1,7 +1,22 @@ package ru.practicum.shareit.booking; -/** - * TODO Sprint add-bookings. - */ +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Data +@EqualsAndHashCode +@ToString public class Booking { + + private int id; + private LocalDateTime start; + private LocalDateTime end; + private Item item; + private User booker; + private BookingStatus status; } diff --git a/src/main/java/ru/practicum/shareit/booking/BookingStatus.java b/src/main/java/ru/practicum/shareit/booking/BookingStatus.java new file mode 100644 index 00000000..bdd840a0 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/BookingStatus.java @@ -0,0 +1,8 @@ +package ru.practicum.shareit.booking; + +public enum BookingStatus { + WAITING, + APPROVED, + REJECTED, + CANCELED; +} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java index 861de9e0..b7fa7d9c 100644 --- a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java +++ b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java @@ -1,7 +1,23 @@ package ru.practicum.shareit.booking.dto; -/** - * TODO Sprint add-bookings. - */ +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import ru.practicum.shareit.booking.BookingStatus; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Data +@EqualsAndHashCode +@ToString public class BookingDto { + + private int id; + private LocalDateTime start; + private LocalDateTime end; + private Item item; + private User booker; + private BookingStatus status; } diff --git a/src/main/java/ru/practicum/shareit/constants/MyConstants.java b/src/main/java/ru/practicum/shareit/constants/MyConstants.java new file mode 100644 index 00000000..3ca2b2b7 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/constants/MyConstants.java @@ -0,0 +1,29 @@ +package ru.practicum.shareit.constants; + +public class MyConstants { + public static final String ADD_USER_POINTCUT = "execution(public ru.practicum.shareit.user.dto.UserDto ru.practicum.shareit.user.dao.UserDaoImpl.addUser" + + "(ru.practicum.shareit.user.User))"; + public static final String REMOVE_USER_POINTCUT = "execution(public String ru.practicum.shareit.user.dao.UserDaoImpl.removeUserById" + + "(*))"; + + public static final String GET_ALL_USERS_POINTCUT = "execution(public java.util.List " + + "ru.practicum.shareit.user.dao.UserDaoImpl.getAllUsers())"; + + public static final String UPDATE_USER_POINTCUT = "execution(public ru.practicum.shareit.user.dto.UserDto " + + "ru.practicum.shareit.user.dao.UserDaoImpl.UpdateUser(..))"; + + public static final String ADD_ITEM_POINTCUT = "execution(public ru.practicum.shareit.item.dto.ItemDto " + + "ru.practicum.shareit.item.dao.ItemDaoImpl.addItem(..))"; + + public static final String UPDATE_ITEM_POINTCUT = "execution(public ru.practicum.shareit.item.dto.ItemDto " + + "ru.practicum.shareit.item.dao.ItemDaoImpl.updateItem(..))"; + + public static final String GET_ITEM_BY_ID_POINTCUT = "execution(public ru.practicum.shareit.item.dto.ItemDto " + + "ru.practicum.shareit.item.dao.ItemDaoImpl.getItemById(..))"; + + public static final String GET_ALL_ITEM_POINTCUT = "execution(public java.util.List " + + "ru.practicum.shareit.item.dao.ItemDaoImpl.getAllItemForOwner(..))"; + + public static final String SEARCH_ITEM_POINTCUT = "execution(public java.util.List " + + "ru.practicum.shareit.item.dao.ItemDaoImpl.searchItem(..))"; +} diff --git a/src/main/java/ru/practicum/shareit/exception/EmailDuplicateException.java b/src/main/java/ru/practicum/shareit/exception/EmailDuplicateException.java new file mode 100644 index 00000000..3c1e0e3d --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/EmailDuplicateException.java @@ -0,0 +1,8 @@ +package ru.practicum.shareit.exception; + +public class EmailDuplicateException extends RuntimeException { + + public EmailDuplicateException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java new file mode 100644 index 00000000..051c25fb --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,23 @@ +package ru.practicum.shareit.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ErrorHandler { + + @ExceptionHandler + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse noDataFound(NoDataFoundException e) { + return new ErrorResponse(e.getMessage(), "Данные не найдены."); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.CONFLICT) + public ErrorResponse emailDuplicate(EmailDuplicateException e) { + return new ErrorResponse(e.getMessage(), "Данный email уже существует."); + } + +} diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java b/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java new file mode 100644 index 00000000..e98af6f2 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java @@ -0,0 +1,22 @@ +package ru.practicum.shareit.exception; + +import lombok.Data; + +@Data +public class ErrorResponse { + private String error; + private String description; + + public ErrorResponse(String error, String description) { + this.error = error; + this.description = description; + } + + public String getError() { + return error; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/ru/practicum/shareit/exception/NoDataFoundException.java b/src/main/java/ru/practicum/shareit/exception/NoDataFoundException.java new file mode 100644 index 00000000..dbef4b4b --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/NoDataFoundException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class NoDataFoundException extends RuntimeException { + public NoDataFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java index bb17668b..97d79911 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -1,12 +1,46 @@ package ru.practicum.shareit.item; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.item.service.ItemService; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; -/** - * TODO Sprint add-controllers. - */ @RestController @RequestMapping("/items") public class ItemController { + @Autowired + ItemService itemService; + + @PostMapping + public ItemDto addItem(@RequestHeader("X-Sharer-User-Id") int userId, + @Valid @RequestBody Item item) { + return itemService.addItem(userId, item); + } + + @GetMapping("/{id}") + public ItemDto getItemById(@PathVariable(name = "id") int itemId) { + return itemService.getItemDtoById(itemId); + } + + @GetMapping + public List getAllItemForOwner(@RequestHeader("X-Sharer-User-Id") int userId) { + return itemService.getAllItemForOwner(userId); + } + + @PatchMapping("/{id}") + public ItemDto updateItem(@RequestHeader("X-Sharer-User-Id") int userId, + @PathVariable(name = "id") int itemId, + @RequestBody Map fields) { + return itemService.updateItem(userId, itemId, fields); + } + + @GetMapping("/search") + public List searchItem(@RequestParam(name = "text") String request) { + return itemService.searchItem(request); + } } diff --git a/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/ItemMapper.java new file mode 100644 index 00000000..9171295c --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/ItemMapper.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.item; + +import org.springframework.stereotype.Component; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; + +@Component +public class ItemMapper { + public ItemDto itemDto(Item item) { + return new ItemDto() + .setId(item.getId()) + .setName(item.getName()) + .setOwnerId(item.getOwnerId()) + .setRequest(item.getRequest()) + .setDescription(item.getDescription()) + .setAvailable(item.getAvailable()); + } +} diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemDao.java b/src/main/java/ru/practicum/shareit/item/dao/ItemDao.java new file mode 100644 index 00000000..cf00efc6 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/dao/ItemDao.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.item.dao; + +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; + +import java.util.List; +import java.util.Map; + +public interface ItemDao { + ItemDto addItem(int userId, Item item); + + ItemDto updateItem(int userId, int itemId, Map fields); + + Item getItemById(int itemId); + + ItemDto getItemDtoById(int itemId); + + List getAllItemForOwner(int ownerId); + + List searchItem(String request); +} diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemDaoImpl.java b/src/main/java/ru/practicum/shareit/item/dao/ItemDaoImpl.java new file mode 100644 index 00000000..ec96a8f8 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/dao/ItemDaoImpl.java @@ -0,0 +1,100 @@ +package ru.practicum.shareit.item.dao; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.util.ReflectionUtils; +import ru.practicum.shareit.exception.NoDataFoundException; +import ru.practicum.shareit.item.ItemMapper; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@Repository +public class ItemDaoImpl implements ItemDao { + private final Map itemMap = new HashMap<>(); + @Autowired + private ItemMapper itemMapper; + private int count = 0; + + @Override + public ItemDto addItem(int userId, Item item) { + item.setOwnerId(userId); + item.setId(++count); + ItemDto itemDto = itemMapper.itemDto(item); + itemMap.put(item.getId(), item); + return itemDto; + } + + @Override + public ItemDto updateItem(int userId, int itemId, Map fields) { + Item item = getItemById(itemId); + if (item.getOwnerId() == userId) { + fields.forEach((key, value) -> { + Field field = ReflectionUtils.findField(Item.class, (String) key); + field.setAccessible(true); + if (!((String) key).equalsIgnoreCase("id") + && !((String) key).equalsIgnoreCase("ownerId")) { + ReflectionUtils.setField(field, item, value); + } + }); + return itemMapper.itemDto(item); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + + " не является владельцем данной вещи. Изменения не сохранены."); + } + } + + @Override + public Item getItemById(int itemId) { + if (itemMap.containsKey(itemId)) { + return itemMap.get(itemId); + } else { + throw new NoDataFoundException("Item с id: " + itemId + " не найдена."); + } + } + + @Override + public ItemDto getItemDtoById(int itemId) { + if (itemMap.containsKey(itemId)) { + return itemMapper.itemDto(itemMap.get(itemId)); + } else { + throw new NoDataFoundException("Item с id: " + itemId + " не найдена."); + } + } + + @Override + public List getAllItemForOwner(int ownerId) { + List itemDtoList = new ArrayList<>(); + for (Item item : itemMap.values()) { + if (item.getOwnerId() == ownerId) { + itemDtoList.add(itemMapper.itemDto(item)); + } + } + return itemDtoList; + } + + @Override + public List searchItem(String request) { + if (request.isBlank()) { + return new ArrayList<>(); + } + List listItem = new ArrayList<>(); + Pattern pattern = Pattern.compile(request.toLowerCase()); + for (Item item : itemMap.values().stream().filter(item -> item.getAvailable() == true) + .collect(Collectors.toList())) { + Matcher matcher = pattern.matcher((item.getName() + " " + item.getDescription()).toLowerCase()); + if (matcher.find()) { + listItem.add(itemMapper.itemDto(item)); + } + } + return listItem; + } +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java index 9319d7d7..0ac7296d 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -1,7 +1,27 @@ package ru.practicum.shareit.item.dto; -/** - * TODO Sprint add-controllers. - */ +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode +@ToString public class ItemDto { + + private int id; + @NotBlank(message = "Поле \'name\' не может быть пустым.") + @NotNull(message = "Поле \'name\' не может быть пустым.") + private String name; + @NotBlank(message = "Поле \'description\' не может быть пустым.") + private String description; + @NotNull(message = "Поле \'available\' не может быть пустым.") + private Boolean available; + private int ownerId; + private String request; } diff --git a/src/main/java/ru/practicum/shareit/item/model/Item.java b/src/main/java/ru/practicum/shareit/item/model/Item.java index 44eb73dd..292918d7 100644 --- a/src/main/java/ru/practicum/shareit/item/model/Item.java +++ b/src/main/java/ru/practicum/shareit/item/model/Item.java @@ -1,7 +1,25 @@ package ru.practicum.shareit.item.model; -/** - * TODO Sprint add-controllers. - */ +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +@EqualsAndHashCode +@ToString public class Item { + + private int id; + @NotBlank(message = "Поле \'name\' не может быть пустым.") + @NotNull(message = "Поле \'name\' не может быть пустым.") + private String name; + @NotBlank(message = "Поле \'description\' не может быть пустым.") + private String description; + @NotNull(message = "Поле \'available\' не может быть пустым.") + private Boolean available; + private int ownerId; + private String request; } diff --git a/src/main/java/ru/practicum/shareit/item/service/ItemService.java b/src/main/java/ru/practicum/shareit/item/service/ItemService.java new file mode 100644 index 00000000..3c29d85b --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/service/ItemService.java @@ -0,0 +1,19 @@ +package ru.practicum.shareit.item.service; + +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; + +import java.util.List; +import java.util.Map; + +public interface ItemService { + ItemDto addItem(int userId, Item item); + + ItemDto updateItem(int userId, int itemId, Map fields); + + ItemDto getItemDtoById(int itemId); + + List getAllItemForOwner(int ownerId); + + List searchItem(String request); +} diff --git a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java new file mode 100644 index 00000000..8956af7e --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java @@ -0,0 +1,45 @@ +package ru.practicum.shareit.item.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.item.dao.ItemDao; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.dao.UserDao; + +import java.util.List; +import java.util.Map; + +@Service +public class ItemServiceImpl implements ItemService { + @Autowired + private ItemDao itemDao; + @Autowired + private UserDao userDao; + + @Override + public ItemDto addItem(int userId, Item item) { + userDao.getUserById(userId); + return itemDao.addItem(userId, item); + } + + @Override + public ItemDto updateItem(int userId, int itemId, Map fields) { + return itemDao.updateItem(userId, itemId, fields); + } + + @Override + public ItemDto getItemDtoById(int itemId) { + return itemDao.getItemDtoById(itemId); + } + + @Override + public List getAllItemForOwner(int ownerId) { + return itemDao.getAllItemForOwner(ownerId); + } + + @Override + public List searchItem(String request) { + return itemDao.searchItem(request); + } +} diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequest.java b/src/main/java/ru/practicum/shareit/request/ItemRequest.java index 95d6f23c..7743bffb 100644 --- a/src/main/java/ru/practicum/shareit/request/ItemRequest.java +++ b/src/main/java/ru/practicum/shareit/request/ItemRequest.java @@ -1,7 +1,19 @@ package ru.practicum.shareit.request; -/** - * TODO Sprint add-item-requests. - */ +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Data +@EqualsAndHashCode +@ToString public class ItemRequest { + + private int id; + private String description; + private User requester; + private LocalDateTime created; } diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java index 7b3ed544..ec055f4c 100644 --- a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java +++ b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java @@ -1,7 +1,21 @@ package ru.practicum.shareit.request.dto; -/** - * TODO Sprint add-item-requests. - */ + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Data +@EqualsAndHashCode +@ToString + public class ItemRequestDto { + + private int id; + private String description; + private User requester; + private LocalDateTime created; } diff --git a/src/main/java/ru/practicum/shareit/user/User.java b/src/main/java/ru/practicum/shareit/user/User.java index ae6e7f33..b4e435dd 100644 --- a/src/main/java/ru/practicum/shareit/user/User.java +++ b/src/main/java/ru/practicum/shareit/user/User.java @@ -1,7 +1,25 @@ package ru.practicum.shareit.user; -/** - * TODO Sprint add-controllers. - */ +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.springframework.stereotype.Component; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode +@ToString +@Component public class User { + + private int id; + @NotBlank(message = "Name - не может быть пустым.") + private String name; + @NotBlank(message = "Email - не может быть пустым.") + @Email(message = "Не правильный формат email") + private String email; } diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/src/main/java/ru/practicum/shareit/user/UserController.java index 03039b9d..c54b304e 100644 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/src/main/java/ru/practicum/shareit/user/UserController.java @@ -1,12 +1,44 @@ package ru.practicum.shareit.user; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.service.UserService; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + -/** - * TODO Sprint add-controllers. - */ @RestController @RequestMapping(path = "/users") public class UserController { + + @Autowired + UserService userService; + + @PostMapping + public UserDto addUser(@Valid @RequestBody User user) { + return userService.addUser(user); + } + + @GetMapping + public List getAllUsers() { + return userService.getAllUsers(); + } + + @GetMapping("/{id}") + public UserDto getUserById(@PathVariable(name = "id") int userId) { + return userService.getUserDtoById(userId); + } + + @DeleteMapping("/{id}") + public String removeUserById(@PathVariable(name = "id") int userId) { + return userService.removeUserById(userId); + } + + @PatchMapping("/{id}") + public UserDto UpdateUser(@PathVariable int id, @RequestBody Map fields) { + return userService.UpdateUser(id, fields); + } } diff --git a/src/main/java/ru/practicum/shareit/user/UserMapper.java b/src/main/java/ru/practicum/shareit/user/UserMapper.java new file mode 100644 index 00000000..f3c1d390 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/UserMapper.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.user; + +import org.springframework.stereotype.Component; +import ru.practicum.shareit.user.dto.UserDto; + +@Component +public class UserMapper { + public UserDto toUserDto(User user) { + return new UserDto() + .setId(user.getId()) + .setName(user.getName()) + .setEmail(user.getEmail()); + } +} diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserDao.java b/src/main/java/ru/practicum/shareit/user/dao/UserDao.java new file mode 100644 index 00000000..6227ff18 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/dao/UserDao.java @@ -0,0 +1,23 @@ +package ru.practicum.shareit.user.dao; + + +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.dto.UserDto; + +import java.util.List; +import java.util.Map; + +public interface UserDao { + + UserDto addUser(User user); + + List getAllUsers(); + + UserDto getUserDtoById(int userId); + + User getUserById(int userId); + + String removeUserById(int userId); + + UserDto UpdateUser(int userId, Map fields); +} diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserDaoImpl.java b/src/main/java/ru/practicum/shareit/user/dao/UserDaoImpl.java new file mode 100644 index 00000000..6c536131 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/dao/UserDaoImpl.java @@ -0,0 +1,101 @@ +package ru.practicum.shareit.user.dao; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.util.ReflectionUtils; +import ru.practicum.shareit.exception.EmailDuplicateException; +import ru.practicum.shareit.exception.NoDataFoundException; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserMapper; +import ru.practicum.shareit.user.dto.UserDto; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Repository +@Slf4j +public class UserDaoImpl implements UserDao { + private final Map userMap = new HashMap<>(); + private int count = 0; + @Autowired + private UserMapper userMapper; + + @Override + public UserDto addUser(User user) { + checkEmailForDuplicate(user.getEmail()); + user.setId(++count); + UserDto userDto = userMapper.toUserDto(user); + userMap.putIfAbsent(user.getId(), user); + return userDto; + } + + @Override + public List getAllUsers() { + List userDtoList = new ArrayList<>(); + for (User user : userMap.values()) { + userDtoList.add(userMapper.toUserDto(user)); + } + return userDtoList; + } + + @Override + public User getUserById(int userId) { + if (userMap.containsKey(userId)) { + return userMap.get(userId); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } + + @Override + public UserDto getUserDtoById(int userId) { + if (userMap.containsKey(userId)) { + return userMapper.toUserDto(userMap.get(userId)); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } + + @Override + public String removeUserById(int userId) { + if (userMap.containsKey(userId)) { + userMap.remove(userId); + userMap.remove(userId); + return "Пользователь с id:" + userId + " удалён."; + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } + + @Override + public UserDto UpdateUser(int userId, Map fields) { + if (userMap.containsKey(userId)) { + User user = getUserById(userId); + fields.forEach((key, value) -> { + Field field = ReflectionUtils.findField(User.class, (String) key); + if (((String) key).equalsIgnoreCase("email") && !user.getEmail() + .equalsIgnoreCase((String) value)) { + checkEmailForDuplicate((String) value); + } + field.setAccessible(true); + ReflectionUtils.setField(field, user, value); + }); + return userMapper.toUserDto(user); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } + + private void checkEmailForDuplicate(String email) { + List userDtoList = getAllUsers(); + for (UserDto userDto : userDtoList) { + if (userDto.getEmail().equalsIgnoreCase(email)) { + throw new EmailDuplicateException("Email: " + email + " уже существует"); + } + } + } +} diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/src/main/java/ru/practicum/shareit/user/dto/UserDto.java new file mode 100644 index 00000000..43ef18cf --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/dto/UserDto.java @@ -0,0 +1,25 @@ +package ru.practicum.shareit.user.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.springframework.stereotype.Component; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode +@ToString +@Component +public class UserDto { + + private int id; + @NotBlank(message = "Name - не может быть пустым.") + private String name; + @NotBlank(message = "Email - не может быть пустым.") + @Email(message = "Не правильный формат email") + private String email; +} diff --git a/src/main/java/ru/practicum/shareit/user/service/UserService.java b/src/main/java/ru/practicum/shareit/user/service/UserService.java new file mode 100644 index 00000000..6b50b57f --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/service/UserService.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.user.service; + +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.dto.UserDto; + +import java.util.List; +import java.util.Map; + +public interface UserService { + + public UserDto addUser(User user); + + public List getAllUsers(); + + public UserDto getUserDtoById(int userId); + + public String removeUserById(int userId); + + public UserDto UpdateUser(int userId, Map fields); +} diff --git a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java new file mode 100644 index 00000000..34dd9630 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java @@ -0,0 +1,43 @@ +package ru.practicum.shareit.user.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.dao.UserDao; +import ru.practicum.shareit.user.dto.UserDto; + +import java.util.List; +import java.util.Map; + +@Service +public class UserServiceImpl implements UserService { + + @Autowired + UserDao userDao; + + @Override + + public UserDto addUser(User user) { + return userDao.addUser(user); + } + + @Override + public List getAllUsers() { + return userDao.getAllUsers(); + } + + @Override + public UserDto getUserDtoById(int userId) { + return userDao.getUserDtoById(userId); + } + + @Override + public String removeUserById(int userId) { + return userDao.removeUserById(userId); + } + + @Override + public UserDto UpdateUser(int userId, Map fields) { + return userDao.UpdateUser(userId, fields); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 318785cf..4201d54b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -11,6 +11,7 @@ logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG #--- # TODO Append connection to DB #--- + spring.config.activate.on-profile=ci,test spring.datasource.driverClassName=org.h2.Driver spring.datasource.url=jdbc:h2:mem:shareit diff --git a/suppressions.xml b/suppressions.xml new file mode 100644 index 00000000..b2c6822f --- /dev/null +++ b/suppressions.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file