diff --git a/src/main/java/com/example/demo/DemoApplication.java b/src/main/java/com/example/demo/DemoApplication.java index e91afe6..bbc1e89 100644 --- a/src/main/java/com/example/demo/DemoApplication.java +++ b/src/main/java/com/example/demo/DemoApplication.java @@ -1,5 +1,6 @@ package com.example.demo; +import java.time.Period; import java.util.Objects; import org.slf4j.Logger; @@ -12,8 +13,12 @@ import com.example.demo.ageRatings.model.AgeRatingEntity; import com.example.demo.ageRatings.service.AgeRatingService; import com.example.demo.movies.model.MovieEntity; import com.example.demo.movies.service.MovieService; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; import com.example.demo.types.model.TypeEntity; import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; @SpringBootApplication public class DemoApplication implements CommandLineRunner { @@ -22,11 +27,16 @@ public class DemoApplication implements CommandLineRunner { private final TypeService typeService; private final AgeRatingService ageRatingService; private final MovieService movieService; + private final UserService userService; + private final SubscriptionService subscriptionService; - public DemoApplication(TypeService typeService, AgeRatingService ageRatingService, MovieService movieService) { + public DemoApplication(TypeService typeService, AgeRatingService ageRatingService, MovieService movieService, + UserService userService, SubscriptionService subscriptionService) { this.typeService = typeService; this.ageRatingService = ageRatingService; this.movieService = movieService; + this.userService = userService; + this.subscriptionService = subscriptionService; } public static void main(String[] args) { @@ -37,35 +47,42 @@ public class DemoApplication implements CommandLineRunner { public void run(String... args) throws Exception { if (args.length > 0 && Objects.equals("--populate", args[0])) { log.info("Create default types values"); - final var type1 = typeService.create(new TypeEntity(null, "Фильм")); - final var type2 = typeService.create(new TypeEntity(null, "Сериал")); + final var type1 = typeService.create(new TypeEntity("Фильм")); + final var type2 = typeService.create(new TypeEntity("Сериал")); log.info("Create default ageRatings values"); - // final var ageRating1 = ageRatingService.create(new AgeRatingEntity(null, - // "0+")); - // final var ageRating2 = ageRatingService.create(new AgeRatingEntity(null, - // "6+")); - // final var ageRating3 = ageRatingService.create(new AgeRatingEntity(null, - // "12+")); - final var ageRating4 = ageRatingService.create(new AgeRatingEntity(null, "16+")); - final var ageRating5 = ageRatingService.create(new AgeRatingEntity(null, "18+")); + // final var ageRating1 = ageRatingService.create(new AgeRatingEntity("0+")); + // final var ageRating2 = ageRatingService.create(new AgeRatingEntity("6+")); + // final var ageRating3 = ageRatingService.create(new AgeRatingEntity("12+")); + final var ageRating4 = ageRatingService.create(new AgeRatingEntity("16+")); + final var ageRating5 = ageRatingService.create(new AgeRatingEntity("18+")); log.info("Create default movies values"); - movieService.create(new MovieEntity(null, "Бойцовский клуб", type1, false, + movieService.create(new MovieEntity("Бойцовский клуб", type1, false, "src/assets/main-page/posters/PeakyBlinders.png", "Сотрудник страховой компании страдает хронической бессонницей и отчаянно пытается вырваться из мучительно скучной жизни...", 1999, "США", "Интриги. Хаос. Мыло", "Дэвид Финчер", ageRating5, "https://www.youtube.com/embed/dfeUzm6KF4g?si=U2D-2WNsJzkcxlBX")); - movieService.create(new MovieEntity(null, "Американский психопат", type1, true, + movieService.create(new MovieEntity("Американский психопат", type1, true, "src/assets/main-page/posters/AmericanPsycho.jpg", "Днем он ничем не отличается от окружающих, и в толпе вы не обратите на него внимания...", 2000, "США, Канада", "Респектабельная внешность дьявола", "Мэри Хэррон", ageRating5, "https://www.youtube.com/embed/PpUWjff_OcM?si=e7tSm4V6qYkz406B")); - movieService.create(new MovieEntity(null, "Острые козырьки", type2, false, + movieService.create(new MovieEntity("Острые козырьки", type2, false, "src/assets/main-page/posters/PeakyBlinders.png", "Сериал рассказывает историю восхождения одной ирландской семьи...", 2013, "Великобритания", "The streets are theirs", "Энтони Бирн, Колм МакКарти", ageRating4, "https://www.youtube.com/embed/7cOfn_sjlXM?si=fxNoZjlUon1SZd70")); + + log.info("Create default subscription values"); + subscriptionService.create(new SubscriptionEntity("Подписка 1", Period.of(0, 0, 7), 199.99)); + subscriptionService.create(new SubscriptionEntity("Подписка 2", Period.of(0, 1, 0), 299.99)); + subscriptionService.create(new SubscriptionEntity("Подписка 3", Period.of(0, 6, 0), 799.99)); + + log.info("Create default users values"); + userService.create(new UserEntity("email01@mail.com", "12345")); + userService.create(new UserEntity("email002@mail.com", "admin")); + userService.create(new UserEntity("email0003@mail.com", "qwerty")); } } } diff --git a/src/main/java/com/example/demo/ageRatings/api/AgeRatingController.java b/src/main/java/com/example/demo/ageRatings/api/AgeRatingController.java index 7474601..39e684a 100644 --- a/src/main/java/com/example/demo/ageRatings/api/AgeRatingController.java +++ b/src/main/java/com/example/demo/ageRatings/api/AgeRatingController.java @@ -53,7 +53,7 @@ public class AgeRatingController { } @PutMapping("/{id}") - public AgeRatingDto update(@PathVariable(name = "id") Long id, @RequestBody AgeRatingDto dto) { + public AgeRatingDto update(@PathVariable(name = "id") Long id, @RequestBody @Valid AgeRatingDto dto) { return toDto(ageRatingService.update(id, toEntity(dto))); } diff --git a/src/main/java/com/example/demo/ageRatings/api/AgeRatingDto.java b/src/main/java/com/example/demo/ageRatings/api/AgeRatingDto.java index 6f46e6c..8c8409f 100644 --- a/src/main/java/com/example/demo/ageRatings/api/AgeRatingDto.java +++ b/src/main/java/com/example/demo/ageRatings/api/AgeRatingDto.java @@ -3,13 +3,16 @@ package com.example.demo.ageRatings.api; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; public class AgeRatingDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Long id; + @NotBlank + @Size(min = 2, max = 4) private String name; - @JsonProperty(access = JsonProperty.Access.READ_ONLY) public Long getId() { return id; } diff --git a/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java b/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java index 9f36ae2..c02f2f9 100644 --- a/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java +++ b/src/main/java/com/example/demo/ageRatings/service/AgeRatingService.java @@ -8,6 +8,7 @@ import org.springframework.transaction.annotation.Transactional; import com.example.demo.ageRatings.model.AgeRatingEntity; import com.example.demo.ageRatings.repository.AgeRatingRepository; +import com.example.demo.core.error.AlreadyExistsException; import com.example.demo.core.error.NotFoundException; @Service @@ -18,6 +19,12 @@ public class AgeRatingService { this.repository = repository; } + private void checkNameUnique(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new AlreadyExistsException(); + } + } + @Transactional(readOnly = true) public List getAll() { return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); @@ -35,6 +42,7 @@ public class AgeRatingService { throw new IllegalArgumentException("Entity is null"); } + checkNameUnique(entity.getName()); return repository.save(entity); } @@ -42,6 +50,7 @@ public class AgeRatingService { public AgeRatingEntity update(Long id, AgeRatingEntity entity) { final AgeRatingEntity existsEntity = get(id); + checkNameUnique(entity.getName()); existsEntity.setName(entity.getName()); return repository.save(existsEntity); } @@ -54,6 +63,7 @@ public class AgeRatingService { return existsEntity; } + @Transactional public void deleteAll() { repository.deleteAll(); } diff --git a/src/main/java/com/example/demo/core/api/PageDto.java b/src/main/java/com/example/demo/core/api/PageDto.java new file mode 100644 index 0000000..4cae429 --- /dev/null +++ b/src/main/java/com/example/demo/core/api/PageDto.java @@ -0,0 +1,97 @@ +package com.example.demo.core.api; + +import java.util.ArrayList; +import java.util.List; + +public class PageDto { + private List items = new ArrayList<>(); + private int itemsCount; + private int currentPage; + private int currentSize; + private int totalPages; + private long totalItems; + private boolean isFirst; + private boolean isLast; + private boolean hasNext; + private boolean hasPrevious; + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public int getItemsCount() { + return itemsCount; + } + + public void setItemsCount(int itemsCount) { + this.itemsCount = itemsCount; + } + + public int getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(int currentPage) { + this.currentPage = currentPage; + } + + public int getCurrentSize() { + return currentSize; + } + + public void setCurrentSize(int currentSize) { + this.currentSize = currentSize; + } + + public int getTotalPages() { + return totalPages; + } + + public void setTotalPages(int totalPages) { + this.totalPages = totalPages; + } + + public long getTotalItems() { + return totalItems; + } + + public void setTotalItems(long totalItems) { + this.totalItems = totalItems; + } + + public boolean isFirst() { + return isFirst; + } + + public void setFirst(boolean isFirst) { + this.isFirst = isFirst; + } + + public boolean isLast() { + return isLast; + } + + public void setLast(boolean isLast) { + this.isLast = isLast; + } + + public boolean isHasNext() { + return hasNext; + } + + public void setHasNext(boolean hasNext) { + this.hasNext = hasNext; + } + + public boolean isHasPrevious() { + return hasPrevious; + } + + public void setHasPrevious(boolean hasPrevious) { + this.hasPrevious = hasPrevious; + } +} diff --git a/src/main/java/com/example/demo/core/api/PageDtoMapper.java b/src/main/java/com/example/demo/core/api/PageDtoMapper.java new file mode 100644 index 0000000..e8d3dd0 --- /dev/null +++ b/src/main/java/com/example/demo/core/api/PageDtoMapper.java @@ -0,0 +1,25 @@ +package com.example.demo.core.api; + +import java.util.function.Function; + +import org.springframework.data.domain.Page; + +public class PageDtoMapper { + private PageDtoMapper() { + } + + public static PageDto toDto(Page page, Function mapper) { + final PageDto dto = new PageDto<>(); + dto.setItems(page.getContent().stream().map(mapper::apply).toList()); + dto.setItemsCount(page.getNumberOfElements()); + dto.setCurrentPage(page.getNumber()); + dto.setCurrentSize(page.getSize()); + dto.setTotalPages(page.getTotalPages()); + dto.setTotalItems(page.getTotalElements()); + dto.setFirst(page.isFirst()); + dto.setLast(page.isLast()); + dto.setHasNext(page.hasNext()); + dto.setHasPrevious(page.hasPrevious()); + return dto; + } +} diff --git a/src/main/java/com/example/demo/core/configuration/Constants.java b/src/main/java/com/example/demo/core/configuration/Constants.java index ec97a88..42de69f 100644 --- a/src/main/java/com/example/demo/core/configuration/Constants.java +++ b/src/main/java/com/example/demo/core/configuration/Constants.java @@ -3,6 +3,7 @@ package com.example.demo.core.configuration; public class Constants { public static final String SEQUENCE_NAME = "hibernate_sequence"; public static final String API_URL = "/api/1.0"; + public static final String DEFAULT_PAGE_SIZE = "5"; private Constants() { } diff --git a/src/main/java/com/example/demo/core/error/AlreadyExistsException.java b/src/main/java/com/example/demo/core/error/AlreadyExistsException.java new file mode 100644 index 0000000..64e0317 --- /dev/null +++ b/src/main/java/com/example/demo/core/error/AlreadyExistsException.java @@ -0,0 +1,7 @@ +package com.example.demo.core.error; + +public class AlreadyExistsException extends RuntimeException { + public AlreadyExistsException() { + super("Entity already exists"); + } +} diff --git a/src/main/java/com/example/demo/movies/api/MovieController.java b/src/main/java/com/example/demo/movies/api/MovieController.java index c8b3727..2fb5036 100644 --- a/src/main/java/com/example/demo/movies/api/MovieController.java +++ b/src/main/java/com/example/demo/movies/api/MovieController.java @@ -1,7 +1,5 @@ package com.example.demo.movies.api; -import java.util.List; - import org.modelmapper.ModelMapper; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -13,6 +11,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.example.demo.ageRatings.service.AgeRatingService; +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; import com.example.demo.core.configuration.Constants; import com.example.demo.movies.model.MovieEntity; import com.example.demo.movies.service.MovieService; @@ -23,13 +24,16 @@ import jakarta.validation.Valid; @RestController @RequestMapping(Constants.API_URL + "/movie") public class MovieController { - private final MovieService itemService; + private final MovieService movieService; private final TypeService typeService; + private final AgeRatingService ageRatingService; private final ModelMapper modelMapper; - public MovieController(MovieService itemService, TypeService typeService, ModelMapper modelMapper) { - this.itemService = itemService; + public MovieController(MovieService movieService, TypeService typeService, AgeRatingService ageRatingService, + ModelMapper modelMapper) { + this.movieService = movieService; this.typeService = typeService; + this.ageRatingService = ageRatingService; this.modelMapper = modelMapper; } @@ -39,32 +43,44 @@ public class MovieController { private MovieEntity toEntity(MovieDto dto) { final MovieEntity entity = modelMapper.map(dto, MovieEntity.class); + entity.setType(typeService.get(dto.getTypeId())); + entity.setAgeRating(ageRatingService.get(dto.getAgeRatingId())); + return entity; } @GetMapping - public List getAll(@RequestParam(name = "typeId", defaultValue = "0") Long typeId) { - return itemService.getAll(typeId).stream().map(this::toDto).toList(); + public PageDto getAll( + @RequestParam(name = "typeId", defaultValue = "0") Long typeId, + @RequestParam(name = "ageRatingId", defaultValue = "0") Long ageRatingId, + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(movieService.getAll(typeId, ageRatingId, page, size), this::toDto); } @GetMapping("/{id}") public MovieDto get(@PathVariable(name = "id") Long id) { - return toDto(itemService.get(id)); + return toDto(movieService.get(id)); } @PostMapping public MovieDto create(@RequestBody @Valid MovieDto dto) { - return toDto(itemService.create(toEntity(dto))); + return toDto(movieService.create(toEntity(dto))); } @PutMapping("/{id}") - public MovieDto update(@PathVariable(name = "id") Long id, @RequestBody MovieDto dto) { - return toDto(itemService.update(id, toEntity(dto))); + public MovieDto update(@PathVariable(name = "id") Long id, @RequestBody @Valid MovieDto dto) { + return toDto(movieService.update(id, toEntity(dto))); } @DeleteMapping("/{id}") public MovieDto delete(@PathVariable(name = "id") Long id) { - return toDto(itemService.delete(id)); + return toDto(movieService.delete(id)); + } + + @GetMapping("/{id}/rating") + public Double getAverageRating(@PathVariable(name = "id") Long id) { + return movieService.getAverageRating(id); } } diff --git a/src/main/java/com/example/demo/movies/api/MovieDto.java b/src/main/java/com/example/demo/movies/api/MovieDto.java index 5ddfd0c..7c15462 100644 --- a/src/main/java/com/example/demo/movies/api/MovieDto.java +++ b/src/main/java/com/example/demo/movies/api/MovieDto.java @@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; public class MovieDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Long id; @NotNull diff --git a/src/main/java/com/example/demo/movies/model/MovieEntity.java b/src/main/java/com/example/demo/movies/model/MovieEntity.java index 0765e32..67edfab 100644 --- a/src/main/java/com/example/demo/movies/model/MovieEntity.java +++ b/src/main/java/com/example/demo/movies/model/MovieEntity.java @@ -8,7 +8,6 @@ import com.example.demo.types.model.TypeEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; @@ -24,37 +23,37 @@ public class MovieEntity extends BaseEntity { @JoinColumn(name = "typeId", nullable = false) private TypeEntity type; - @ManyToOne - @JoinColumn(name = "ageRatingId", nullable = false) - private AgeRatingEntity ageRating; - @Column(nullable = false) private boolean requiresSubscription; @Column(length = 50) private String poster; - @Column(nullable = false, unique = true, length = 100) + @Column(nullable = false, length = 200) private String description; private Integer releaseDate; - @Column(nullable = false, unique = true, length = 20) + @Column(nullable = false, length = 20) private String country; - @Column(nullable = false, unique = true, length = 30) + @Column(nullable = false, length = 50) private String tagline; - @Column(nullable = false, unique = true, length = 30) + @Column(nullable = false, length = 50) private String director; - @Column(nullable = false, unique = true, length = 100) + @ManyToOne + @JoinColumn(name = "ageRatingId", nullable = false) + private AgeRatingEntity ageRating; + + @Column(nullable = false, length = 100) private String video; public MovieEntity() { } - public MovieEntity(Long id, String title, TypeEntity type, boolean requiresSubscription, String poster, + public MovieEntity(String title, TypeEntity type, boolean requiresSubscription, String poster, String description, Integer releaseDate, String country, String tagline, String director, AgeRatingEntity ageRating, String video) { this.title = title; diff --git a/src/main/java/com/example/demo/movies/repository/MovieRepository.java b/src/main/java/com/example/demo/movies/repository/MovieRepository.java index 10db0ab..7145a6f 100644 --- a/src/main/java/com/example/demo/movies/repository/MovieRepository.java +++ b/src/main/java/com/example/demo/movies/repository/MovieRepository.java @@ -1,10 +1,32 @@ package com.example.demo.movies.repository; -import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; -import com.example.demo.core.repository.MapRepository; import com.example.demo.movies.model.MovieEntity; -@Repository -public class MovieRepository extends MapRepository { +public interface MovieRepository + extends CrudRepository, PagingAndSortingRepository { + Optional findByTitleIgnoreCase(String title); + + List findByTypeId(Long typeId); + + Page findByTypeId(long typeId, Pageable pageable); + + List findByAgeRatingId(Long ageRatingId); + + Page findByAgeRatingId(Long ageRatingId, Pageable pageable); + + List findByTypeIdAndAgeRatingId(Long typeId, Long ageRatingId); + + Page findByTypeIdAndAgeRatingId(Long typeId, Long ageRatingId, Pageable pageable); + + @Query("SELECT CAST(SUM(r.rate) as double) / COUNT(*) as average_rate FROM MovieEntity as m JOIN ReviewEntity as r ON r.movie.id=m.id WHERE m.id=?1") + Double getAverageRating(long id); } diff --git a/src/main/java/com/example/demo/movies/service/MovieService.java b/src/main/java/com/example/demo/movies/service/MovieService.java index 9c497a9..10daa5f 100644 --- a/src/main/java/com/example/demo/movies/service/MovieService.java +++ b/src/main/java/com/example/demo/movies/service/MovieService.java @@ -1,11 +1,16 @@ package com.example.demo.movies.service; +import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.stream.StreamSupport; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.example.demo.core.error.AlreadyExistsException; import com.example.demo.core.error.NotFoundException; import com.example.demo.movies.model.MovieEntity; import com.example.demo.movies.repository.MovieRepository; @@ -18,26 +23,60 @@ public class MovieService { this.repository = repository; } - public List getAll(Long typeId) { - if (Objects.equals(typeId, 0L)) { - return repository.getAll(); + private void checkTitleUnique(String title) { + if (repository.findByTitleIgnoreCase(title).isPresent()) { + throw new AlreadyExistsException(); } - return repository.getAll().stream() - .filter(item -> item.getType().getId().equals(typeId)) - .toList(); } + @Transactional(readOnly = true) + public List getAll(Long typeId, Long ageRatingId) { + if (typeId <= 0L && ageRatingId <= 0L) { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } else if (typeId <= 0L) { + return repository.findByAgeRatingId(ageRatingId); + } else if (ageRatingId <= 0L) { + return repository.findByTypeId(typeId); + } + + return repository.findByTypeIdAndAgeRatingId(typeId, ageRatingId); + } + + @Transactional(readOnly = true) + public Page getAll(Long typeId, Long ageRatingId, int page, int size) { + final Pageable pageRequest = PageRequest.of(page, size); + + if (typeId <= 0L && ageRatingId <= 0L) { + return repository.findAll(pageRequest); + } else if (typeId <= 0L) { + return repository.findByAgeRatingId(ageRatingId, pageRequest); + } else if (ageRatingId <= 0L) { + return repository.findByTypeId(typeId, pageRequest); + } + + return repository.findByTypeIdAndAgeRatingId(typeId, ageRatingId, pageRequest); + } + + @Transactional(readOnly = true) public MovieEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(MovieEntity.class, id)); } + @Transactional public MovieEntity create(MovieEntity entity) { - return repository.create(entity); + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + + checkTitleUnique(entity.getTitle()); + return repository.save(entity); } + @Transactional public MovieEntity update(Long id, MovieEntity entity) { final MovieEntity existsEntity = get(id); + existsEntity.setTitle(entity.getTitle()); existsEntity.setType(entity.getType()); existsEntity.setRequiresSubscription(entity.getRequiresSubscription()); @@ -49,15 +88,39 @@ public class MovieService { existsEntity.setDirector(entity.getDirector()); existsEntity.setAgeRating(entity.getAgeRating()); existsEntity.setVideo(entity.getVideo()); - return repository.update(existsEntity); + + return repository.save(existsEntity); } + @Transactional public MovieEntity delete(Long id) { final MovieEntity existsEntity = get(id); - return repository.delete(existsEntity); + repository.delete(existsEntity); + + return existsEntity; } + @Transactional public void deleteAll() { repository.deleteAll(); } + + @Transactional + public Double getAverageRating(Long id) { + final MovieEntity existsEntity = get(id); + Double rating = repository.getAverageRating(existsEntity.getId()); + + if (rating == null) { + return 0D; + } + return rating; + } + + @Transactional + public List getItemsByIds(List ids) { + List items = new ArrayList<>(); + repository.findAllById(ids).forEach(item -> items.add(item)); + + return items; + } } diff --git a/src/main/java/com/example/demo/reviews/api/ReviewController.java b/src/main/java/com/example/demo/reviews/api/ReviewController.java new file mode 100644 index 0000000..e8bc5fe --- /dev/null +++ b/src/main/java/com/example/demo/reviews/api/ReviewController.java @@ -0,0 +1,81 @@ +package com.example.demo.reviews.api; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; +import com.example.demo.core.configuration.Constants; +import com.example.demo.reviews.model.ReviewEntity; +import com.example.demo.reviews.service.ReviewService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/review") +public class ReviewController { + private final ReviewService reviewService; + private final ModelMapper modelMapper; + + public ReviewController(ReviewService reviewService, + ModelMapper modelMapper) { + this.reviewService = reviewService; + this.modelMapper = modelMapper; + } + + private ReviewDto toDto(ReviewEntity entity) { + return modelMapper.map(entity, ReviewDto.class); + } + + private ReviewEntity toEntity(ReviewDto dto) { + final ReviewEntity entity = modelMapper.map(dto, ReviewEntity.class); + return entity; + } + + @GetMapping + public PageDto getAll( + @RequestParam(name = "userId") Long userId, + @RequestParam(name = "movieId") Long movieId, + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(reviewService.getAll(movieId, userId, page, size), this::toDto); + } + + @GetMapping("/{id}") + public ReviewDto get( + @PathVariable(name = "id") Long id, + @RequestParam(name = "userId") Long userId) { + return toDto(reviewService.get(userId, id)); + } + + @PostMapping + public ReviewDto create( + @RequestParam(name = "userId") Long userId, + @RequestParam(name = "movieId") Long movieId, + @RequestBody @Valid ReviewDto dto) { + return toDto(reviewService.create(userId, movieId, toEntity(dto))); + } + + @PutMapping("/{id}") + public ReviewDto update( + @PathVariable(name = "id") Long id, + @RequestParam(name = "userId") Long userId, + @RequestBody ReviewDto dto) { + return toDto(reviewService.update(userId, id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public ReviewDto delete( + @PathVariable(name = "userId") Long userId, + @PathVariable(name = "id") Long id) { + return toDto(reviewService.delete(userId, id)); + } +} diff --git a/src/main/java/com/example/demo/reviews/api/ReviewDto.java b/src/main/java/com/example/demo/reviews/api/ReviewDto.java new file mode 100644 index 0000000..f312983 --- /dev/null +++ b/src/main/java/com/example/demo/reviews/api/ReviewDto.java @@ -0,0 +1,43 @@ +package com.example.demo.reviews.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +public class ReviewDto { + private Long id; + + private String description; + + @NotNull + @Min(1) + @Max(5) + private Integer rate; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getRate() { + return rate; + } + + public void setRate(Integer rate) { + this.rate = rate; + } +} diff --git a/src/main/java/com/example/demo/reviews/model/ReviewEntity.java b/src/main/java/com/example/demo/reviews/model/ReviewEntity.java new file mode 100644 index 0000000..6bf2ff6 --- /dev/null +++ b/src/main/java/com/example/demo/reviews/model/ReviewEntity.java @@ -0,0 +1,87 @@ +package com.example.demo.reviews.model; + +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.movies.model.MovieEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "reviews") +public class ReviewEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "userId", nullable = false) + private UserEntity user; + @ManyToOne + @JoinColumn(name = "movieId", nullable = false) + private MovieEntity movie; + @Column(nullable = false) + private String description; + @Column(nullable = false) + private Integer rate; + + public ReviewEntity() { + } + + public ReviewEntity(String description, Integer rate) { + this.description = description; + this.rate = rate; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + } + + public MovieEntity getMovie() { + return movie; + } + + public void setMovie(MovieEntity movie) { + this.movie = movie; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getRate() { + return rate; + } + + public void setRate(Integer rate) { + this.rate = rate; + } + + @Override + public int hashCode() { + return Objects.hash(id, user, movie, description, rate); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + final ReviewEntity other = (ReviewEntity) obj; + return Objects.equals(other.getId(), id) + && Objects.equals(other.getUser(), user) + && Objects.equals(other.getMovie(), movie) + && Objects.equals(other.getDescription(), description) + && Objects.equals(other.getRate(), rate); + } +} diff --git a/src/main/java/com/example/demo/reviews/repository/ReviewRepository.java b/src/main/java/com/example/demo/reviews/repository/ReviewRepository.java new file mode 100644 index 0000000..5d90123 --- /dev/null +++ b/src/main/java/com/example/demo/reviews/repository/ReviewRepository.java @@ -0,0 +1,30 @@ +package com.example.demo.reviews.repository; + +import com.example.demo.reviews.model.ReviewEntity; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; + +public interface ReviewRepository + extends CrudRepository, PagingAndSortingRepository { + List findByUserId(long userId); + + Page findByUserId(long userId, Pageable pageable); + + List findByMovieId(long movieId); + + Page findByMovieId(long movieId, Pageable pageable); + + Optional findOneByUserIdAndId(long userId, long id); + + Optional findOneByUserIdAndMovieId(long userId, long movieId); + + List findByUserIdAndMovieId(long userId, long movieId); + + Page findByUserIdAndMovieId(long userId, long movieId, Pageable pageable); +} diff --git a/src/main/java/com/example/demo/reviews/service/ReviewService.java b/src/main/java/com/example/demo/reviews/service/ReviewService.java new file mode 100644 index 0000000..96d472b --- /dev/null +++ b/src/main/java/com/example/demo/reviews/service/ReviewService.java @@ -0,0 +1,110 @@ +package com.example.demo.reviews.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.AlreadyExistsException; +import com.example.demo.core.error.NotFoundException; +import com.example.demo.movies.model.MovieEntity; +import com.example.demo.movies.service.MovieService; +import com.example.demo.reviews.model.ReviewEntity; +import com.example.demo.reviews.repository.ReviewRepository; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@Service +public class ReviewService { + private final ReviewRepository repository; + private final UserService userService; + private final MovieService movieService; + + public ReviewService(ReviewRepository repository, UserService userService, MovieService movieService) { + this.repository = repository; + this.userService = userService; + this.movieService = movieService; + } + + private void checkReview(Long userId, Long movieId) { + if (repository.findByUserIdAndMovieId(userId, movieId).size() == 1) { + throw new AlreadyExistsException(); + } + } + + @Transactional(readOnly = true) + public List getAll(Long movieId, Long userId) { + if (movieId <= 0L && userId <= 0L) { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + if (userId <= 0L && movieId > 0L) { + return repository.findByMovieId(movieId); + } + if (movieId <= 0L && userId > 0L) { + return repository.findByUserId(userId); + } else { + return repository.findByUserIdAndMovieId(userId, movieId); + } + } + + @Transactional(readOnly = true) + public Page getAll(Long movieId, Long userId, int page, int size) { + final Pageable pageRequest = PageRequest.of(page, size); + + if (movieId <= 0L && userId <= 0L) { + return repository.findAll(pageRequest); + } + if (userId <= 0L && movieId > 0L) { + return repository.findByMovieId(movieId, pageRequest); + } + if (movieId <= 0L && userId > 0L) { + return repository.findByUserId(userId, pageRequest); + } else { + return repository.findByUserIdAndMovieId(userId, movieId, pageRequest); + } + } + + @Transactional(readOnly = true) + public ReviewEntity get(Long userId, Long id) { + userService.get(userId); + return repository.findOneByUserIdAndId(userId, id) + .orElseThrow(() -> new NotFoundException(ReviewEntity.class, id)); + } + + @Transactional + public ReviewEntity create(long userId, long movieId, ReviewEntity entity) { + checkReview(userId, movieId); + + final UserEntity existsUser = userService.get(userId); + final MovieEntity existsmovie = movieService.get(movieId); + + entity.setMovie(existsmovie); + entity.setUser(existsUser); + return repository.save(entity); + } + + @Transactional + public ReviewEntity update(Long userId, Long id, ReviewEntity entity) { + final ReviewEntity existsEntity = get(userId, id); + + existsEntity.setDescription(entity.getDescription()); + existsEntity.setRate(entity.getRate()); + return repository.save(existsEntity); + } + + @Transactional + public ReviewEntity delete(Long userId, Long id) { + final ReviewEntity existsEntity = get(userId, id); + + repository.delete(existsEntity); + return existsEntity; + } + + public void deleteAll() { + repository.deleteAll(); + } +} diff --git a/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java b/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java new file mode 100644 index 0000000..1a7dd9b --- /dev/null +++ b/src/main/java/com/example/demo/subscriptions/api/SubscriptionController.java @@ -0,0 +1,68 @@ +package com.example.demo.subscriptions.api; + +import java.util.List; + +import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.demo.core.configuration.Constants; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping(Constants.API_URL + "/subsciption") +public class SubscriptionController { + private final SubscriptionService subscriptionService; + private final ModelMapper modelMapper; + + public SubscriptionController(SubscriptionService subscriptionService, ModelMapper modelMapper) { + this.subscriptionService = subscriptionService; + this.modelMapper = modelMapper; + } + + private SubscriptionDto toDto(SubscriptionEntity entity) { + return modelMapper.map(entity, SubscriptionDto.class); + } + + private SubscriptionEntity toEntity(SubscriptionDto dto) { + return modelMapper.map(dto, SubscriptionEntity.class); + } + + @GetMapping + public List getAll() { + return subscriptionService.getAll().stream() + .map(this::toDto) + .toList(); + } + + @GetMapping("/{id}") + public SubscriptionDto get(@PathVariable(name = "id") Long id) { + return toDto(subscriptionService.get(id)); + } + + @PostMapping + public SubscriptionDto create(@RequestBody @Valid SubscriptionDto dto) { + return toDto(subscriptionService.create(toEntity(dto))); + } + + @PutMapping("/{id}") + public SubscriptionDto update( + @PathVariable(name = "id") Long id, + @RequestBody @Valid SubscriptionDto dto) { + return toDto(subscriptionService.update(id, toEntity(dto))); + } + + @DeleteMapping("/{id}") + public SubscriptionDto delete(@PathVariable(name = "id") Long id) { + return toDto(subscriptionService.delete(id)); + } +} diff --git a/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java b/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java new file mode 100644 index 0000000..e6bbd53 --- /dev/null +++ b/src/main/java/com/example/demo/subscriptions/api/SubscriptionDto.java @@ -0,0 +1,55 @@ +package com.example.demo.subscriptions.api; + +import java.time.Period; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class SubscriptionDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long id; + + @NotBlank + @Size(min = 5, max = 50) + private String name; + + @NotBlank + private Period duration; + + @NotBlank + private Double price; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Period getDuration() { + return duration; + } + + public void setDuration(Period duration) { + this.duration = duration; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } +} diff --git a/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java b/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java new file mode 100644 index 0000000..9bdfdf1 --- /dev/null +++ b/src/main/java/com/example/demo/subscriptions/model/SubscriptionEntity.java @@ -0,0 +1,74 @@ +package com.example.demo.subscriptions.model; + +import java.time.Period; +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = "subscriptions") +public class SubscriptionEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 50) + private String name; + + @Column(nullable = false, unique = true) + private Period duration; + + @Column(nullable = false) + private Double price; + + public SubscriptionEntity() { + } + + public SubscriptionEntity(String name, Period duration, Double price) { + this.name = name; + this.duration = duration; + this.price = price; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Period getDuration() { + return duration; + } + + public void setDuration(Period duration) { + this.duration = duration; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, duration, price); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + SubscriptionEntity other = (SubscriptionEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(name, other.name) + && Objects.equals(duration, other.duration) + && Objects.equals(price, other.price); + } +} diff --git a/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java b/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java new file mode 100644 index 0000000..0ce4dc9 --- /dev/null +++ b/src/main/java/com/example/demo/subscriptions/repository/SubscriptionRepository.java @@ -0,0 +1,11 @@ +package com.example.demo.subscriptions.repository; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +import com.example.demo.subscriptions.model.SubscriptionEntity; + +public interface SubscriptionRepository extends CrudRepository { + Optional findByNameIgnoreCase(String name); +} diff --git a/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java b/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java new file mode 100644 index 0000000..883c41e --- /dev/null +++ b/src/main/java/com/example/demo/subscriptions/service/SubscriptionService.java @@ -0,0 +1,70 @@ +package com.example.demo.subscriptions.service; + +import java.util.List; +import java.util.stream.StreamSupport; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.core.error.NotFoundException; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.repository.SubscriptionRepository; + +@Service +public class SubscriptionService { + private final SubscriptionRepository repository; + + public SubscriptionService(SubscriptionRepository repository) { + this.repository = repository; + } + + private void checkName(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new IllegalArgumentException( + String.format("Subscription with name %s is already exists", name)); + } + } + + @Transactional(readOnly = true) + public List getAll() { + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); + } + + @Transactional(readOnly = true) + public SubscriptionEntity get(long id) { + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(SubscriptionEntity.class, id)); + } + + @Transactional + public SubscriptionEntity create(SubscriptionEntity entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + checkName(entity.getName()); + return repository.save(entity); + } + + @Transactional + public SubscriptionEntity update(Long id, SubscriptionEntity entity) { + final SubscriptionEntity existsEntity = get(id); + checkName(entity.getName()); + + existsEntity.setName(entity.getName()); + existsEntity.setDuration(entity.getDuration()); + existsEntity.setPrice(existsEntity.getPrice()); + return repository.save(existsEntity); + } + + @Transactional + public SubscriptionEntity delete(Long id) { + final SubscriptionEntity existsEntity = get(id); + repository.delete(existsEntity); + return existsEntity; + } + + @Transactional + public void deleteAll() { + repository.deleteAll(); + } +} diff --git a/src/main/java/com/example/demo/types/api/TypeController.java b/src/main/java/com/example/demo/types/api/TypeController.java index 81f07cf..d9785d8 100644 --- a/src/main/java/com/example/demo/types/api/TypeController.java +++ b/src/main/java/com/example/demo/types/api/TypeController.java @@ -53,7 +53,7 @@ public class TypeController { } @PutMapping("/{id}") - public TypeDto update(@PathVariable(name = "id") Long id, @RequestBody TypeDto dto) { + public TypeDto update(@PathVariable(name = "id") Long id, @RequestBody @Valid TypeDto dto) { return toDto(typeService.update(id, toEntity(dto))); } diff --git a/src/main/java/com/example/demo/types/api/TypeDto.java b/src/main/java/com/example/demo/types/api/TypeDto.java index 3b2f3dd..01e7847 100644 --- a/src/main/java/com/example/demo/types/api/TypeDto.java +++ b/src/main/java/com/example/demo/types/api/TypeDto.java @@ -3,14 +3,16 @@ package com.example.demo.types.api; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; public class TypeDto { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Long id; - + @NotBlank + @Size(min = 3, max = 20) private String name; - @JsonProperty(access = JsonProperty.Access.READ_ONLY) public Long getId() { return id; } diff --git a/src/main/java/com/example/demo/types/service/TypeService.java b/src/main/java/com/example/demo/types/service/TypeService.java index bbfaefc..65a904d 100644 --- a/src/main/java/com/example/demo/types/service/TypeService.java +++ b/src/main/java/com/example/demo/types/service/TypeService.java @@ -6,6 +6,7 @@ import java.util.stream.StreamSupport; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.example.demo.core.error.AlreadyExistsException; import com.example.demo.core.error.NotFoundException; import com.example.demo.types.model.TypeEntity; import com.example.demo.types.repository.TypeRepository; @@ -18,6 +19,12 @@ public class TypeService { this.repository = repository; } + private void checkNameUnique(String name) { + if (repository.findByNameIgnoreCase(name).isPresent()) { + throw new AlreadyExistsException(); + } + } + @Transactional(readOnly = true) public List getAll() { return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); @@ -35,6 +42,7 @@ public class TypeService { throw new IllegalArgumentException("Entity is null"); } + checkNameUnique(entity.getName()); return repository.save(entity); } @@ -42,6 +50,7 @@ public class TypeService { public TypeEntity update(Long id, TypeEntity entity) { final TypeEntity existsEntity = get(id); + checkNameUnique(entity.getName()); existsEntity.setName(entity.getName()); return repository.save(existsEntity); } @@ -54,6 +63,7 @@ public class TypeService { return existsEntity; } + @Transactional public void deleteAll() { repository.deleteAll(); } diff --git a/src/main/java/com/example/demo/users/api/UserController.java b/src/main/java/com/example/demo/users/api/UserController.java index 10414e8..d1a02af 100644 --- a/src/main/java/com/example/demo/users/api/UserController.java +++ b/src/main/java/com/example/demo/users/api/UserController.java @@ -1,6 +1,8 @@ package com.example.demo.users.api; +import java.util.Date; import java.util.List; +import java.util.Objects; import org.modelmapper.ModelMapper; import org.springframework.web.bind.annotation.DeleteMapping; @@ -10,13 +12,19 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.example.demo.movies.api.MovieDto; +import com.example.demo.core.api.PageDto; +import com.example.demo.core.api.PageDtoMapper; import com.example.demo.core.configuration.Constants; import com.example.demo.movies.model.MovieEntity; import com.example.demo.movies.service.MovieService; import com.example.demo.users.model.UserEntity; import com.example.demo.users.service.UserService; +import com.example.demo.usersubscription.api.SubscriptionInfoDto; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; import jakarta.validation.Valid; @@ -25,13 +33,11 @@ import jakarta.validation.Valid; public class UserController { private final UserService userService; private final MovieService movieService; - private final ModelMapper modelMapper; public UserController(UserService userService, MovieService movieService, ModelMapper modelMapper) { this.userService = userService; this.movieService = movieService; - this.modelMapper = modelMapper; } @@ -39,13 +45,28 @@ public class UserController { return modelMapper.map(entity, UserDto.class); } + private SubscriptionInfoDto toSubscriptionDto(UserSubscriptionEntity entity) { + if (!Objects.isNull(entity)) { + return new SubscriptionInfoDto(entity.getExpirationDate().after(new Date()), entity.getActivationDate(), + entity.getExpirationDate()); + } else { + return new SubscriptionInfoDto(false, null, null); + } + } + + private MovieDto toMovieDto(MovieEntity entity) { + return modelMapper.map(entity, MovieDto.class); + } + private UserEntity toEntity(UserDto dto) { return modelMapper.map(dto, UserEntity.class); } @GetMapping - public List getAll() { - return userService.getAll().stream().map(this::toDto).toList(); + public PageDto getAll( + @RequestParam(name = "page", defaultValue = "0") int page, + @RequestParam(name = "size", defaultValue = Constants.DEFAULT_PAGE_SIZE) int size) { + return PageDtoMapper.toDto(userService.getAll(page, size), this::toDto); } @GetMapping("/{id}") @@ -68,12 +89,48 @@ public class UserController { return toDto(userService.delete(id)); } - @PutMapping("/{id}/movie/{movieId}") - public UserDto addMovie(@PathVariable(name = "id") Long id, @RequestBody UserDto dto, - @PathVariable(name = "movieId") Long movieId) { - MovieEntity movie = movieService.get(movieId); - userService.addMovie(id, movie.getId()); + @GetMapping("/{id}/subscription") + public SubscriptionInfoDto getActiveSubscription(@PathVariable(name = "id") Long id) { + return toSubscriptionDto(userService.getActiveSubscription(id)); + } - return toDto(userService.update(id, toEntity(dto))); + @PostMapping("/{id}/subscription/all") + public List getAllSubscriptions(@PathVariable(name = "id") Long id) { + return userService.getUserSubscriptions(id).stream().map(this::toSubscriptionDto).toList(); + } + + @PostMapping("/{id}/subscription") + public SubscriptionInfoDto activateSubscription( + @PathVariable(name = "id") Long id, + @RequestParam(name = "subscriptionId", defaultValue = "") Long subscriptionId) { + return toSubscriptionDto(userService.activateSubscription(id, subscriptionId)); + } + + @DeleteMapping("/{id}/subscription") + public SubscriptionInfoDto deactivateSubscription(@PathVariable(name = "id") Long id) { + return toSubscriptionDto(userService.deactivateSubscription(id)); + } + + @GetMapping("/{id}/watchLater") + public List getWatchLater(@PathVariable(name = "id") Long id) { + return userService.getWatchLater(id).stream().map(this::toMovieDto).toList(); + } + + @PostMapping("/{id}/watchLater") + public UserDto addToWatchLater(@PathVariable(name = "id") Long id, + @RequestParam(name = "movieId", defaultValue = "") Long movieId) { + MovieEntity movie = movieService.get(movieId); + UserEntity user = userService.addMovieToWatchLater(id, movie); + + return toDto(user); + } + + @DeleteMapping("/{id}/watchLater") + public UserDto deleteFromWatchLater(@PathVariable(name = "id") Long id, + @RequestParam(name = "movieId", defaultValue = "") Long movieId) { + MovieEntity movie = movieService.get(movieId); + UserEntity user = userService.deleteMovieFromWatchLater(id, movie); + + return toDto(user); } } diff --git a/src/main/java/com/example/demo/users/api/UserDto.java b/src/main/java/com/example/demo/users/api/UserDto.java index d93fe5f..d0eb4a8 100644 --- a/src/main/java/com/example/demo/users/api/UserDto.java +++ b/src/main/java/com/example/demo/users/api/UserDto.java @@ -1,29 +1,26 @@ package com.example.demo.users.api; -import java.util.Date; -import java.util.List; - import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; public class UserDto { + @JsonProperty(access = Access.READ_ONLY) private Long id; @NotNull @NotBlank + @Size(min = 5, max = 30) private String email; @NotNull @NotBlank + @Size(min = 5, max = 30) private String password; - private Date expirationDate; - - private List watchLater; - - @JsonProperty(access = JsonProperty.Access.READ_ONLY) public Long getId() { return id; } @@ -47,20 +44,4 @@ public class UserDto { public void setPassword(String password) { this.password = password; } - - public Date getExpirationDate() { - return expirationDate; - } - - public void setExpirationDate(Date expirationDate) { - this.expirationDate = expirationDate; - } - - public List getWatchLater() { - return watchLater; - } - - public void setWatchLater(List watchLater) { - this.watchLater = watchLater; - } } diff --git a/src/main/java/com/example/demo/users/model/UserEntity.java b/src/main/java/com/example/demo/users/model/UserEntity.java index 8c2bbb3..d5af8e7 100644 --- a/src/main/java/com/example/demo/users/model/UserEntity.java +++ b/src/main/java/com/example/demo/users/model/UserEntity.java @@ -1,28 +1,49 @@ package com.example.demo.users.model; -import java.util.Date; -import java.util.List; +import java.util.HashSet; +import java.util.Collections; +import java.util.Set; import java.util.Objects; +import java.util.Optional; import com.example.demo.core.model.BaseEntity; +import com.example.demo.movies.model.MovieEntity; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") public class UserEntity extends BaseEntity { + @Column(nullable = false, unique = true, length = 30) private String email; + + @Column(nullable = false, length = 30) private String password; - private Date expirationDate; - private List watchLater; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + @OrderBy("id ASC") + private Set userSubscriptions = new HashSet<>(); + + @ManyToMany() + @JoinTable(name = "user_movie", joinColumns = @JoinColumn(name = "userId"), inverseJoinColumns = @JoinColumn(name = "movieId")) + @OrderBy("id ASC") + private Set watchLater = new HashSet<>(); public UserEntity() { - super(); } - public UserEntity(Long id, String email, String password, Date expirationDate, List watchLater) { - super(id); - + public UserEntity(String email, String password) { this.email = email; this.password = password; - this.expirationDate = expirationDate; - this.watchLater = watchLater; } public String getEmail() { @@ -41,25 +62,46 @@ public class UserEntity extends BaseEntity { this.password = password; } - public Date getExpirationDate() { - return expirationDate; + public Set getUserSubscriptions() { + return userSubscriptions; } - public void setExpirationDate(Date expirationDate) { - this.expirationDate = expirationDate; + public void addSubscription(UserSubscriptionEntity userSubscription) { + if (userSubscription.getUser() != this) { + userSubscription.setUser(this); + } + userSubscriptions.add(userSubscription); } - public List getWatchLater() { + public void deleteSubscription(UserSubscriptionEntity userSubscription) { + if (userSubscription.getUser() != this) { + return; + } + userSubscriptions.remove(userSubscription); + } + + public Set getWatchLater() { return watchLater; } - public void setWatchLater(List watchLater) { - this.watchLater = watchLater; + public void setWatchLater(Set watchLater) { + Set newWatchLater = new HashSet<>(); + newWatchLater.addAll(watchLater); + this.watchLater.clear(); + this.watchLater.addAll(Optional.ofNullable(newWatchLater).orElse(Collections.emptySet())); + } + + public void addMovieToWatchLater(MovieEntity movie) { + watchLater.add(movie); + } + + public void deleteMovieFromWatchLater(MovieEntity movie) { + watchLater.remove(movie); } @Override public int hashCode() { - return Objects.hash(id, email, password, expirationDate, watchLater); + return Objects.hash(id, email, password, userSubscriptions, watchLater); } @Override @@ -71,8 +113,6 @@ public class UserEntity extends BaseEntity { final UserEntity other = (UserEntity) obj; return Objects.equals(other.getId(), id) && Objects.equals(other.getEmail(), email) - && Objects.equals(other.getPassword(), password) - && Objects.equals(other.getExpirationDate(), expirationDate) - && Objects.equals(other.getWatchLater(), watchLater); + && Objects.equals(other.getPassword(), password); } } diff --git a/src/main/java/com/example/demo/users/repository/UserRepository.java b/src/main/java/com/example/demo/users/repository/UserRepository.java index fa4b654..0366745 100644 --- a/src/main/java/com/example/demo/users/repository/UserRepository.java +++ b/src/main/java/com/example/demo/users/repository/UserRepository.java @@ -1,10 +1,20 @@ package com.example.demo.users.repository; -import org.springframework.stereotype.Repository; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; -import com.example.demo.core.repository.MapRepository; import com.example.demo.users.model.UserEntity; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; -@Repository -public class UserRepository extends MapRepository { +public interface UserRepository extends CrudRepository, PagingAndSortingRepository { + Optional findByEmailIgnoreCase(String login); + + @Query("SELECT us FROM UserSubscriptionEntity us WHERE us.user.id = :userId AND us.expirationDate > :now") + List getActiveSubscriptions(@Param("userId") Long userId, @Param("now") Date now); } diff --git a/src/main/java/com/example/demo/users/service/UserService.java b/src/main/java/com/example/demo/users/service/UserService.java index cc0063d..7b58684 100644 --- a/src/main/java/com/example/demo/users/service/UserService.java +++ b/src/main/java/com/example/demo/users/service/UserService.java @@ -1,61 +1,167 @@ package com.example.demo.users.service; +import java.time.ZoneId; +import java.util.Date; import java.util.List; -import java.util.Optional; +import java.util.Objects; +import java.util.stream.StreamSupport; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.example.demo.users.model.UserEntity; import com.example.demo.users.repository.UserRepository; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; import com.example.demo.core.error.NotFoundException; import com.example.demo.movies.model.MovieEntity; -import com.example.demo.movies.service.MovieService; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; @Service public class UserService { private final UserRepository repository; - private final MovieService movieService; + private final SubscriptionService subscriptionService; - public UserService(UserRepository repository, MovieService movieService) { - this.movieService = movieService; + public UserService(UserRepository repository, SubscriptionService subscriptionService) { this.repository = repository; + this.subscriptionService = subscriptionService; } + private void checkEmail(String email) { + if (repository.findByEmailIgnoreCase(email).isPresent()) { + throw new IllegalArgumentException( + String.format("User with email %s is already exists", email)); + } + } + + @Transactional(readOnly = true) public List getAll() { - return repository.getAll(); + return StreamSupport.stream(repository.findAll().spliterator(), false).toList(); } + @Transactional(readOnly = true) + public Page getAll(int page, int size) { + final Pageable pageRequest = PageRequest.of(page, size); + return repository.findAll(pageRequest); + } + + @Transactional(readOnly = true) public UserEntity get(Long id) { - return Optional.ofNullable(repository.get(id)) - .orElseThrow(() -> new NotFoundException(id)); + return repository.findById(id) + .orElseThrow(() -> new NotFoundException(UserEntity.class, id)); } + @Transactional public UserEntity create(UserEntity entity) { - return repository.create(entity); + if (entity == null) { + throw new IllegalArgumentException("Entity is null"); + } + + checkEmail(entity.getEmail()); + repository.save(entity); + return repository.save(entity); } - public UserEntity update(Long id, UserEntity entity) { + @Transactional + public UserEntity update(long id, UserEntity entity) { final UserEntity existsEntity = get(id); + checkEmail(entity.getEmail()); + existsEntity.setEmail(entity.getEmail()); - return repository.update(existsEntity); + existsEntity.setPassword(entity.getPassword()); + return repository.save(existsEntity); } - public UserEntity delete(Long id) { + @Transactional + public UserEntity delete(long id) { final UserEntity existsEntity = get(id); - return repository.delete(existsEntity); + repository.delete(existsEntity); + + return existsEntity; } + @Transactional public void deleteAll() { repository.deleteAll(); } - public UserEntity addMovie(Long id, Long movieId) { + @Transactional(readOnly = true) + public List getUserSubscriptions(Long id) { + return get(id).getUserSubscriptions().stream().toList(); + } + + @Transactional(readOnly = true) + public UserSubscriptionEntity getActiveSubscription(Long id) { + List activeSubscriptions = repository.getActiveSubscriptions(id, new Date()); + + if (activeSubscriptions.size() != 1) { + return null; + } + return activeSubscriptions.get(0); + } + + @Transactional() + public UserSubscriptionEntity activateSubscription(Long id, Long subscriptionId) { + UserSubscriptionEntity activeSubscription = getActiveSubscription(id); + if (!Objects.isNull(activeSubscription)) { + return activeSubscription; + } + + final UserEntity existsUser = get(id); + final SubscriptionEntity subscription = subscriptionService.get(subscriptionId); + + Date now = new Date(); + Date expirationDate = Date + .from(now.toInstant().atZone(ZoneId.systemDefault()).plus(subscription.getDuration()).toInstant()); + + final UserSubscriptionEntity newUserSubscription = new UserSubscriptionEntity(existsUser, subscription, now, + expirationDate); + newUserSubscription.setUser(existsUser); + + repository.save(existsUser); + return newUserSubscription; + } + + @Transactional + public UserSubscriptionEntity deactivateSubscription(Long id) { + UserSubscriptionEntity subscriptionToDeactivate = getActiveSubscription(id); + if (Objects.isNull(subscriptionToDeactivate)) { + return null; + } + + final UserEntity existsUser = get(id); + subscriptionToDeactivate.setExpirationDate(new Date()); + repository.save(existsUser); + return subscriptionToDeactivate; + } + + @Transactional(readOnly = true) + public List getWatchLater(long id) { + return get(id).getWatchLater().stream().toList(); + } + + @Transactional + public UserEntity addMovieToWatchLater(Long id, MovieEntity movie) { final UserEntity existsEntity = get(id); - MovieEntity movie = movieService.get(movieId); + if (!existsEntity.getWatchLater().contains(movie)) { + existsEntity.addMovieToWatchLater(movie); + return repository.save(existsEntity); + } - if (!existsEntity.getWatchLater().contains(movie.getId())) { - existsEntity.getWatchLater().add(movie.getId()); + return existsEntity; + } + + @Transactional + public UserEntity deleteMovieFromWatchLater(Long id, MovieEntity movie) { + final UserEntity existsEntity = get(id); + + if (existsEntity.getWatchLater().contains(movie)) { + existsEntity.deleteMovieFromWatchLater(movie); + return repository.save(existsEntity); } return existsEntity; diff --git a/src/main/java/com/example/demo/usersubscription/api/SubscriptionInfoDto.java b/src/main/java/com/example/demo/usersubscription/api/SubscriptionInfoDto.java new file mode 100644 index 0000000..771bf2b --- /dev/null +++ b/src/main/java/com/example/demo/usersubscription/api/SubscriptionInfoDto.java @@ -0,0 +1,34 @@ +package com.example.demo.usersubscription.api; + +import java.util.Date; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class SubscriptionInfoDto { + @NotNull + @NotBlank + private boolean isActive; + + Date activationDate; + + Date expirationDate; + + public SubscriptionInfoDto(boolean isActive, Date activationDate, Date expirationDate) { + this.isActive = isActive; + this.activationDate = activationDate; + this.expirationDate = expirationDate; + } + + public boolean getIsActive() { + return isActive; + } + + public Date getActivationDate() { + return activationDate; + } + + public Date getExpirationDate() { + return expirationDate; + } +} diff --git a/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java b/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java new file mode 100644 index 0000000..30a136e --- /dev/null +++ b/src/main/java/com/example/demo/usersubscription/model/UserSubscriptionEntity.java @@ -0,0 +1,98 @@ +package com.example.demo.usersubscription.model; + +import java.util.Date; +import java.util.Objects; + +import com.example.demo.core.model.BaseEntity; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.users.model.UserEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users_subscriptions") +public class UserSubscriptionEntity extends BaseEntity { + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private UserEntity user; + + @ManyToOne + @JoinColumn(name = "subscription_id", nullable = false) + private SubscriptionEntity subscription; + + @Column(nullable = false) + private Date activationDate; + + @Column(nullable = false) + private Date expirationDate; + + public UserSubscriptionEntity() { + } + + public UserSubscriptionEntity(UserEntity user, SubscriptionEntity subscription, Date activationDate, + Date expirationDate) { + this.user = user; + this.subscription = subscription; + this.activationDate = activationDate; + this.expirationDate = expirationDate; + } + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + + if (!user.getUserSubscriptions().contains(this)) { + user.getUserSubscriptions().add(this); + } + } + + public SubscriptionEntity getSubscription() { + return subscription; + } + + public void setSubscription(SubscriptionEntity subscription) { + this.subscription = subscription; + } + + public Date getExpirationDate() { + return expirationDate; + } + + public void setExpirationDate(Date expirationDate) { + this.expirationDate = expirationDate; + } + + public Date getActivationDate() { + return activationDate; + } + + public void setActivationDate(Date activationDate) { + this.activationDate = activationDate; + } + + @Override + public int hashCode() { + return Objects.hash(id, user.getId(), subscription.getId(), activationDate, expirationDate); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + UserSubscriptionEntity other = (UserSubscriptionEntity) obj; + return Objects.equals(id, other.id) + && Objects.equals(user.getId(), other.user.getId()) + && Objects.equals(subscription.getId(), other.subscription.getId()) + && Objects.equals(activationDate, other.activationDate) + && Objects.equals(expirationDate, other.expirationDate); + } +} diff --git a/src/test/java/com/example/demo/AgeRatingServiceTests.java b/src/test/java/com/example/demo/AgeRatingServiceTests.java index 7483ec6..7b6a273 100644 --- a/src/test/java/com/example/demo/AgeRatingServiceTests.java +++ b/src/test/java/com/example/demo/AgeRatingServiceTests.java @@ -1,15 +1,18 @@ package com.example.demo; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; import com.example.demo.ageRatings.model.AgeRatingEntity; import com.example.demo.ageRatings.service.AgeRatingService; +import com.example.demo.core.error.AlreadyExistsException; import com.example.demo.core.error.NotFoundException; @SpringBootTest @@ -18,48 +21,64 @@ class AgeRatingServiceTests { @Autowired private AgeRatingService ageRatingService; + private AgeRatingEntity ageRating; + + @BeforeEach + void createData() { + removeData(); + + ageRating = ageRatingService.create(new AgeRatingEntity("12+")); + ageRatingService.create(new AgeRatingEntity("16+")); + ageRatingService.create(new AgeRatingEntity("18+")); + } + + @AfterEach + void removeData() { + ageRatingService.getAll().forEach(item -> ageRatingService.delete(item.getId())); + } + @Test void getTest() { Assertions.assertThrows(NotFoundException.class, () -> ageRatingService.get(0L)); } @Test - @Order(1) void createTest() { - ageRatingService.deleteAll(); - - ageRatingService.create(new AgeRatingEntity(null, "12+")); - ageRatingService.create(new AgeRatingEntity(null, "16+")); - final AgeRatingEntity last = ageRatingService.create(new AgeRatingEntity(null, "18+")); - Assertions.assertEquals(3, ageRatingService.getAll().size()); - Assertions.assertEquals(last, ageRatingService.get(3L)); + Assertions.assertEquals(ageRating, ageRatingService.get(ageRating.getId())); + } + + @Test + void createNotUniqueTest() { + final AgeRatingEntity nonUniqueAgeRating = new AgeRatingEntity("16+"); + Assertions.assertThrows(AlreadyExistsException.class, () -> ageRatingService.create(nonUniqueAgeRating)); + } + + @Test + void createNullableTest() { + final AgeRatingEntity nullableType = new AgeRatingEntity(null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> ageRatingService.create(nullableType)); } @Test - @Order(2) void updateTest() { final String test = "TEST"; - final AgeRatingEntity entity = ageRatingService.get(3L); - final String oldName = entity.getName(); - final AgeRatingEntity newEntity = ageRatingService.update(3L, new AgeRatingEntity(2L, test)); + final String oldName = ageRating.getName(); + final AgeRatingEntity newEntity = ageRatingService.update(ageRating.getId(), new AgeRatingEntity(test)); Assertions.assertEquals(3, ageRatingService.getAll().size()); - Assertions.assertEquals(newEntity, ageRatingService.get(3L)); + Assertions.assertEquals(newEntity, ageRatingService.get(ageRating.getId())); Assertions.assertEquals(test, newEntity.getName()); Assertions.assertNotEquals(oldName, newEntity.getName()); } @Test - @Order(3) void deleteTest() { - ageRatingService.delete(3L); + ageRatingService.delete(ageRating.getId()); Assertions.assertEquals(2, ageRatingService.getAll().size()); - final AgeRatingEntity last = ageRatingService.get(2L); - Assertions.assertEquals(2L, last.getId()); - final AgeRatingEntity newEntity = ageRatingService.create(new AgeRatingEntity(null, "18+")); + final AgeRatingEntity newEntity = ageRatingService.create(new AgeRatingEntity("12+")); Assertions.assertEquals(3, ageRatingService.getAll().size()); - Assertions.assertEquals(4L, newEntity.getId()); + Assertions.assertNotEquals(ageRating.getId(), newEntity.getId()); } } diff --git a/src/test/java/com/example/demo/MovieServiceTests.java b/src/test/java/com/example/demo/MovieServiceTests.java index 599e3d8..ce86e72 100644 --- a/src/test/java/com/example/demo/MovieServiceTests.java +++ b/src/test/java/com/example/demo/MovieServiceTests.java @@ -1,15 +1,18 @@ package com.example.demo; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; import com.example.demo.ageRatings.model.AgeRatingEntity; import com.example.demo.ageRatings.service.AgeRatingService; +import com.example.demo.core.error.AlreadyExistsException; import com.example.demo.core.error.NotFoundException; import com.example.demo.movies.model.MovieEntity; import com.example.demo.movies.service.MovieService; @@ -28,82 +31,108 @@ class MovieServiceTests { @Autowired private MovieService movieService; + private TypeEntity type1; + private TypeEntity type2; + private AgeRatingEntity ageRating1; + private AgeRatingEntity ageRating2; + + private MovieEntity last; + + @BeforeEach + void createData() { + removeData(); + + type1 = typeService.create(new TypeEntity("Фильм")); + type2 = typeService.create(new TypeEntity("Сериал")); + + ageRating1 = ageRatingService.create(new AgeRatingEntity("16+")); + ageRating2 = ageRatingService.create(new AgeRatingEntity("18+")); + + movieService.create(new MovieEntity("Бойцовский клуб", type1, false, + "src/assets/main-page/posters/PeakyBlinders.png", + "Сотрудник страховой компании страдает хронической бессонницей...", + 1999, "США", "Интриги. Хаос. Мыло", "Дэвид Финчер", ageRating2, + "https://www.youtube.com/embed/dfeUzm6KF4g?si=U2D-2WNsJzkcxlBX")); + movieService.create(new MovieEntity("Американский психопат", type1, true, + "src/assets/main-page/posters/AmericanPsycho.jpg", + "Днем он ничем не отличается от окружающих, и в толпе вы не обратите на него внимания...", + 2000, "США, Канада", "Респектабельная внешность дьявола", "Мэри Хэррон", ageRating2, + "https://www.youtube.com/embed/PpUWjff_OcM?si=e7tSm4V6qYkz406B")); + last = movieService.create(new MovieEntity("Острые козырьки", type2, false, + "src/assets/main-page/posters/PeakyBlinders.png", + "Сериал рассказывает историю восхождения одной ирландской семьи...", + 2013, "Великобритания", "The streets are theirs", "Энтони Бирн, Колм МакКарти", ageRating1, + "https://www.youtube.com/embed/7cOfn_sjlXM?si=fxNoZjlUon1SZd70")); + } + + @AfterEach + void removeData() { + movieService.deleteAll(); + typeService.deleteAll(); + ageRatingService.deleteAll(); + } + @Test void getTest() { Assertions.assertThrows(NotFoundException.class, () -> movieService.get(0L)); } @Test - @Order(1) void createTest() { - movieService.deleteAll(); - - final var type1 = typeService.create(new TypeEntity(null, "Фильм")); - final var type2 = typeService.create(new TypeEntity(null, "Сериал")); - final var ageRating4 = ageRatingService.create(new AgeRatingEntity(null, "16+")); - final var ageRating5 = ageRatingService.create(new AgeRatingEntity(null, "18+")); - - movieService.create(new MovieEntity(null, "Бойцовский клуб", type1, false, - "src/assets/main-page/posters/PeakyBlinders.png", - "Сотрудник страховой компании страдает хронической бессонницей и отчаянно пытается вырваться из мучительно скучной жизни...", - 1999, "США", "Интриги. Хаос. Мыло", "Дэвид Финчер", ageRating5, - "https://www.youtube.com/embed/dfeUzm6KF4g?si=U2D-2WNsJzkcxlBX")); - movieService.create(new MovieEntity(null, "Американский психопат", type1, true, - "src/assets/main-page/posters/AmericanPsycho.jpg", - "Днем он ничем не отличается от окружающих, и в толпе вы не обратите на него внимания...", - 2000, "США, Канада", "Респектабельная внешность дьявола", "Мэри Хэррон", ageRating5, - "https://www.youtube.com/embed/PpUWjff_OcM?si=e7tSm4V6qYkz406B")); - final MovieEntity last = movieService.create(new MovieEntity(null, "Острые козырьки", type2, false, - "src/assets/main-page/posters/PeakyBlinders.png", - "Сериал рассказывает историю восхождения одной ирландской семьи...", - 2013, "Великобритания", "The streets are theirs", "Энтони Бирн, Колм МакКарти", ageRating4, - "https://www.youtube.com/embed/7cOfn_sjlXM?si=fxNoZjlUon1SZd70")); - - Assertions.assertEquals(3, movieService.getAll(0L).size()); - Assertions.assertEquals(2, movieService.getAll(1L).size()); - Assertions.assertEquals(last, movieService.get(3L)); + Assertions.assertEquals(3, movieService.getAll(0L, 0L).size()); + Assertions.assertEquals(2, movieService.getAll(type1.getId(), 0L).size()); + Assertions.assertEquals(last, movieService.get(last.getId())); } @Test - @Order(2) - void updateTest() { - final var type2 = typeService.create(new TypeEntity(null, "Сериал")); - final var ageRating4 = ageRatingService.create(new AgeRatingEntity(null, "16+")); - - final String test = "TEST"; - final MovieEntity entity = movieService.get(3L); - final String oldName = entity.getTitle(); - final MovieEntity newEntity = movieService.update(3L, new MovieEntity(null, - test, type2, false, + void createNotUniqueTest() { + final MovieEntity nonUniqueMovie = new MovieEntity("Острые козырьки", type2, false, "src/assets/main-page/posters/PeakyBlinders.png", "Сериал рассказывает историю восхождения одной ирландской семьи...", - 2013, "Великобритания", "The streets are theirs", "Энтони Бирн, Колм МакКарти", ageRating4, - "https://www.youtube.com/embed/7cOfn_sjlXM?si=fxNoZjlUon1SZd70")); + 2013, "Великобритания", "The streets are theirs", "Энтони Бирн, Колм МакКарти", ageRating1, + "https://www.youtube.com/embed/7cOfn_sjlXM?si=fxNoZjlUon1SZd70"); + Assertions.assertThrows(AlreadyExistsException.class, () -> movieService.create(nonUniqueMovie)); + } - Assertions.assertEquals(3, movieService.getAll(0L).size()); - Assertions.assertEquals(newEntity, movieService.get(3L)); + @Test + void createNullableTest() { + final MovieEntity nullableItem = new MovieEntity(null, null, false, null, null, null, null, null, null, null, + null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> movieService.create(nullableItem)); + } + + @Test + void updateTest() { + final String test = "TEST"; + final MovieEntity entity = movieService.get(last.getId()); + final String oldName = entity.getTitle(); + final MovieEntity newEntity = movieService.update(last.getId(), new MovieEntity( + test, entity.getType(), entity.getRequiresSubscription(), + entity.getPoster(), + entity.getDescription(), + entity.getReleaseDate(), entity.getCountry(), entity.getTagline(), entity.getDirector(), + entity.getAgeRating(), + entity.getVideo())); + + Assertions.assertEquals(3, movieService.getAll(0L, 0L).size()); + Assertions.assertEquals(newEntity, movieService.get(last.getId())); Assertions.assertEquals(test, newEntity.getTitle()); Assertions.assertNotEquals(oldName, newEntity.getTitle()); } @Test - @Order(3) void deleteTest() { - final var type2 = typeService.create(new TypeEntity(null, "Сериал")); - final var ageRating4 = ageRatingService.create(new AgeRatingEntity(null, "16+")); + movieService.delete(last.getId()); + Assertions.assertEquals(2, movieService.getAll(0L, 0L).size()); - movieService.delete(3L); - Assertions.assertEquals(2, movieService.getAll(0L).size()); - final MovieEntity last = movieService.get(2L); - Assertions.assertEquals(2L, last.getId()); - - final MovieEntity newEntity = movieService.create(new MovieEntity(null, - "Острые козырьки", type2, false, - "src/assets/main-page/posters/PeakyBlinders.png", - "Сериал рассказывает историю восхождения одной ирландской семьи...", - 2013, "Великобритания", "The streets are theirs", "Энтони Бирн, Колм МакКарти", ageRating4, - "https://www.youtube.com/embed/7cOfn_sjlXM?si=fxNoZjlUon1SZd70")); - Assertions.assertEquals(3, movieService.getAll(0L).size()); - Assertions.assertEquals(4L, newEntity.getId()); + final MovieEntity newEntity = movieService.create(new MovieEntity( + last.getTitle(), last.getType(), last.getRequiresSubscription(), + last.getPoster(), + last.getDescription(), + last.getReleaseDate(), last.getCountry(), last.getTagline(), last.getDirector(), + last.getAgeRating(), + last.getVideo())); + Assertions.assertEquals(3, movieService.getAll(0L, 0L).size()); + Assertions.assertNotEquals(last.getId(), newEntity.getId()); } } diff --git a/src/test/java/com/example/demo/ReviewServiceTests.java b/src/test/java/com/example/demo/ReviewServiceTests.java new file mode 100644 index 0000000..381c29f --- /dev/null +++ b/src/test/java/com/example/demo/ReviewServiceTests.java @@ -0,0 +1,139 @@ +package com.example.demo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; + +import com.example.demo.ageRatings.model.AgeRatingEntity; +import com.example.demo.ageRatings.service.AgeRatingService; +import com.example.demo.core.error.AlreadyExistsException; +import com.example.demo.core.error.NotFoundException; +import com.example.demo.movies.model.MovieEntity; +import com.example.demo.movies.service.MovieService; +import com.example.demo.reviews.model.ReviewEntity; +import com.example.demo.reviews.service.ReviewService; +import com.example.demo.types.model.TypeEntity; +import com.example.demo.types.service.TypeService; +import com.example.demo.users.model.UserEntity; +import com.example.demo.users.service.UserService; + +@SpringBootTest +@TestMethodOrder(OrderAnnotation.class) +class ReviewServiceTests { + @Autowired + private MovieService movieService; + @Autowired + private TypeService typeService; + @Autowired + private AgeRatingService ageRatingService; + @Autowired + private ReviewService reviewService; + @Autowired + private UserService userService; + + private MovieEntity movie1; + private MovieEntity movie2; + private UserEntity user1; + private UserEntity user2; + private ReviewEntity last; + + @Test + void getTest() { + Assertions.assertThrows(NotFoundException.class, () -> reviewService.get(0L, 0L)); + } + + @BeforeEach + void createData() { + removeData(); + + var type1 = typeService.create(new TypeEntity("Фильм")); + var type2 = typeService.create(new TypeEntity("Сериал")); + + var ageRating1 = ageRatingService.create(new AgeRatingEntity("16+")); + var ageRating2 = ageRatingService.create(new AgeRatingEntity("18+")); + + movie1 = movieService.create(new MovieEntity("Американский психопат", type1, true, + "src/assets/main-page/posters/AmericanPsycho.jpg", + "Днем он ничем не отличается от окружающих, и в толпе вы не обратите на него внимания...", + 2000, "США, Канада", "Респектабельная внешность дьявола", "Мэри Хэррон", ageRating2, + "https://www.youtube.com/embed/PpUWjff_OcM?si=e7tSm4V6qYkz406B")); + movie2 = movieService.create(new MovieEntity("Острые козырьки", type2, false, + "src/assets/main-page/posters/PeakyBlinders.png", + "Сериал рассказывает историю восхождения одной ирландской семьи...", + 2013, "Великобритания", "The streets are theirs", "Энтони Бирн, Колм МакКарти", ageRating1, + "https://www.youtube.com/embed/7cOfn_sjlXM?si=fxNoZjlUon1SZd70")); + + user1 = userService.create(new UserEntity("myemail@mail.com", "123")); + user2 = userService.create(new UserEntity("secondemail@mail.com", "321")); + + reviewService.create(user1.getId(), movie1.getId(), new ReviewEntity("dsdd", 5)); + reviewService.create(user2.getId(), movie1.getId(), new ReviewEntity("dggfg", 4)); + last = reviewService.create(user1.getId(), movie2.getId(), new ReviewEntity("dsddddd", 5)); + } + + @AfterEach + void removeData() { + reviewService.deleteAll(); + movieService.deleteAll(); + typeService.deleteAll(); + ageRatingService.deleteAll(); + userService.deleteAll(); + } + + @Test + void createTest() { + Assertions.assertEquals(3, reviewService.getAll(0L, 0L).size()); + Assertions.assertEquals(1, reviewService.getAll(movie1.getId(), user1.getId()).size()); + Assertions.assertEquals(last, reviewService.get(user1.getId(), last.getId())); + Assertions.assertThrows(AlreadyExistsException.class, + () -> reviewService.create(user1.getId(), movie1.getId(), new ReviewEntity("dsdd", 5))); + } + + @Test + void createNotUniqueTest() { + final ReviewEntity nonUniqueReview = new ReviewEntity("dsdd", 5); + Assertions.assertThrows(AlreadyExistsException.class, () -> reviewService.create(user1.getId(), movie1.getId(), + nonUniqueReview)); + } + + @Test + void createNullableTest() { + final ReviewEntity nullableReview = new ReviewEntity(null, null); + Assertions.assertThrows(NotFoundException.class, + () -> reviewService.create(0L, 0L, nullableReview)); + Assertions.assertThrows(NotFoundException.class, + () -> reviewService.create(user2.getId(), 0L, nullableReview)); + Assertions.assertThrows(DataIntegrityViolationException.class, + () -> reviewService.create(user2.getId(), movie2.getId(), nullableReview)); + } + + @Test + void updateTest() { + final String test = "TEST"; + final ReviewEntity entity = reviewService.get(user1.getId(), last.getId()); + final String oldDesc = entity.getDescription(); + final ReviewEntity newEntity = reviewService.update(user1.getId(), last.getId(), + new ReviewEntity(test, 5)); + Assertions.assertEquals(3, reviewService.getAll(0L, 0L).size()); + Assertions.assertEquals(newEntity, reviewService.get(user1.getId(), last.getId())); + Assertions.assertEquals(test, newEntity.getDescription()); + Assertions.assertNotEquals(oldDesc, newEntity.getDescription()); + } + + @Test + void deleteTest() { + reviewService.delete(last.getUser().getId(), last.getId()); + Assertions.assertEquals(2, reviewService.getAll(0L, 0L).size()); + + final ReviewEntity newEntity = reviewService.create(user1.getId(), movie2.getId(), + new ReviewEntity("3210", 5)); + Assertions.assertEquals(3, reviewService.getAll(0L, 0L).size()); + Assertions.assertNotEquals(last.getId(), newEntity.getId()); + } +} diff --git a/src/test/java/com/example/demo/TypeServiceTests.java b/src/test/java/com/example/demo/TypeServiceTests.java index cbc6fa7..962e1cc 100644 --- a/src/test/java/com/example/demo/TypeServiceTests.java +++ b/src/test/java/com/example/demo/TypeServiceTests.java @@ -1,13 +1,16 @@ package com.example.demo; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; +import com.example.demo.core.error.AlreadyExistsException; import com.example.demo.core.error.NotFoundException; import com.example.demo.types.model.TypeEntity; import com.example.demo.types.service.TypeService; @@ -18,47 +21,63 @@ class TypeServiceTests { @Autowired private TypeService typeService; + private TypeEntity type; + + @BeforeEach + void createData() { + removeData(); + + type = typeService.create(new TypeEntity("Фильм")); + typeService.create(new TypeEntity("Сериал")); + } + + @AfterEach + void removeData() { + typeService.getAll().forEach(item -> typeService.delete(item.getId())); + } + @Test void getTest() { Assertions.assertThrows(NotFoundException.class, () -> typeService.get(0L)); } @Test - @Order(1) void createTest() { - typeService.deleteAll(); - - typeService.create(new TypeEntity(null, "Фильм")); - final TypeEntity last = typeService.create(new TypeEntity(null, "Сериал")); - Assertions.assertEquals(2, typeService.getAll().size()); - Assertions.assertEquals(last, typeService.get(2L)); + Assertions.assertEquals(type, typeService.get(type.getId())); + } + + @Test + void createNotUniqueTest() { + final TypeEntity nonUniqueType = new TypeEntity("Фильм"); + Assertions.assertThrows(AlreadyExistsException.class, () -> typeService.create(nonUniqueType)); + } + + @Test + void createNullableTest() { + final TypeEntity nullableType = new TypeEntity(null); + Assertions.assertThrows(DataIntegrityViolationException.class, () -> typeService.create(nullableType)); } @Test - @Order(2) void updateTest() { final String test = "TEST"; - final TypeEntity entity = typeService.get(2L); - final String oldName = entity.getName(); - final TypeEntity newEntity = typeService.update(2L, new TypeEntity(1L, test)); + final String oldName = type.getName(); + final TypeEntity newEntity = typeService.update(type.getId(), new TypeEntity(test)); Assertions.assertEquals(2, typeService.getAll().size()); - Assertions.assertEquals(newEntity, typeService.get(2L)); + Assertions.assertEquals(newEntity, typeService.get(type.getId())); Assertions.assertEquals(test, newEntity.getName()); Assertions.assertNotEquals(oldName, newEntity.getName()); } @Test - @Order(3) void deleteTest() { - typeService.delete(2L); + typeService.delete(type.getId()); Assertions.assertEquals(1, typeService.getAll().size()); - final TypeEntity last = typeService.get(1L); - Assertions.assertEquals(1L, last.getId()); - final TypeEntity newEntity = typeService.create(new TypeEntity(null, "Сериал")); + final TypeEntity newEntity = typeService.create(new TypeEntity("Фильм")); Assertions.assertEquals(2, typeService.getAll().size()); - Assertions.assertEquals(3L, newEntity.getId()); + Assertions.assertNotEquals(type.getId(), newEntity.getId()); } } diff --git a/src/test/java/com/example/demo/UserServiceTests.java b/src/test/java/com/example/demo/UserServiceTests.java index 3b0fe4d..ff9e35b 100644 --- a/src/test/java/com/example/demo/UserServiceTests.java +++ b/src/test/java/com/example/demo/UserServiceTests.java @@ -1,11 +1,12 @@ package com.example.demo; -import java.util.ArrayList; +import java.time.Period; import java.util.Date; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; @@ -16,87 +17,134 @@ import com.example.demo.ageRatings.service.AgeRatingService; import com.example.demo.core.error.NotFoundException; import com.example.demo.movies.model.MovieEntity; import com.example.demo.movies.service.MovieService; +import com.example.demo.subscriptions.model.SubscriptionEntity; +import com.example.demo.subscriptions.service.SubscriptionService; import com.example.demo.types.model.TypeEntity; import com.example.demo.types.service.TypeService; import com.example.demo.users.model.UserEntity; import com.example.demo.users.service.UserService; +import com.example.demo.usersubscription.model.UserSubscriptionEntity; @SpringBootTest @TestMethodOrder(OrderAnnotation.class) class UserServiceTests { @Autowired private TypeService typeService; - @Autowired private AgeRatingService ageRatingService; - @Autowired private MovieService movieService; - @Autowired private UserService userService; + @Autowired + private SubscriptionService subscriptionService; + + private UserEntity last; + private TypeEntity type1; + private AgeRatingEntity ageRating1; + private MovieEntity movie1; + private MovieEntity movie2; + private SubscriptionEntity subscription1; + private SubscriptionEntity subscription2; @Test void getTest() { Assertions.assertThrows(NotFoundException.class, () -> userService.get(0L)); } - @Test - @Order(1) - void createTest() { - ageRatingService.deleteAll(); - typeService.deleteAll(); - movieService.deleteAll(); - userService.deleteAll(); + @BeforeEach + void createData() { + removeData(); - userService.create(new UserEntity(null, "myemail@mail.com", "12345678", new Date(), new ArrayList<>())); - userService.create(new UserEntity(null, "secondemail@mail.com", "qwwerty", new Date(), new ArrayList<>())); - final var last = userService - .create(new UserEntity(null, "thirdemail@mail.com", "ytrewq", new Date(), new ArrayList<>())); + userService.create(new UserEntity("myemail@mail.com", "123")); + userService.create(new UserEntity("secondemail@mail.com", "321")); + last = userService.create(new UserEntity("thirdemail@mail.com", "3210")); - Assertions.assertEquals(3, userService.getAll().size()); - Assertions.assertEquals(last, userService.get(3L)); - } - - @Test - @Order(2) - void updateTest() { - final var type1 = typeService.create(new TypeEntity(null, "Фильм")); - final var ageRating5 = ageRatingService.create(new AgeRatingEntity(null, "18+")); - - final var movie1 = movieService.create(new MovieEntity(null, "Бойцовский клуб", type1, false, + type1 = typeService.create(new TypeEntity("Фильм")); + ageRating1 = ageRatingService.create(new AgeRatingEntity("18+")); + movie1 = movieService.create(new MovieEntity("Бойцовский клуб", type1, false, "src/assets/main-page/posters/PeakyBlinders.png", "Сотрудник страховой компании страдает хронической бессонницей и отчаянно пытается вырваться из мучительно скучной жизни...", - 1999, "США", "Интриги. Хаос. Мыло", "Дэвид Финчер", ageRating5, + 1999, "США", "Интриги. Хаос. Мыло", "Дэвид Финчер", ageRating1, "https://www.youtube.com/embed/dfeUzm6KF4g?si=U2D-2WNsJzkcxlBX")); - final var movie2 = movieService.create(new MovieEntity(null, "Американский психопат", type1, true, + movie2 = movieService.create(new MovieEntity("Американский психопат", type1, true, "src/assets/main-page/posters/AmericanPsycho.jpg", "Днем он ничем не отличается от окружающих, и в толпе вы не обратите на него внимания...", - 2000, "США, Канада", "Респектабельная внешность дьявола", "Мэри Хэррон", ageRating5, + 2000, "США, Канада", "Респектабельная внешность дьявола", "Мэри Хэррон", ageRating1, "https://www.youtube.com/embed/PpUWjff_OcM?si=e7tSm4V6qYkz406B")); - userService.addMovie(1L, movie1.getId()); - final UserEntity newEntity = userService.addMovie(1L, movie2.getId()); + subscription1 = subscriptionService.create(new SubscriptionEntity("Подписка 1", Period.of(0, 0, 7), 199.99)); + subscription2 = subscriptionService.create(new SubscriptionEntity("Подписка 2", Period.of(0, 1, 0), 299.99)); + } - Assertions.assertEquals(3, userService.getAll().size()); - Assertions.assertEquals(newEntity, userService.get(1L)); - Assertions.assertEquals(2, userService.get(1L).getWatchLater().size()); - - userService.addMovie(1L, movie1.getId()); - Assertions.assertEquals(2, userService.get(1L).getWatchLater().size()); + @AfterEach + void removeData() { + userService.deleteAll(); + movieService.deleteAll(); + typeService.deleteAll(); + ageRatingService.deleteAll(); + subscriptionService.deleteAll(); } @Test - @Order(3) - void deleteTest() { - userService.delete(3L); - Assertions.assertEquals(2, userService.getAll().size()); - final UserEntity last = userService.get(2L); - Assertions.assertEquals(2L, last.getId()); - - final var newEntity = userService - .create(new UserEntity(null, "thirdemail@mail.com", "ytrewq", new Date(), new ArrayList<>())); + void createTest() { Assertions.assertEquals(3, userService.getAll().size()); - Assertions.assertEquals(4L, newEntity.getId()); + Assertions.assertEquals(last, userService.get(last.getId())); + } + + @Test + void updateTest() { + final String test = "TEST"; + final UserEntity entity = userService.get(last.getId()); + final String oldEmail = entity.getEmail(); + final UserEntity newEntity = userService.update(entity.getId(), + new UserEntity(test, entity.getPassword())); + + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertEquals(newEntity, userService.get(newEntity.getId())); + Assertions.assertEquals(test, newEntity.getEmail()); + Assertions.assertNotEquals(oldEmail, newEntity.getEmail()); + } + + @Test + void deleteTest() { + userService.delete(last.getId()); + Assertions.assertEquals(2, userService.getAll().size()); + + final var newEntity = userService.create(new UserEntity("thirdemail@mail.com", "3210")); + Assertions.assertEquals(3, userService.getAll().size()); + Assertions.assertNotEquals(last.getId(), newEntity.getId()); + } + + @Test + void watchLaterTest() { + userService.addMovieToWatchLater(last.getId(), movie1); + userService.addMovieToWatchLater(last.getId(), movie1); + Assertions.assertEquals(1, userService.getWatchLater(last.getId()).size()); + userService.addMovieToWatchLater(last.getId(), movie2); + Assertions.assertEquals(2, userService.getWatchLater(last.getId()).size()); + + userService.deleteMovieFromWatchLater(last.getId(), movie1); + Assertions.assertEquals(1, userService.getWatchLater(last.getId()).size()); + } + + @Test + void subscriptionsTest() { + Assertions.assertNull(userService.getActiveSubscription(last.getId())); + + userService.activateSubscription(last.getId(), subscription1.getId()); + userService.activateSubscription(last.getId(), subscription1.getId()); + Assertions.assertEquals(1, userService.getUserSubscriptions(last.getId()).size()); + + UserSubscriptionEntity activeSubscription = userService.getActiveSubscription(last.getId()); + Assertions.assertNotNull(activeSubscription); + Assertions.assertTrue(activeSubscription.getExpirationDate().after(new Date())); + + userService.deactivateSubscription(last.getId()); + Assertions.assertNull(userService.getActiveSubscription(last.getId())); + + userService.activateSubscription(last.getId(), subscription2.getId()); + Assertions.assertNotNull(userService.getActiveSubscription(last.getId())); + Assertions.assertEquals(2, userService.getUserSubscriptions(last.getId()).size()); } }