diff --git a/README.md b/README.md
index e2cfdc7..af503c6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,7 @@
# OtusSpringHW-Library
-Домашние задания по Spring для Otus с базами данных.
+
+Домашнее задание #9:
+- CRUD приложение с Web UI и хранением данных в БД.
+- Создайте приложение с хранением сущностей в БД (можно взять DAOs из прошлых занятий).
+- Использовать классический View, предусмотреть страницу отображения всех сущностей и создания/редактирования.
+- View на Thymeleaf, classic Controllers.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a884528
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,229 @@
+
+
+ 4.0.0
+
+ home-work
+
+ ru.otus.mkulikov
+ home-work
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.3.RELEASE
+
+
+
+
+ UTF-8
+
+
+
+
+ net.sf.opencsv
+ opencsv
+ 2.3
+
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.4
+ provided
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+ org.assertj
+ assertj-core
+ 3.12.2
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.3.2
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.3.2
+ test
+
+
+
+ org.junit.platform
+ junit-platform-launcher
+ 1.4.1
+ test
+
+
+
+
+ org.mockito
+ mockito-core
+ 2.21.0
+ test
+
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 2.23.0
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.shell
+ spring-shell-starter
+ 2.0.0.RELEASE
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+
+
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+
+
+
+ org.springframework.data
+ spring-data-keyvalue
+
+
+
+ com.github.cloudyrock.mongock
+ mongock-spring
+ 2.0.0
+
+
+
+
+ org.springframework.data
+ spring-data-jpa
+ 2.0.8.RELEASE
+
+
+
+
+
+ org.webjars
+ jquery
+ 3.4.1
+
+
+
+
+ org.webjars
+ popper.js
+ 1.15.0
+
+
+
+ org.webjars
+ bootstrap
+ 4.3.1
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 11
+ 11
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+ ${encoding}
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.0
+
+
+ false
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+ src/main/resources
+
+ **/*.yml
+ **/*.txt
+ **/*.yaml
+ **/*.html
+ **/*.css
+
+ false
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/ru/otus/mkulikov/Application.java b/src/main/java/ru/otus/mkulikov/Application.java
new file mode 100644
index 0000000..005151d
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/Application.java
@@ -0,0 +1,22 @@
+package ru.otus.mkulikov;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 2019-03-14
+ * Time: 15:27
+ */
+
+@SpringBootApplication
+@EnableConfigurationProperties
+public class Application {
+
+ public static void main(String[] args) throws Exception {
+ SpringApplication.run(Application.class);
+ //Console.main(args);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/AuthorDao.java b/src/main/java/ru/otus/mkulikov/app/dao/AuthorDao.java
new file mode 100644
index 0000000..ee744cc
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/AuthorDao.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+import ru.otus.mkulikov.app.model.Author;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:06
+ */
+
+public interface AuthorDao extends MongoRepository {
+
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/BookDao.java b/src/main/java/ru/otus/mkulikov/app/dao/BookDao.java
new file mode 100644
index 0000000..c44a9bf
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/BookDao.java
@@ -0,0 +1,20 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+import ru.otus.mkulikov.app.model.Book;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 13:28
+ */
+
+public interface BookDao extends MongoRepository {
+
+ List findByAuthor_Id(String id);
+
+ List findByGenre_Id(String id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/CommentDao.java b/src/main/java/ru/otus/mkulikov/app/dao/CommentDao.java
new file mode 100644
index 0000000..aeb0f7d
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/CommentDao.java
@@ -0,0 +1,19 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 20.06.2019
+ * Time: 23:48
+ */
+
+public interface CommentDao extends MongoRepository {
+
+ List findByBook(Book book);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/dao/GenreDao.java b/src/main/java/ru/otus/mkulikov/app/dao/GenreDao.java
new file mode 100644
index 0000000..5c40169
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/dao/GenreDao.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+import ru.otus.mkulikov.app.model.Genre;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:07
+ */
+
+public interface GenreDao extends MongoRepository {
+
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/exception/ObjectNotFound.java b/src/main/java/ru/otus/mkulikov/app/exception/ObjectNotFound.java
new file mode 100644
index 0000000..43f598d
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/exception/ObjectNotFound.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.app.exception;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 03.09.2019
+ * Time: 1:58
+ */
+
+public class ObjectNotFound extends RuntimeException {
+
+ public ObjectNotFound(String obj, String id) {
+ super("Not found object - " + obj + ", id - " + id);
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Author.java b/src/main/java/ru/otus/mkulikov/app/model/Author.java
new file mode 100644
index 0000000..5866262
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Author.java
@@ -0,0 +1,53 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:02
+ */
+
+@Data
+@Document
+public class Author {
+
+ @Id
+ private String id;
+ @Field(value = "surname")
+ private String surname;
+ @Field(value = "firstName")
+ private String firstName;
+ @Field(value = "secondName")
+ private String secondName;
+
+ public Author() {
+ }
+
+ public Author(String id, String surname, String firstName, String secondName) {
+ this.id = id;
+ this.surname = surname;
+ this.firstName = firstName;
+ this.secondName = secondName;
+ }
+
+ public Author(String surname, String firstName, String secondName) {
+ this.surname = surname;
+ this.firstName = firstName;
+ this.secondName = secondName;
+ }
+
+ @Override
+ public String toString() {
+ return "Author{" +
+ "id=" + id +
+ ", surname='" + surname + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", secondName='" + secondName + '\'' +
+ "}\n";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Book.java b/src/main/java/ru/otus/mkulikov/app/model/Book.java
new file mode 100644
index 0000000..86ae6e3
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Book.java
@@ -0,0 +1,67 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.DBRef;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+import java.util.Date;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 13:28
+ */
+
+@Data
+@Document
+public class Book {
+
+ @Id
+ private String id;
+ @Field(value = "addRecordDate")
+ private Date addRecordDate;
+ @Field(value = "caption")
+ private String caption;
+ @Field(value = "description")
+ private String description;
+
+ @DBRef
+ private Author author;
+ @DBRef
+ private Genre genre;
+
+ public Book() {
+ }
+
+ public Book(String id, Date addRecordDate, String caption, Author author, Genre genre, String description) {
+ this.id = id;
+ this.addRecordDate = addRecordDate;
+ this.author = author;
+ this.genre = genre;
+ this.caption = caption;
+ this.description = description;
+ }
+
+ public Book(Date addRecordDate, String caption, Author author, Genre genre, String description) {
+ this.addRecordDate = addRecordDate;
+ this.caption = caption;
+ this.author = author;
+ this.genre = genre;
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return "Book{" +
+ "id=" + id +
+ ", addRecordDate=" + addRecordDate +
+ ", caption='" + caption + '\'' +
+ ", description=" + description +
+ ", author=" + author +
+ ", genre=" + genre +
+ "}\n";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Comment.java b/src/main/java/ru/otus/mkulikov/app/model/Comment.java
new file mode 100644
index 0000000..6cfeaaf
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Comment.java
@@ -0,0 +1,62 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.DBRef;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+import java.util.Date;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 20.06.2019
+ * Time: 23:38
+ */
+
+@Data
+@Document
+public class Comment {
+
+ @Id
+ private String id;
+ @Field(value = "addRecordDate")
+ private Date addRecordDate;
+ @Field(value = "userName")
+ private String userName;
+ @Field(value = "text")
+ private String text;
+
+ @DBRef
+ private Book book;
+
+ public Comment() {
+ }
+
+ public Comment(String id, Book book, Date addRecordDate, String userName, String text) {
+ this.id = id;
+ this.book = book;
+ this.addRecordDate = addRecordDate;
+ this.userName = userName;
+ this.text = text;
+ }
+
+ public Comment(Book book, Date addRecordDate, String userName, String text) {
+ this.book = book;
+ this.addRecordDate = addRecordDate;
+ this.userName = userName;
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return "Comment{" +
+ "id=" + id +
+ ", addRecordDate=" + addRecordDate +
+ ", userName=" + userName +
+ ", text='" + text + '\'' +
+ ", book='" + book + '\'' +
+ "}\n";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/model/Genre.java b/src/main/java/ru/otus/mkulikov/app/model/Genre.java
new file mode 100644
index 0000000..14ca394
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/model/Genre.java
@@ -0,0 +1,43 @@
+package ru.otus.mkulikov.app.model;
+
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:04
+ */
+
+@Data
+@Document
+public class Genre {
+
+ @Id
+ private String id;
+ @Field(value = "name")
+ private String name;
+
+ public Genre() {
+ }
+
+ public Genre(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Genre(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Genre{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ "}\n";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/otus/mkulikov/app/service/AuthorManageService.java b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageService.java
new file mode 100644
index 0000000..84dd68f
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageService.java
@@ -0,0 +1,27 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:59
+ */
+
+public interface AuthorManageService {
+
+ T getAuthorById(String id);
+
+ List getAuthors();
+
+ T addAuthor(String surname, String firstName, String secondName);
+
+ T updateAuthor(String id, String surname, String firstName, String secondName);
+
+ T updateAuthor(T t);
+
+ int deleteAuthor(String id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/AuthorManageServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageServiceImpl.java
new file mode 100644
index 0000000..bca5644
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/AuthorManageServiceImpl.java
@@ -0,0 +1,64 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.AuthorDao;
+import ru.otus.mkulikov.app.exception.ObjectNotFound;
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:53
+ */
+
+@Service
+@RequiredArgsConstructor
+public class AuthorManageServiceImpl implements AuthorManageService {
+
+ private final AuthorDao authorDao;
+
+ @Override
+ public Author getAuthorById(String id) {
+ return authorDao.findById(id).orElseThrow(() -> new ObjectNotFound("Author", id));
+ }
+
+ @Override
+ public List getAuthors() {
+ return authorDao.findAll();
+ }
+
+ @Override
+ public Author addAuthor(String surname, String firstName, String secondName) {
+ return authorDao.save(new Author(surname, firstName, secondName));
+ }
+
+ @Override
+ public Author updateAuthor(String id, String surname, String firstName, String secondName) {
+ Author author = authorDao.findById(id).orElseThrow(() -> new ObjectNotFound("Author", id));
+ author.setSurname(surname);
+ author.setFirstName(firstName);
+ author.setSecondName(secondName);
+
+ return authorDao.save(author);
+ }
+
+ @Override
+ public Author updateAuthor(Author author) {
+ Author orig = authorDao.findById(author.getId()).orElseThrow(() -> new ObjectNotFound("Author", author.getId()));
+ orig.setSurname(author.getSurname());
+ orig.setFirstName(author.getFirstName());
+ orig.setSecondName(author.getSecondName());
+
+ return authorDao.save(orig);
+ }
+
+ @Override
+ public int deleteAuthor(String id) {
+ authorDao.deleteById(id);
+ return 1;
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/BookManageService.java b/src/main/java/ru/otus/mkulikov/app/service/BookManageService.java
new file mode 100644
index 0000000..48991bd
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/BookManageService.java
@@ -0,0 +1,31 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Book;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 27.05.2019
+ * Time: 13:42
+ */
+
+public interface BookManageService {
+
+ T getBookById(String id);
+
+ List getBookByAuthorId(String id);
+
+ List getBookByGenreId(String id);
+
+ List getBooks();
+
+ T addBook(String caption, String authorId, String genreId, String description);
+
+ T updateBook(String id, String caption, String authorId, String genreId, String description);
+
+ T updateBook(T t);
+
+ int deleteBook(String id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/BookManageServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/BookManageServiceImpl.java
new file mode 100644
index 0000000..87e1e97
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/BookManageServiceImpl.java
@@ -0,0 +1,84 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.AuthorDao;
+import ru.otus.mkulikov.app.dao.BookDao;
+import ru.otus.mkulikov.app.dao.GenreDao;
+import ru.otus.mkulikov.app.exception.ObjectNotFound;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 27.05.2019
+ * Time: 13:42
+ */
+
+@Service
+@RequiredArgsConstructor
+public class BookManageServiceImpl implements BookManageService {
+
+ private final BookDao bookDao;
+ private final AuthorDao authorDao;
+ private final GenreDao genreDao;
+
+ @Override
+ public Book getBookById(String id) {
+ return bookDao.findById(id).orElseThrow(() -> new ObjectNotFound("Book", id));
+ }
+
+ @Override
+ public List getBookByAuthorId(String id) {
+ return bookDao.findByAuthor_Id(id);
+ }
+
+ @Override
+ public List getBookByGenreId(String id) {
+ return bookDao.findByGenre_Id(id);
+ }
+
+ @Override
+ public List getBooks() {
+ return bookDao.findAll();
+ }
+
+ @Override
+ public Book addBook(String caption, String authorId, String genreId, String description) {
+ Author author = authorDao.findById(authorId).orElseThrow(() -> new ObjectNotFound("Author", authorId));
+ Genre genre = genreDao.findById(genreId).orElseThrow(() -> new ObjectNotFound("Genre", genreId));
+
+ return bookDao.save(new Book(new Date(), caption, author, genre, description));
+ }
+
+ @Override
+ public Book updateBook(String id, String caption, String authorId, String genreId, String description) {
+ Author author = authorDao.findById(authorId).orElseThrow(() -> new ObjectNotFound("Author", authorId));
+ Genre genre = genreDao.findById(genreId).orElseThrow(() -> new ObjectNotFound("Genre", genreId));
+
+ Book book = bookDao.findById(id).orElseThrow(() -> new ObjectNotFound("Book", id));
+ book.setCaption(caption);
+ book.setAuthor(author);
+ book.setGenre(genre);
+ book.setDescription(description);
+
+ return bookDao.save(book);
+ }
+
+ @Override
+ public Book updateBook(Book book) {
+ book.setAddRecordDate(new Date());
+ return bookDao.save(book);
+ }
+
+ @Override
+ public int deleteBook(String id) {
+ bookDao.deleteById(id);
+ return 1;
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/CommentManageService.java b/src/main/java/ru/otus/mkulikov/app/service/CommentManageService.java
new file mode 100644
index 0000000..1618f08
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/CommentManageService.java
@@ -0,0 +1,30 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Comment;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:50
+ */
+public interface CommentManageService {
+
+ T getCommentById(String id);
+
+ List getComments();
+
+ List getCommentsByBookId(String bookId);
+
+ T addComment(String bookId, String userName, String text);
+
+ T updateComment(String id, String userName, String text);
+
+ T updateComment(T t);
+
+ int deleteComment(String id);
+
+ int deleteComments(List list);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/CommentManageServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/CommentManageServiceImpl.java
new file mode 100644
index 0000000..ee0ffce
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/CommentManageServiceImpl.java
@@ -0,0 +1,77 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.BookDao;
+import ru.otus.mkulikov.app.dao.CommentDao;
+import ru.otus.mkulikov.app.exception.ObjectNotFound;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:52
+ */
+
+@Service
+@RequiredArgsConstructor
+public class CommentManageServiceImpl implements CommentManageService {
+
+ private final CommentDao commentDao;
+ private final BookDao bookDao;
+
+ @Override
+ public Comment getCommentById(String id) {
+ return commentDao.findById(id).orElseThrow(() -> new ObjectNotFound("Comment", id));
+ }
+
+ @Override
+ public List getComments() {
+ return commentDao.findAll();
+ }
+
+ @Override
+ public List getCommentsByBookId(String bookId) {
+ Book book = bookDao.findById(bookId).orElseThrow(() -> new ObjectNotFound("Book", bookId));
+ return commentDao.findByBook(book);
+ }
+
+ @Override
+ public Comment addComment(String bookId, String userName, String text) {
+ Book book = bookDao.findById(bookId).orElseThrow(() -> new ObjectNotFound("Book", bookId));
+ return commentDao.save(new Comment(book, new Date(), userName, text));
+ }
+
+ @Override
+ public Comment updateComment(String id, String userName, String text) {
+ Comment comment = commentDao.findById(id).orElseThrow(() -> new ObjectNotFound("Comment", id));
+ comment.setAddRecordDate(new Date());
+ comment.setUserName(userName);
+ comment.setText(text);
+
+ return commentDao.save(comment);
+ }
+
+ @Override
+ public Comment updateComment(Comment comment) {
+ comment.setAddRecordDate(new Date());
+ return commentDao.save(comment);
+ }
+
+ @Override
+ public int deleteComment(String id) {
+ commentDao.deleteById(id);
+ return 1;
+ }
+
+ @Override
+ public int deleteComments(List list) {
+ commentDao.deleteAll(list);
+ return 1;
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/GenreManageService.java b/src/main/java/ru/otus/mkulikov/app/service/GenreManageService.java
new file mode 100644
index 0000000..d6bfbce
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/GenreManageService.java
@@ -0,0 +1,26 @@
+package ru.otus.mkulikov.app.service;
+
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:50
+ */
+public interface GenreManageService {
+
+ T getGenreById(String id);
+
+ List getGenres();
+
+ T addGenre(String name);
+
+ T updateGenre(String id, String name);
+
+ T updateGenre(T t);
+
+ int deleteGenre(String id);
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/service/GenreManageServiceImpl.java b/src/main/java/ru/otus/mkulikov/app/service/GenreManageServiceImpl.java
new file mode 100644
index 0000000..53bb46b
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/service/GenreManageServiceImpl.java
@@ -0,0 +1,57 @@
+package ru.otus.mkulikov.app.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import ru.otus.mkulikov.app.dao.GenreDao;
+import ru.otus.mkulikov.app.exception.ObjectNotFound;
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:52
+ */
+
+@Service
+@RequiredArgsConstructor
+public class GenreManageServiceImpl implements GenreManageService {
+
+ private final GenreDao genreDao;
+
+ @Override
+ public Genre getGenreById(String id) {
+ return genreDao.findById(id).orElseThrow(() -> new ObjectNotFound("Genre", id));
+ }
+
+ @Override
+ public List getGenres() {
+ return genreDao.findAll();
+ }
+
+ @Override
+ public Genre addGenre(String name) {
+ return genreDao.save(new Genre(name));
+ }
+
+ @Override
+ public Genre updateGenre(String id, String name) {
+ Genre genre = genreDao.findById(id).orElseThrow(() -> new ObjectNotFound("Genre", id));
+ genre.setName(name);
+
+ return genreDao.save(genre);
+ }
+
+ @Override
+ public Genre updateGenre(Genre genre) {
+ return genreDao.save(genre);
+ }
+
+ @Override
+ public int deleteGenre(String id) {
+ genreDao.deleteById(id);
+ return 1;
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/app/utils/DateUtil.java b/src/main/java/ru/otus/mkulikov/app/utils/DateUtil.java
new file mode 100644
index 0000000..2401e4f
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/app/utils/DateUtil.java
@@ -0,0 +1,44 @@
+package ru.otus.mkulikov.app.utils;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 05.06.2019
+ * Time: 11:33
+ */
+
+public class DateUtil {
+
+ private final static String DATE_FORMAT = "yyyy-MM-dd";
+
+ private final static String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ public static String dateToString(Date date) {
+ DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+ return date != null ? dateFormat.format(date) : null;
+ }
+
+ public static String dateTimeToString(Date date) {
+ DateFormat dateFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
+ return date != null ? dateFormat.format(date) : null;
+ }
+
+ public static java.util.Date stringToDateTime(String stringDate) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
+ LocalDateTime dateTime = LocalDateTime.parse(stringDate, formatter);
+ return dateTime != null ? convertToDateViaInstant(dateTime) : null;
+ }
+
+ public static Date convertToDateViaInstant(LocalDateTime dateToConvert) {
+ return java.util.Date
+ .from(dateToConvert.atZone(ZoneId.systemDefault())
+ .toInstant());
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/changelog/DbChangeLog.java b/src/main/java/ru/otus/mkulikov/changelog/DbChangeLog.java
new file mode 100644
index 0000000..f84fb0f
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/changelog/DbChangeLog.java
@@ -0,0 +1,73 @@
+package ru.otus.mkulikov.changelog;
+
+import com.github.cloudyrock.mongock.ChangeLog;
+import com.github.cloudyrock.mongock.ChangeSet;
+import com.mongodb.client.MongoDatabase;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.Date;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 19.08.2019
+ * Time: 23:33
+ */
+
+@ChangeLog(order = "001")
+public class DbChangeLog {
+
+ private final String DATE_TIME = "2019-01-01 10:01:01";
+ private Date date = DateUtil.stringToDateTime(DATE_TIME);
+
+ private Genre genre1;
+ private Genre genre2;
+ private Genre genre3;
+
+ private Author author1;
+ private Author author2;
+ private Author author3;
+
+ private Book book1;
+ private Book book2;
+ private Book book3;
+
+ @ChangeSet(order = "000", id = "dropDB", author = "kulikov-mv", runAlways = true)
+ public void dropDB(MongoDatabase database) {
+ database.drop();
+ }
+
+ @ChangeSet(order = "001", id = "initGenres", author = "kulikov-mv", runAlways = true)
+ public void initGenres(MongoTemplate template) {
+ genre1 = template.save(new Genre("GenreName1"));
+ genre2 = template.save(new Genre("GenreName2"));
+ genre3 = template.save(new Genre("GenreName3"));
+ }
+
+ @ChangeSet(order = "002", id = "initAuthors", author = "kulikov-mv", runAlways = true)
+ public void initAuthors(MongoTemplate template) {
+ author1 = template.save(new Author("Surname1", "FirstName1", "SecondName1"));
+ author2 = template.save(new Author("Surname2", "FirstName2", "SecondName2"));
+ author3 = template.save(new Author("Surname3", "FirstName3", "SecondName3"));
+ }
+
+ @ChangeSet(order = "003", id = "initBooks", author = "kulikov-mv", runAlways = true)
+ public void initBooks(MongoTemplate template) {
+ book1 = template.save(new Book(date, "BookName1", author1, genre1, "Description1"));
+ book2 = template.save(new Book(date, "BookName2", author2, genre2, "Description2"));
+ book3 = template.save(new Book(date, "BookName3", author3, genre3, "Description3"));
+ }
+
+ @ChangeSet(order = "004", id = "initComments", author = "kulikov-mv", runAlways = true)
+ public void initComments(MongoTemplate template) {
+ template.save(new Comment(book1, date, "user1", "text1"));
+ template.save(new Comment(book1, date, "user2", "text2"));
+ template.save(new Comment(book2, date, "user3", "text3"));
+ template.save(new Comment(book2, date, "user4", "text4"));
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/config/ApplicationConfig.java b/src/main/java/ru/otus/mkulikov/config/ApplicationConfig.java
new file mode 100644
index 0000000..ab6f856
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/config/ApplicationConfig.java
@@ -0,0 +1,24 @@
+package ru.otus.mkulikov.config;
+
+import com.github.cloudyrock.mongock.Mongock;
+import com.github.cloudyrock.mongock.SpringMongockBuilder;
+import com.mongodb.MongoClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 19.08.2019
+ * Time: 23:56
+ */
+
+@Configuration
+public class ApplicationConfig {
+
+ @Bean
+ public Mongock mongock(MongoProps mongoProps, MongoClient mongoClient) {
+ return new SpringMongockBuilder(mongoClient, mongoProps.getDatabase(), mongoProps.getPack())
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/otus/mkulikov/config/MongoProps.java b/src/main/java/ru/otus/mkulikov/config/MongoProps.java
new file mode 100644
index 0000000..d195ebe
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/config/MongoProps.java
@@ -0,0 +1,22 @@
+package ru.otus.mkulikov.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 19.08.2019
+ * Time: 23:55
+ */
+
+@Data
+@Component
+@ConfigurationProperties("spring.data.mongodb")
+public class MongoProps {
+ private int port;
+ private String database;
+ private String uri;
+ private String pack;
+}
diff --git a/src/main/java/ru/otus/mkulikov/config/WebMvcConfig.java b/src/main/java/ru/otus/mkulikov/config/WebMvcConfig.java
new file mode 100644
index 0000000..a6eb5ac
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/config/WebMvcConfig.java
@@ -0,0 +1,26 @@
+package ru.otus.mkulikov.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@EnableWebMvc
+public class WebMvcConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler(
+ "/webjars/**",
+ "/img/**",
+ "/css/**",
+ "/js/**")
+ .addResourceLocations(
+ "classpath:/META-INF/resources/webjars/",
+ "classpath:/static/img/",
+ "classpath:/static/css/",
+ "classpath:/static/js/");
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/ru/otus/mkulikov/rest/AuthorController.java b/src/main/java/ru/otus/mkulikov/rest/AuthorController.java
new file mode 100644
index 0000000..83f5038
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/rest/AuthorController.java
@@ -0,0 +1,58 @@
+package ru.otus.mkulikov.rest;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.service.AuthorManageService;
+import ru.otus.mkulikov.app.service.BookManageService;
+
+import java.util.List;
+
+@Controller
+@RequiredArgsConstructor
+public class AuthorController {
+
+ private final AuthorManageService authorManageService;
+ private final BookManageService bookManageService;
+
+ @GetMapping("/author/list")
+ public String getAll(Model model) {
+ List authors = authorManageService.getAuthors();
+ model.addAttribute("authors", authors);
+ return "authors";
+ }
+
+ @GetMapping("/author")
+ public String getById(@RequestParam("id") String id, Model model) {
+ Author author = authorManageService.getAuthorById(id);
+ model.addAttribute("author", author);
+ return "author";
+ }
+
+ @GetMapping("/author/new")
+ public String getNew() {
+ Author author = authorManageService.addAuthor("", "", "");
+ return "redirect:/author?id=" + author.getId();
+ }
+
+ @PostMapping(value = "/author/edit")
+ public String edit(@ModelAttribute Author author) {
+ authorManageService.updateAuthor(author);
+ return "redirect:/author/list";
+ }
+
+ @PostMapping(value = "/author/delete")
+ public String delete(@RequestParam("id") String id) {
+ List books = bookManageService.getBookByAuthorId(id);
+ if (books == null || (books != null && books.isEmpty())) {
+ authorManageService.deleteAuthor(id);
+ }
+ return "redirect:/author/list";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/rest/BookController.java b/src/main/java/ru/otus/mkulikov/rest/BookController.java
new file mode 100644
index 0000000..c7c8dbf
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/rest/BookController.java
@@ -0,0 +1,91 @@
+package ru.otus.mkulikov.rest;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.service.AuthorManageService;
+import ru.otus.mkulikov.app.service.BookManageService;
+import ru.otus.mkulikov.app.service.CommentManageService;
+import ru.otus.mkulikov.app.service.GenreManageService;
+
+import java.util.List;
+
+@Controller
+@RequiredArgsConstructor
+public class BookController {
+
+ private final BookManageService bookManageSevice;
+ private final AuthorManageService authorManageService;
+ private final GenreManageService genreManageService;
+ private final CommentManageService commentManageService;
+
+ @GetMapping("/book/list")
+ public String getAll(Model model) {
+ List books = bookManageSevice.getBooks();
+ model.addAttribute("books", books);
+ return "books";
+ }
+
+ @GetMapping("/book")
+ public String getById(@RequestParam("id") String id, Model model) {
+ List authors = authorManageService.getAuthors();
+ List genres = genreManageService.getGenres();
+ Book book = bookManageSevice.getBookById(id);
+
+ model.addAttribute("book", book);
+ model.addAttribute("authors", authors);
+ model.addAttribute("genres", genres);
+
+ return "book";
+ }
+
+ @PostMapping(value = "/book/edit")
+ public String edit(@ModelAttribute Book book, Model model) {
+ List authors = authorManageService.getAuthors();
+ List genres = genreManageService.getGenres();
+ Book updated = bookManageSevice.updateBook(book);
+
+ model.addAttribute("book", updated);
+ model.addAttribute("authors", authors);
+ model.addAttribute("genres", genres);
+
+ return "redirect:/book/list";
+ }
+
+ @GetMapping("/book/new")
+ public String getNew(Model model) {
+ List authors = authorManageService.getAuthors();
+ List genres = genreManageService.getGenres();
+
+ String authorId = (authors != null && !authors.isEmpty())
+ ? authors.get(0).getId()
+ : "";
+ String genreId = (genres != null && !genres.isEmpty())
+ ? genres.get(0).getId()
+ : "";
+
+ Book book = bookManageSevice.addBook("", authorId, genreId, "");
+
+ model.addAttribute("book", book);
+ model.addAttribute("authors", authors);
+ model.addAttribute("genres", genres);
+
+ return "book";
+ }
+
+ @PostMapping(value = "/book/delete")
+ public String delete(@RequestParam("id") String id) {
+ List comments = commentManageService.getCommentsByBookId(id);
+ commentManageService.deleteComments(comments);
+ bookManageSevice.deleteBook(id);
+ return "redirect:/book/list";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/rest/CommentController.java b/src/main/java/ru/otus/mkulikov/rest/CommentController.java
new file mode 100644
index 0000000..4e98ccd
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/rest/CommentController.java
@@ -0,0 +1,73 @@
+package ru.otus.mkulikov.rest;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.service.BookManageService;
+import ru.otus.mkulikov.app.service.CommentManageService;
+
+import java.util.List;
+
+@Controller
+@RequiredArgsConstructor
+public class CommentController {
+
+ private final BookManageService bookManageSevice;
+ private final CommentManageService commentManageService;
+
+ @GetMapping("/comment/list")
+ public String getAll(Model model) {
+ List comments = commentManageService.getComments();
+ model.addAttribute("comments", comments);
+ return "comments";
+ }
+
+ @GetMapping("/comment")
+ public String getById(@RequestParam("id") String id, Model model) {
+ List books = bookManageSevice.getBooks();
+ Comment comment = commentManageService.getCommentById(id);
+
+ model.addAttribute("books", books);
+ model.addAttribute("comment", comment);
+
+ return "comment";
+ }
+
+ @PostMapping(value = "/comment/edit")
+ public String edit(@ModelAttribute Comment comment, Model model) {
+ List books = bookManageSevice.getBooks();
+ Comment updated = commentManageService.updateComment(comment);
+
+ model.addAttribute("books", books);
+ model.addAttribute("comment", updated);
+
+ return "redirect:/comment/list";
+ }
+
+ @GetMapping("/comment/new")
+ public String getNew(Model model) {
+ List books = bookManageSevice.getBooks();
+
+ String bookId = (books != null && !books.isEmpty())
+ ? books.get(0).getId()
+ : "";
+ Comment comment = commentManageService.addComment(bookId, "", "");
+
+ model.addAttribute("books", books);
+ model.addAttribute("comment", comment);
+
+ return "comment";
+ }
+
+ @PostMapping(value = "/comment/delete")
+ public String delete(@RequestParam("id") String id) {
+ commentManageService.deleteComment(id);
+ return "redirect:/comment/list";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/rest/GenreController.java b/src/main/java/ru/otus/mkulikov/rest/GenreController.java
new file mode 100644
index 0000000..7683485
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/rest/GenreController.java
@@ -0,0 +1,59 @@
+package ru.otus.mkulikov.rest;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.service.BookManageService;
+import ru.otus.mkulikov.app.service.GenreManageService;
+
+import java.util.List;
+
+@Controller
+@RequiredArgsConstructor
+public class GenreController {
+
+ private final BookManageService bookManageService;
+ private final GenreManageService genreManageService;
+
+ @GetMapping("/genre/list")
+ public String getAll(Model model) {
+ List genres = genreManageService.getGenres();
+ model.addAttribute("genres", genres);
+ return "genres";
+ }
+
+ @GetMapping("/genre")
+ public String getById(@RequestParam("id") String id, Model model) {
+ Genre genre = genreManageService.getGenreById(id);
+ model.addAttribute("genre", genre);
+ return "genre";
+ }
+
+ @GetMapping("/genre/new")
+ public String getNew() {
+ Genre genre = genreManageService.addGenre("");
+ return "redirect:/genre?id=" + genre.getId();
+ }
+
+ @PostMapping(value = "/genre/edit")
+ public String edit(@ModelAttribute Genre genre, Model model) {
+ Genre updated = genreManageService.updateGenre(genre);
+ model.addAttribute("genre", updated);
+ return "redirect:/genre/list";
+ }
+
+ @PostMapping(value = "/genre/delete")
+ public String delete(@RequestParam("id") String id) {
+ List books = bookManageService.getBookByGenreId(id);
+ if (books == null || (books != null && books.isEmpty())) {
+ genreManageService.deleteGenre(id);
+ }
+ return "redirect:/genre/list";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/rest/MainController.java b/src/main/java/ru/otus/mkulikov/rest/MainController.java
new file mode 100644
index 0000000..cff40c8
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/rest/MainController.java
@@ -0,0 +1,15 @@
+package ru.otus.mkulikov.rest;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+public class MainController {
+
+ @RequestMapping("/")
+ public String helloWorld(Model model) {
+ return "index";
+ }
+
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/AuthorCommands.java b/src/main/java/ru/otus/mkulikov/shell/AuthorCommands.java
new file mode 100644
index 0000000..241ce94
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/AuthorCommands.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.service.AuthorManageService;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:46
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class AuthorCommands {
+
+ private final AuthorManageService authorManageService;
+
+ @ShellMethod(key = {"getAuthorById"}, value = "Select author by id.")
+ public String getAuthorById(@ShellOption String id) {
+ Author author = authorManageService.getAuthorById(id);
+ return author.toString();
+ }
+
+ @ShellMethod(key = {"getAuthors"}, value = "Select all authors.")
+ public String getAuthors() {
+ List allObjects = authorManageService.getAuthors();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addAuthor"}, value = "Add new author.")
+ public String addAuthor(@ShellOption String surname, @ShellOption String firstName, @ShellOption String secondName) {
+ Author author = authorManageService.addAuthor(surname, firstName, secondName);
+ return "Add " + (author != null ? 1 : 0) + " row(s)\nID: " + author.getId();
+ }
+
+ @ShellMethod(key = {"updateAuthor"}, value = "Update author by id.")
+ public String updateAuthor(@ShellOption String id, @ShellOption String surname, @ShellOption String firstName, @ShellOption String secondName) {
+ Author author = authorManageService.updateAuthor(id, surname, firstName, secondName);
+ return "Updated " + (author != null ? 1 : 0) + " row(s)\nID: " + author.getId();
+ }
+
+ @ShellMethod(key = {"deleteAuthor"}, value = "Delete author by id.")
+ public String deleteAuthor(@ShellOption String id) {
+ int count = authorManageService.deleteAuthor(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/BookCommands.java b/src/main/java/ru/otus/mkulikov/shell/BookCommands.java
new file mode 100644
index 0000000..21036c8
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/BookCommands.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.service.BookManageService;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 23.05.2019
+ * Time: 17:33
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class BookCommands {
+
+ private final BookManageService bookManageSevice;
+
+ @ShellMethod(key = {"getBookById"}, value = "Select book by id.")
+ public String getBookById(@ShellOption String id) {
+ Book book = bookManageSevice.getBookById(id);
+ return book.toString();
+ }
+
+ @ShellMethod(key = {"getBooks"}, value = "Select all books.")
+ public String getBooks() {
+ List allObjects = bookManageSevice.getBooks();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addBook"}, value = "Add new book.")
+ public String addBook(@ShellOption String caption, @ShellOption String authorId, @ShellOption String genreId, @ShellOption String description) {
+ Book book = bookManageSevice.addBook(caption, authorId, genreId, description);
+ return "Add " + (book != null ? 1 : 0) + " row(s)\nID: " + book.getId();
+ }
+
+ @ShellMethod(key = {"updateBook"}, value = "Update book by id.")
+ public String updateBook(@ShellOption String id, @ShellOption String caption, @ShellOption String authorId, @ShellOption String genreId, @ShellOption String description) {
+ Book book = bookManageSevice.updateBook(id, caption, authorId, genreId, description);
+ return "Updated " + (book != null ? 1 : 0) + " row(s)\nID: " + book.getId();
+ }
+
+ @ShellMethod(key = {"deleteBook"}, value = "Delete book by id.")
+ public String deleteBook(@ShellOption String id) {
+ int count = bookManageSevice.deleteBook(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/CommentCommands.java b/src/main/java/ru/otus/mkulikov/shell/CommentCommands.java
new file mode 100644
index 0000000..abd452d
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/CommentCommands.java
@@ -0,0 +1,60 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.service.CommentManageService;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:47
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class CommentCommands {
+
+ private final CommentManageService commentManageService;
+
+ @ShellMethod(key = {"getCommentById"}, value = "Select comment by id.")
+ public String getCommentById(@ShellOption String id) {
+ Comment comment = commentManageService.getCommentById(id);
+ return comment.toString();
+ }
+
+ @ShellMethod(key = {"getComments"}, value = "Select all comments.")
+ public String getComments() {
+ List allObjects = commentManageService.getComments();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"getCommentsByBookId"}, value = "Select all comments.")
+ public String getCommentsByBookId(@ShellOption String bookId) {
+ List allObjects = commentManageService.getCommentsByBookId(bookId);
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addComment"}, value = "Add new comment.")
+ public String addComment(@ShellOption String bookId, @ShellOption String userName, @ShellOption String text) {
+ Comment comment = commentManageService.addComment(bookId, userName, text);
+ return "Add " + (comment != null ? 1 : 0) + " row(s)\nID: " + comment.getId();
+ }
+
+ @ShellMethod(key = {"updateComment"}, value = "Update comment by id.")
+ public String updateComment(@ShellOption String id, @ShellOption String userName, @ShellOption String text) {
+ Comment comment = commentManageService.updateComment(id, userName, text);
+ return "Updated " + (comment != null ? 1 : 0) + " row(s)\nID: " + comment.getId();
+ }
+
+ @ShellMethod(key = {"deleteComment"}, value = "Delete comment by id.")
+ public String deleteComment(@ShellOption String id) {
+ int count = commentManageService.deleteComment(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/java/ru/otus/mkulikov/shell/GenreCommands.java b/src/main/java/ru/otus/mkulikov/shell/GenreCommands.java
new file mode 100644
index 0000000..aa4b95a
--- /dev/null
+++ b/src/main/java/ru/otus/mkulikov/shell/GenreCommands.java
@@ -0,0 +1,54 @@
+package ru.otus.mkulikov.shell;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.service.GenreManageService;
+
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 14:47
+ */
+
+@ShellComponent
+@RequiredArgsConstructor
+public class GenreCommands {
+
+ private final GenreManageService genreManageService;
+
+ @ShellMethod(key = {"getGenreById"}, value = "Select genre by id.")
+ public String getGenreById(@ShellOption String id) {
+ Genre genre = genreManageService.getGenreById(id);
+ return genre.toString();
+ }
+
+ @ShellMethod(key = {"getGenres"}, value = "Select all genres.")
+ public String getGenres() {
+ List allObjects = genreManageService.getGenres();
+ return allObjects.toString();
+ }
+
+ @ShellMethod(key = {"addGenre"}, value = "Add new genre.")
+ public String addGenre(@ShellOption String name) {
+ Genre genre = genreManageService.addGenre(name);
+ return "Add " + (genre != null ? 1 : 0) + " row(s)\nID: " + genre.getId();
+ }
+
+ @ShellMethod(key = {"updateGenre"}, value = "Update genre by id.")
+ public String updateGenre(@ShellOption String id, @ShellOption String name) {
+ Genre genre = genreManageService.updateGenre(id, name);
+ return "Updated " + (genre != null ? 1 : 0) + " row(s)\nID: " + genre.getId();
+ }
+
+ @ShellMethod(key = {"deleteGenre"}, value = "Delete genre by id.")
+ public String deleteGenre(@ShellOption String id) {
+ int count = genreManageService.deleteGenre(id);
+ return "Deleted " + count + " row(s)";
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..43cd088
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,11 @@
+spring:
+ banner:
+ location: "classpath:banner.txt"
+
+ data:
+ mongodb:
+ uri: mongodb://localhost
+ port: 27017
+ database: library
+ pack: "ru.otus.mkulikov.changelog"
+
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
new file mode 100644
index 0000000..1687640
--- /dev/null
+++ b/src/main/resources/banner.txt
@@ -0,0 +1,11 @@
+ ___ ___ ___ ___ ___
+ /\__\ /\ \ /\__\ /\ \ /\ \
+ /:/ / /::\ \ /:/ / /::\ \ /::\ \
+ /:/ / /:/\:\ \ /:/ / /:/\:\ \ /:/\ \ \
+ /:/ / ___ /:/ \:\ \ /:/ / /::\~\:\ \ _\:\~\ \ \
+ /:/__/ /\__\ /:/__/ \:\__\ /:/__/ /:/\:\ \:\__\ /\ \:\ \ \__\
+ \:\ \ /:/ / \:\ \ /:/ / \:\ \ \:\~\:\ \/__/ \:\ \:\ \/__/
+ \:\ /:/ / \:\ /:/ / \:\ \ \:\ \:\__\ \:\ \:\__\
+ \:\/:/ / \:\/:/ / \:\ \ \:\ \/__/ \:\/:/ /
+ \::/ / \::/ / \:\__\ \:\__\ \::/ /
+ \/__/ \/__/ \/__/ \/__/ \/__/
\ No newline at end of file
diff --git a/src/main/resources/static/css/header.css b/src/main/resources/static/css/header.css
new file mode 100644
index 0000000..f1541bf
--- /dev/null
+++ b/src/main/resources/static/css/header.css
@@ -0,0 +1,80 @@
+body {
+ padding: 50px;
+}
+
+.btns td {
+ border: 1px solid white;
+ padding: 5px;
+}
+
+.objects td {
+ border: 1px solid lightgray;
+ padding: 5px;
+}
+
+/*https://cssgradientbutton.com/#*/
+.button-new {
+ border:1px solid #96f0ff; -webkit-border-radius: 3px; -moz-border-radius: 3px;border-radius: 3px;font-size:12px;font-family:arial, helvetica, sans-serif; padding: 10px 10px 10px 10px; text-decoration:none; display:inline-block;font-weight:bold; color: #000000;
+ background-color: #cef8ff; background-image: -webkit-gradient(linear, left top, left bottom, from(#cef8ff), to(#7fe0f8));
+ background-image: -webkit-linear-gradient(top, #cef8ff, #7fe0f8);
+ background-image: -moz-linear-gradient(top, #cef8ff, #7fe0f8);
+ background-image: -ms-linear-gradient(top, #cef8ff, #7fe0f8);
+ background-image: -o-linear-gradient(top, #cef8ff, #7fe0f8);
+ background-image: linear-gradient(to bottom, #cef8ff, #7fe0f8);
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#cef8ff, endColorstr=#7fe0f8);
+}
+
+.button-new:hover {
+ border:1px solid #68e9ff;
+ background-color: #9bf1ff; background-image: -webkit-gradient(linear, left top, left bottom, from(#9bf1ff), to(#4fd4f5));
+ background-image: -webkit-linear-gradient(top, #9bf1ff, #4fd4f5);
+ background-image: -moz-linear-gradient(top, #9bf1ff, #4fd4f5);
+ background-image: -ms-linear-gradient(top, #9bf1ff, #4fd4f5);
+ background-image: -o-linear-gradient(top, #9bf1ff, #4fd4f5);
+ background-image: linear-gradient(to bottom, #9bf1ff, #4fd4f5);
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#9bf1ff, endColorstr=#4fd4f5);
+}
+
+.button {
+ border:1px solid #d1dcdf; -webkit-border-radius: 3px; -moz-border-radius: 3px;border-radius: 3px;font-size:12px;font-family:arial, helvetica, sans-serif; padding: 10px 10px 10px 10px; text-decoration:none; display:inline-block;font-weight:bold; color: #000000;
+ background-color: #f2f5f6; background-image: -webkit-gradient(linear, left top, left bottom, from(#f2f5f6), to(#c8d7dc));
+ background-image: -webkit-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: -moz-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: -ms-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: -o-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: linear-gradient(to bottom, #f2f5f6, #c8d7dc);
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#f2f5f6, endColorstr=#c8d7dc);
+}
+
+.button:hover {
+ border:1px solid #b6c7cc;
+ background-color: #d4dee1; background-image: -webkit-gradient(linear, left top, left bottom, from(#d4dee1), to(#a9c0c8));
+ background-image: -webkit-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: -moz-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: -ms-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: -o-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: linear-gradient(to bottom, #d4dee1, #a9c0c8);
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#d4dee1, endColorstr=#a9c0c8);
+}
+
+.button-table {
+ border:1px solid #d1dcdf; -webkit-border-radius: 3px; -moz-border-radius: 3px;border-radius: 3px;font-size:12px;font-family:arial, helvetica, sans-serif; padding: 3px 3px 3px 3px; text-decoration:none; display:inline-block;font-weight:bold; color: #000000;
+ background-color: #f2f5f6; background-image: -webkit-gradient(linear, left top, left bottom, from(#f2f5f6), to(#c8d7dc));
+ background-image: -webkit-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: -moz-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: -ms-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: -o-linear-gradient(top, #f2f5f6, #c8d7dc);
+ background-image: linear-gradient(to bottom, #f2f5f6, #c8d7dc);
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#f2f5f6, endColorstr=#c8d7dc);
+}
+
+.button-table:hover {
+ border:1px solid #b6c7cc;
+ background-color: #d4dee1; background-image: -webkit-gradient(linear, left top, left bottom, from(#d4dee1), to(#a9c0c8));
+ background-image: -webkit-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: -moz-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: -ms-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: -o-linear-gradient(top, #d4dee1, #a9c0c8);
+ background-image: linear-gradient(to bottom, #d4dee1, #a9c0c8);
+ filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#d4dee1, endColorstr=#a9c0c8);
+}
\ No newline at end of file
diff --git a/src/main/resources/templates/author.html b/src/main/resources/templates/author.html
new file mode 100644
index 0000000..481b222
--- /dev/null
+++ b/src/main/resources/templates/author.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ Author detail
+
+
+
+
+
+
+
+Author detail:
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/authors.html b/src/main/resources/templates/authors.html
new file mode 100644
index 0000000..94ff040
--- /dev/null
+++ b/src/main/resources/templates/authors.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+ List of all authors
+
+
+
+
+Authors:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Surname
+ FirstName
+ SecondName
+
+
+
+
+
+
+ 1
+ John Doe
+ John Doe
+ John Doe
+
+
+
+
+
+
+
+
+
+
+
+Сущности, у которых есть связи с другими объектами, удаляться не будут
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/book.html b/src/main/resources/templates/book.html
new file mode 100644
index 0000000..602d475
--- /dev/null
+++ b/src/main/resources/templates/book.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ Book detail
+
+
+
+
+
+
+
+Book detail:
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/books.html b/src/main/resources/templates/books.html
new file mode 100644
index 0000000..6d792b3
--- /dev/null
+++ b/src/main/resources/templates/books.html
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+ List of all books
+
+
+
+
+Books:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Date
+ Caption
+ Description
+ Author
+ Genre
+
+
+
+
+
+
+ 1
+ John Doe
+ John Doe
+ John Doe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/comment.html b/src/main/resources/templates/comment.html
new file mode 100644
index 0000000..9d6f12e
--- /dev/null
+++ b/src/main/resources/templates/comment.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+ Comment detail
+
+
+
+
+
+
+
+Comment detail:
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/comments.html b/src/main/resources/templates/comments.html
new file mode 100644
index 0000000..90a696a
--- /dev/null
+++ b/src/main/resources/templates/comments.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+ List of all comments
+
+
+
+
+Comments:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ AddRecordDate
+ Book
+ UserName
+ Text
+
+
+
+
+
+
+ 1
+ John Doe
+
+
+
+ John Doe
+ John Doe
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/genre.html b/src/main/resources/templates/genre.html
new file mode 100644
index 0000000..84a31df
--- /dev/null
+++ b/src/main/resources/templates/genre.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+ Genre detail
+
+
+
+
+
+
+
+Genre detail:
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/genres.html b/src/main/resources/templates/genres.html
new file mode 100644
index 0000000..1dda84c
--- /dev/null
+++ b/src/main/resources/templates/genres.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+ List of all genres
+
+
+
+
+Genres:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Name
+
+
+
+
+
+
+ 1
+ John Doe
+
+
+
+
+
+
+
+
+
+
+
+Сущности, у которых есть связи с другими объектами, удаляться не будут
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
new file mode 100644
index 0000000..121da5e
--- /dev/null
+++ b/src/main/resources/templates/index.html
@@ -0,0 +1,72 @@
+
+
+
+
+ Twitter Bootstrap Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Библиотека
+
+
+
+
+
+
+
+
+
+
+ #
+ Имя
+ Фамилия
+ Username
+
+
+
+
+ 1
+ Mark
+ Otto
+ @mdo
+
+
+ 2
+ Jacob
+ Thornton
+ @fat
+
+
+ 3
+ Larry
+ the Bird
+ @twitter
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/AuthorDaoTest.java b/src/test/java/ru/otus/mkulikov/app/dao/AuthorDaoTest.java
new file mode 100644
index 0000000..6c3c185
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/AuthorDaoTest.java
@@ -0,0 +1,103 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthor;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthorList;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 29.05.2019
+ * Time: 10:12
+ */
+
+@DataMongoTest
+@DisplayName("Класс AuthorDao")
+class AuthorDaoTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+ private final String ID_3 = "3";
+ private final String ID_4 = "4";
+
+ private final int OBJECT_COUNT_3 = 3;
+ private final int OBJECT_COUNT_2 = 2;
+
+ private final String TEST_SURNAME = "TestSurname";
+ private final String TEST_FIRST_NAME = "TestFirstName";
+ private final String TEST_SECOND_NAME = "TestSecondName";
+
+ @Autowired
+ private MongoTemplate mongoTemplate;
+
+ @Autowired
+ private AuthorDao authorDao;
+
+ @BeforeEach
+ void init() {
+ mongoTemplate.save(getAuthor(ID_1));
+ mongoTemplate.save(getAuthor(ID_2));
+ mongoTemplate.save(getAuthor(ID_3));
+ }
+
+ @Test
+ @DisplayName("Получение автора по id")
+ void getById() {
+ Optional author = authorDao.findById(ID_1);
+
+ assertThat(author).isNotEmpty();
+ assertThat(author).contains(getAuthor(ID_1));
+ }
+
+ @Test
+ @DisplayName("Получение всех авторов")
+ void getAllObjects() {
+ List authors = authorDao.findAll();
+
+ assertThat(authors).size().isEqualTo(OBJECT_COUNT_3);
+ assertThat(authors).containsAll(getAuthorList());
+ }
+
+ @Test
+ @DisplayName("Добавление автора")
+ void addObject() {
+ Author author = authorDao.save(getAuthor(ID_4));
+ Optional author_selected = authorDao.findById(author.getId());
+
+ assertThat(author_selected).isNotEmpty();
+ assertThat(author_selected).contains(getAuthor(ID_4));
+ }
+
+ @Test
+ @DisplayName("Удаление автора")
+ @Transactional(propagation = Propagation.NOT_SUPPORTED)
+ void deleteObject() {
+ assertThat(authorDao.count()).isEqualTo(OBJECT_COUNT_3);
+ authorDao.deleteById(ID_1);
+ assertThat(authorDao.count()).isEqualTo(OBJECT_COUNT_2);
+ }
+
+ @Test
+ @DisplayName("Обновление автора")
+ void updateObject() {
+ Author author = new Author(ID_1, TEST_SURNAME, TEST_FIRST_NAME, TEST_SECOND_NAME);
+ authorDao.save(author);
+ Optional author_selected = authorDao.findById(ID_1);
+
+ assertThat(author_selected).isNotEmpty();
+ assertThat(author_selected).contains(author);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/BookDaoTest.java b/src/test/java/ru/otus/mkulikov/app/dao/BookDaoTest.java
new file mode 100644
index 0000000..6695308
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/BookDaoTest.java
@@ -0,0 +1,156 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthor;
+import static ru.otus.mkulikov.generators.GenerateBook.getBook;
+import static ru.otus.mkulikov.generators.GenerateBook.getBooksList;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenre;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 28.05.2019
+ * Time: 13:16
+ */
+
+@DataMongoTest
+@DisplayName("Класс BookDao")
+class BookDaoTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+ private final String ID_3 = "3";
+ private final String ID_4 = "4";
+
+ private final int OBJECT_COUNT_1 = 1;
+ private final int OBJECT_COUNT_2 = 2;
+ private final int OBJECT_COUNT_3 = 3;
+
+ private final String DATE_TIME = "2019-01-01 10:01:01";
+
+ private final String UPDATED_BOOK_NAME = "UpdatedBookName";
+ private final String UPDATED_DESCRIPTION = "UpdatedDescription";
+
+ @Autowired
+ private MongoTemplate mongoTemplate;
+
+ @Autowired
+ private BookDao bookDaoJpa;
+
+ @Autowired
+ private AuthorDao authorDao;
+
+ @Autowired
+ private GenreDao genreDao;
+
+ @BeforeEach
+ void init() {
+ mongoTemplate.save(getAuthor(ID_1));
+ mongoTemplate.save(getAuthor(ID_2));
+ mongoTemplate.save(getAuthor(ID_3));
+
+ mongoTemplate.save(getGenre(ID_1));
+ mongoTemplate.save(getGenre(ID_2));
+ mongoTemplate.save(getGenre(ID_3));
+
+ mongoTemplate.save(getBook(ID_1));
+ mongoTemplate.save(getBook(ID_2));
+ mongoTemplate.save(getBook(ID_3));
+ }
+
+ @Test
+ @DisplayName("Получение книги по id")
+ void getById() {
+ Optional book = bookDaoJpa.findById(ID_1);
+
+ assertThat(book).isNotEmpty();
+ assertThat(book).contains(getBook(ID_1));
+ }
+
+ @Test
+ @DisplayName("Получение книг по id автора")
+ void getByAuthorId() {
+ List books = bookDaoJpa.findByAuthor_Id(ID_1);
+
+ assertThat(books).isNotEmpty();
+ assertThat(books).size().isEqualTo(OBJECT_COUNT_1);
+ }
+
+ @Test
+ @DisplayName("Получение книг по id жанра")
+ void getByGenreId() {
+ List books = bookDaoJpa.findByGenre_Id(ID_1);
+
+ assertThat(books).isNotEmpty();
+ assertThat(books).size().isEqualTo(OBJECT_COUNT_2);
+ }
+
+ @Test
+ @DisplayName("Получение всех книг")
+ void getAllObjects() {
+ List books = bookDaoJpa.findAll();
+
+ assertThat(books).size().isEqualTo(OBJECT_COUNT_3);
+ assertThat(books).containsAll(getBooksList());
+ }
+
+ @Test
+ @DisplayName("Добавление книги")
+ void addObject() {
+ Book book = getNewBook(ID_4);
+
+ bookDaoJpa.save(book);
+ Optional book_selected = bookDaoJpa.findById(ID_4);
+
+ assertThat(book_selected).isNotEmpty();
+ assertThat(book_selected).contains(book);
+ }
+
+ @Test
+ @DisplayName("Удаление книги по id")
+ void deleteObject() {
+ bookDaoJpa.deleteById(ID_1);
+ Optional book = bookDaoJpa.findById(ID_1);
+ assertThat(book).isEmpty();
+ }
+
+ @Test
+ @DisplayName("Обновление книги")
+ void updateObject() {
+ Book book = getUpdatedBook();
+
+ bookDaoJpa.save(book);
+ Optional book_selected = bookDaoJpa.findById(ID_1);
+
+ assertThat(book_selected).isNotEmpty();
+ assertThat(book_selected).contains(book);
+ }
+
+ private Book getUpdatedBook() {
+ Author author = authorDao.findById(ID_1).orElse(null);
+ Genre genre = genreDao.findById(ID_1).orElse(null);
+ return new Book(ID_1, new Date(), UPDATED_BOOK_NAME, author, genre, UPDATED_DESCRIPTION);
+ }
+
+ private Book getNewBook(String id) {
+ Author author = authorDao.findById(ID_1).orElse(null);
+ Genre genre = genreDao.findById(ID_1).orElse(null);
+ Date date = DateUtil.stringToDateTime(DATE_TIME);
+ return new Book(id, date, UPDATED_BOOK_NAME, author, genre, UPDATED_DESCRIPTION);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/CommentDaoTest.java b/src/test/java/ru/otus/mkulikov/app/dao/CommentDaoTest.java
new file mode 100644
index 0000000..cfa7b0c
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/CommentDaoTest.java
@@ -0,0 +1,149 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthor;
+import static ru.otus.mkulikov.generators.GenerateBook.getBook;
+import static ru.otus.mkulikov.generators.GenerateComment.getComment;
+import static ru.otus.mkulikov.generators.GenerateComment.getCommentList;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenre;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 28.05.2019
+ * Time: 9:45
+ */
+
+@DataMongoTest
+@DisplayName("Класс CommentDao")
+class CommentDaoTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+ private final String ID_3 = "3";
+ private final String ID_4 = "4";
+ private final String ID_5 = "5";
+
+ private final int OBJECT_COUNT_4 = 4;
+ private final int OBJECT_COUNT_2 = 2;
+
+ private final String DATE_TIME = "2019-01-01 10:01:01";
+
+ private final String COMMENT_USER_NAME = "user";
+ private final String COMMENT_UPDATED_USER_NAME = "TestUser";
+ private final String COMMENT_TEXT = "text";
+ private final String COMMENT_UPDATED_TEXT = "TestText";
+
+ @Autowired
+ private MongoTemplate mongoTemplate;
+
+ @Autowired
+ private BookDao bookDao;
+
+ @Autowired
+ private CommentDao commentDao;
+
+ @BeforeEach
+ void init() {
+ mongoTemplate.save(getAuthor(ID_1));
+ mongoTemplate.save(getAuthor(ID_2));
+ mongoTemplate.save(getAuthor(ID_3));
+
+ mongoTemplate.save(getGenre(ID_1));
+ mongoTemplate.save(getGenre(ID_2));
+ mongoTemplate.save(getGenre(ID_3));
+
+ mongoTemplate.save(getBook(ID_1));
+ mongoTemplate.save(getBook(ID_2));
+
+ mongoTemplate.save(getComment(ID_1, ID_1));
+ mongoTemplate.save(getComment(ID_2, ID_1));
+ mongoTemplate.save(getComment(ID_3, ID_2));
+ mongoTemplate.save(getComment(ID_4, ID_2));
+ }
+
+ @Test
+ @DisplayName("Получение комментария по id")
+ void getById() {
+ Optional comment = commentDao.findById(ID_1);
+
+ assertThat(comment).isNotEmpty();
+ assertThat(comment).contains(getComment(ID_1, ID_1));
+ }
+
+ @Test
+ @DisplayName("Получение всех комментариев")
+ void getAllObjects() {
+ List comments = commentDao.findAll();
+
+ assertThat(comments).isNotEmpty();
+ assertThat(comments).size().isEqualTo(OBJECT_COUNT_4);
+ assertThat(comments).containsAll(getCommentList());
+ }
+
+ @Test
+ @DisplayName("Получение всех комментариев для книги")
+ void getObjectsByBook() {
+ Optional book = bookDao.findById(ID_1);
+ List comments = commentDao.findByBook(book.orElse(null));
+
+ assertThat(comments).isNotEmpty();
+ assertThat(comments).size().isEqualTo(OBJECT_COUNT_2);
+ assertThat(comments).contains(getComment(ID_1, ID_1), getComment(ID_2, ID_1));
+ }
+
+ @Test
+ @DisplayName("Добавление комментария")
+ void addObject() {
+ Date date = DateUtil.stringToDateTime(DATE_TIME);
+ Book book = bookDao.findById(ID_1).orElse(null);
+ Comment comment = new Comment(ID_5, book, date, COMMENT_USER_NAME + ID_5, COMMENT_TEXT + ID_5);
+ commentDao.save(comment);
+
+ Optional comment_selected = commentDao.findById(ID_5);
+
+ assertThat(comment_selected).isNotEmpty();
+ assertThat(comment_selected).contains(comment);
+ }
+
+ @Test
+ @DisplayName("Удаление комментария")
+ void deleteObject() {
+ commentDao.deleteById(ID_1);
+ Optional comment = commentDao.findById(ID_1);
+ assertThat(comment).isEmpty();
+ }
+
+ @Test
+ @DisplayName("Обновление комментария")
+ void updateObject() {
+ Comment comment = getUpdatedComment();
+ commentDao.save(comment);
+ Optional comment_selected = commentDao.findById(ID_1);
+
+ assertThat(comment_selected).isNotEmpty();
+ assertThat(comment_selected).contains(comment);
+ }
+
+ private Comment getUpdatedComment() {
+ Comment comment = commentDao.findById(ID_1).orElse(null);
+ comment.setAddRecordDate(new Date());
+ comment.setUserName(COMMENT_UPDATED_USER_NAME);
+ comment.setText(COMMENT_UPDATED_TEXT);
+ return comment;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/dao/GenreDaoTest.java b/src/test/java/ru/otus/mkulikov/app/dao/GenreDaoTest.java
new file mode 100644
index 0000000..f5aa9dc
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/dao/GenreDaoTest.java
@@ -0,0 +1,103 @@
+package ru.otus.mkulikov.app.dao;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenre;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenreList;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 28.05.2019
+ * Time: 9:45
+ */
+
+@DataMongoTest
+@DisplayName("Класс GenreDao")
+class GenreDaoTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+ private final String ID_3 = "3";
+ private final String ID_4 = "4";
+
+ private final int OBJECT_COUNT_3 = 3;
+ private final int OBJECT_COUNT_2 = 2;
+
+ private final String UPDATED_NAME = "UpdatedName";
+
+ @Autowired
+ private MongoTemplate mongoTemplate;
+
+ @Autowired
+ private GenreDao genreDao;
+
+ @BeforeEach
+ void init() {
+ mongoTemplate.save(getGenre(ID_1));
+ mongoTemplate.save(getGenre(ID_2));
+ mongoTemplate.save(getGenre(ID_3));
+ }
+
+ @Test
+ @DisplayName("Получение жанра по id")
+ void getById() {
+ Optional genre = genreDao.findById(ID_1);
+
+ assertThat(genre).isNotEmpty();
+ assertThat(genre).contains(getGenre(ID_1));
+ }
+
+ @Test
+ @DisplayName("Получение всех жанров")
+ void getAllObjects() {
+ List genres = genreDao.findAll();
+
+ assertThat(genres).isNotEmpty();
+ assertThat(genres).size().isEqualTo(OBJECT_COUNT_3);
+ assertThat(genres).containsAll(getGenreList());
+ }
+
+ @Test
+ @DisplayName("Добавление жанра")
+ void addObject() {
+ Genre genre = genreDao.save(getGenre(ID_4));
+ Optional genre_selected = genreDao.findById(genre.getId());
+
+ assertThat(genre_selected).isNotEmpty();
+ assertThat(genre_selected).contains(genre);
+ }
+
+ @Test
+ @DisplayName("Удаление жанра")
+ @Transactional(propagation = Propagation.NOT_SUPPORTED)
+ void deleteObject() {
+ assertThat(genreDao.count()).isEqualTo(OBJECT_COUNT_3);
+ genreDao.deleteById(ID_1);
+ assertThat(genreDao.count()).isEqualTo(OBJECT_COUNT_2);
+ }
+
+ @Test
+ @DisplayName("Обновление жанра")
+ void updateObject() {
+ Genre genre = new Genre(ID_1, UPDATED_NAME);
+
+ genreDao.save(genre);
+ Optional genre_updated = genreDao.findById(ID_1);
+
+ assertThat(genre_updated).isNotEmpty();
+ assertThat(genre_updated).contains(genre);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/model/DateUtilTest.java b/src/test/java/ru/otus/mkulikov/app/model/DateUtilTest.java
new file mode 100644
index 0000000..62f8d72
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/model/DateUtilTest.java
@@ -0,0 +1,53 @@
+package ru.otus.mkulikov.app.model;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 29.05.2019
+ * Time: 11:39
+ */
+
+@DisplayName("Класс DateUtil")
+class DateUtilTest {
+
+ private final String DATE_PATTERN = "yyyy-MM-dd";
+ private final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+ private final String DATE_TIME = "2019-01-01 10:01:01";
+
+ @Test
+ @DisplayName("Перевод даты в строку, формат даты '" + DATE_PATTERN + "'")
+ void dateToString() {
+ Date date = new Date();
+
+ DateFormat dateFormat = new SimpleDateFormat(DATE_PATTERN);
+ assertEquals(dateFormat.format(date), DateUtil.dateToString(date));
+ }
+
+ @Test
+ @DisplayName("Перевод даты в строку, формат даты '" + DATETIME_PATTERN + "'")
+ void dateTimeToString() {
+ Date date = new Date();
+
+ DateFormat dateFormat = new SimpleDateFormat(DATETIME_PATTERN);
+ assertEquals(dateFormat.format(date), DateUtil.dateTimeToString(date));
+ }
+
+ @Test
+ @DisplayName("Перевод строки в дату, формат даты '" + DATETIME_PATTERN + "'")
+ void stringToDateTime() {
+ Date date = DateUtil.stringToDateTime(DATE_TIME);
+ DateFormat dateFormat = new SimpleDateFormat(DATETIME_PATTERN);
+
+ assertEquals(DATE_TIME, dateFormat.format(date));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/service/AuthorManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/AuthorManageSeviceImplTest.java
new file mode 100644
index 0000000..818a794
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/AuthorManageSeviceImplTest.java
@@ -0,0 +1,145 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.stubbing.Answer;
+import org.springframework.dao.DataIntegrityViolationException;
+import ru.otus.mkulikov.app.dao.AuthorDao;
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthor;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthorList;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:59
+ */
+
+@DisplayName("Класс AuthorManageSevice")
+@ExtendWith(MockitoExtension.class)
+class AuthorManageSeviceImplTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+
+ private final int OBJECT_COUNT_3 = 3;
+
+ private final String SURNAME = "Surname1";
+ private final String FIRST_NAME = "FirstName1";
+ private final String SECOND_NAME = "SecondName1";
+
+ @Mock
+ private AuthorDao authorDao;
+
+ @InjectMocks
+ private AuthorManageServiceImpl authorManageService;
+
+ @Test
+ @DisplayName("Получение автора по id")
+ void getAuthorById() {
+ when(authorDao.findById(anyString())).thenReturn(Optional.of(getAuthor(ID_1)));
+ Author author = authorManageService.getAuthorById(ID_1);
+
+ assertThat(author).isNotNull();
+ assertThat(author).isEqualTo(getAuthor(ID_1));
+ }
+
+ @Test
+ @DisplayName("Получение всех авторов")
+ void getAuthors() {
+ when(authorDao.findAll()).thenReturn(getAuthorList());
+ List authors = authorManageService.getAuthors();
+
+ assertThat(authors).isNotNull();
+ assertThat(authors).hasSize(OBJECT_COUNT_3);
+ assertThat(authors).containsAll(getAuthorList());
+ }
+
+ @Test
+ @DisplayName("Добавление автора")
+ void addAuthor() {
+ when(authorDao.save(any(Author.class))).then(new Answer() {
+ @Override
+ public Author answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Author author = (Author) invocationOnMock.getArgument(0);
+ author.setId("2");
+ return author;
+ }
+ });
+ when(authorDao.findById(anyString())).thenReturn(Optional.of(getAuthor(ID_2)));
+
+ Author author1 = authorManageService.addAuthor(SURNAME, FIRST_NAME, SECOND_NAME);
+ Author author2 = authorManageService.getAuthorById(author1.getId());
+
+ assertThat(author1.getId()).isEqualTo(ID_2);
+ assertThat(author2).isNotNull();
+ assertThat(author2).isEqualTo(getAuthor(ID_2));
+ }
+
+ @Test
+ @DisplayName("Обновление автора")
+ void updateAuthor() {
+ when(authorDao.save(any(Author.class))).then(new Answer() {
+
+ @Override
+ public Author answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Author author = (Author) invocationOnMock.getArgument(0);
+ author.setId(ID_1);
+ return author;
+ }
+ });
+ when(authorDao.findById(anyString())).thenReturn(Optional.of(getAuthor(ID_1)));
+
+ Author author1 = authorManageService.updateAuthor(ID_1, SURNAME, FIRST_NAME, SECOND_NAME);
+ Author author2 = authorManageService.getAuthorById(ID_1);
+
+ assertThat(author1).isNotNull();
+ assertThat(author2).isEqualTo(getAuthor(ID_1));
+ }
+
+ @Test
+ @DisplayName("Обновление автора объектом")
+ void updateAuthorByObject() {
+ when(authorDao.save(any(Author.class))).then(new Answer() {
+
+ @Override
+ public Author answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Author author = (Author) invocationOnMock.getArgument(0);
+ author.setId(ID_1);
+ return author;
+ }
+ });
+ when(authorDao.findById(anyString())).thenReturn(Optional.of(getAuthor(ID_1)));
+
+ Author author1 = authorManageService.updateAuthor(getAuthor(ID_1));
+ Author author2 = authorManageService.getAuthorById(ID_1);
+
+ assertThat(author1).isNotNull();
+ assertThat(author2).isEqualTo(getAuthor(ID_1));
+ }
+
+ @Test
+ @DisplayName("Удаление автора, который используется в таблице книг")
+ void deleteAuthor() {
+ doThrow(DataIntegrityViolationException.class).when(authorDao).deleteById(ID_1);
+ assertThrows(DataIntegrityViolationException.class, () -> {
+ authorManageService.deleteAuthor(ID_1);
+ });
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/app/service/BookManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/BookManageSeviceImplTest.java
new file mode 100644
index 0000000..b5e416a
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/BookManageSeviceImplTest.java
@@ -0,0 +1,187 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.stubbing.Answer;
+import org.springframework.dao.DataIntegrityViolationException;
+import ru.otus.mkulikov.app.dao.AuthorDao;
+import ru.otus.mkulikov.app.dao.BookDao;
+import ru.otus.mkulikov.app.dao.GenreDao;
+import ru.otus.mkulikov.app.model.Book;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthor;
+import static ru.otus.mkulikov.generators.GenerateBook.getBook;
+import static ru.otus.mkulikov.generators.GenerateBook.getBooksList;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenre;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 29.05.2019
+ * Time: 13:40
+ */
+
+@DisplayName("Класс BookManageSevice")
+@ExtendWith(MockitoExtension.class)
+class BookManageSeviceImplTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+
+ private final int OBJECT_COUNT_1 = 1;
+ private final int OBJECT_COUNT_3 = 3;
+
+ private final String BOOK_NAME = "BookName";
+ private final String BOOK_DESCRIPTION = "Description";
+
+ @Mock
+ private BookDao bookDao;
+
+ @Mock
+ private AuthorDao authorDao;
+
+ @Mock
+ private GenreDao genreDao;
+
+ @InjectMocks
+ private BookManageServiceImpl booksManageSevice;
+
+ @Test
+ @DisplayName("Получение книги по id")
+ void getBookById() {
+ Book book = getBook(ID_1);
+ when(bookDao.findById(anyString())).thenReturn(Optional.of(book));
+ Book bookById = booksManageSevice.getBookById(ID_1);
+
+ assertThat(bookById).isNotNull();
+ assertThat(bookById).isEqualTo(book);
+ }
+
+ @Test
+ @DisplayName("Получение всех книг")
+ void getBooks() {
+ List list = getBooksList();
+ when(bookDao.findAll()).thenReturn(list);
+ List books = booksManageSevice.getBooks();
+
+ assertThat(books).isNotNull();
+ assertThat(books).hasSize(OBJECT_COUNT_3);
+ assertThat(books).containsAll(list);
+ }
+
+ @Test
+ @DisplayName("Получение книг по id автора")
+ void getBooksByAuthorId() {
+ Book book = getBook(ID_1);
+
+ List list = new ArrayList<>();
+ list.add(book);
+
+ when(bookDao.findByAuthor_Id(anyString())).thenReturn(list);
+ List books = booksManageSevice.getBookByAuthorId(ID_1);
+
+ assertThat(books).isNotNull();
+ assertThat(books).hasSize(OBJECT_COUNT_1);
+ assertThat(books).contains(book);
+ }
+
+ @Test
+ @DisplayName("Получение книг по id жанра")
+ void getBooksByGenreId() {
+ Book book = getBook(ID_1);
+
+ List list = new ArrayList<>();
+ list.add(book);
+
+ when(bookDao.findByGenre_Id(anyString())).thenReturn(list);
+ List books = booksManageSevice.getBookByGenreId(ID_1);
+
+ assertThat(books).isNotNull();
+ assertThat(books).hasSize(OBJECT_COUNT_1);
+ assertThat(books).contains(book);
+ }
+
+ @Test
+ @DisplayName("Добавление книги")
+ void addBook() {
+ when(bookDao.save(any(Book.class))).then(new Answer() {
+ @Override
+ public Book answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Book book = (Book) invocationOnMock.getArgument(0);
+ book.setId("2");
+ return book;
+ }
+ });
+ when(authorDao.findById(anyString())).thenReturn(Optional.of(getAuthor(ID_1)));
+ when(genreDao.findById(anyString())).thenReturn(Optional.of(getGenre(ID_1)));
+
+ Book book = booksManageSevice.addBook(BOOK_NAME, ID_1, ID_1, BOOK_DESCRIPTION);
+
+ assertThat(book).isNotNull();
+ assertThat(book.getId()).isEqualTo(ID_2);
+ }
+
+ @Test
+ @DisplayName("Обновление книги")
+ void updateBook() {
+ when(bookDao.save(any(Book.class))).then(new Answer() {
+
+ @Override
+ public Book answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Book book = (Book) invocationOnMock.getArgument(0);
+ book.setId(ID_1);
+ return book;
+ }
+ });
+ when(authorDao.findById(anyString())).thenReturn(Optional.of(getAuthor(ID_1)));
+ when(genreDao.findById(anyString())).thenReturn(Optional.of(getGenre(ID_1)));
+ when(bookDao.findById(anyString())).thenReturn(Optional.of(getBook(ID_1)));
+
+ Book book = booksManageSevice.updateBook(ID_1, BOOK_NAME, ID_1, ID_1, BOOK_DESCRIPTION);
+
+ assertThat(book).isNotNull();
+ assertThat(book.getId()).isEqualTo(ID_1);
+ }
+
+ @Test
+ @DisplayName("Обновление книги объектом")
+ void updateBookByObject() {
+ when(bookDao.save(any(Book.class))).then(new Answer() {
+
+ @Override
+ public Book answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Book book = (Book) invocationOnMock.getArgument(0);
+ book.setId(ID_1);
+ return book;
+ }
+ });
+ Book book = booksManageSevice.updateBook(getBook(ID_1));
+
+ assertThat(book).isNotNull();
+ assertThat(book.getId()).isEqualTo(ID_1);
+ }
+
+ @Test
+ @DisplayName("Удаление книги по id")
+ void deleteBook() {
+ doThrow(DataIntegrityViolationException.class).when(bookDao).deleteById(ID_1);
+ assertThrows(DataIntegrityViolationException.class, () -> {
+ booksManageSevice.deleteBook(ID_1);
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ru/otus/mkulikov/app/service/CommentManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/CommentManageSeviceImplTest.java
new file mode 100644
index 0000000..e1d6775
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/CommentManageSeviceImplTest.java
@@ -0,0 +1,165 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.stubbing.Answer;
+import org.springframework.dao.DataIntegrityViolationException;
+import ru.otus.mkulikov.app.dao.BookDao;
+import ru.otus.mkulikov.app.dao.CommentDao;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static ru.otus.mkulikov.generators.GenerateBook.getBook;
+import static ru.otus.mkulikov.generators.GenerateComment.getComment;
+import static ru.otus.mkulikov.generators.GenerateComment.getCommentList;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:56
+ */
+
+@DisplayName("Класс CommentManageSevice")
+@ExtendWith(MockitoExtension.class)
+class CommentManageSeviceImplTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+
+ private final int OBJECT_COUNT_4 = 4;
+
+ private final String COMMENT_USER_NAME = "user";
+ private final String COMMENT_UPDATED_USER_NAME = "TestUser";
+ private final String COMMENT_TEXT = "text";
+ private final String COMMENT_UPDATED_TEXT = "TestText";
+
+ @Mock
+ private CommentDao commentDao;
+
+ @Mock
+ private BookDao bookDao;
+
+ @InjectMocks
+ private CommentManageServiceImpl commentManageService;
+
+ @Test
+ @DisplayName("Получение комментария по id")
+ void getCommentById() {
+ Comment comment = getComment(ID_1, ID_1);
+ when(commentDao.findById(anyString())).thenReturn(Optional.of(comment));
+ Comment commentById = commentManageService.getCommentById(ID_1);
+
+ assertThat(commentById).isNotNull();
+ assertThat(commentById).isEqualTo(comment);
+ }
+
+ @Test
+ @DisplayName("Получение всех комментариев")
+ void getComments() {
+ List commentsList = getCommentList();
+ when(commentDao.findAll()).thenReturn(commentsList);
+ List comments = commentManageService.getComments();
+
+ assertThat(comments).isNotNull();
+ assertThat(comments).hasSize(OBJECT_COUNT_4);
+ assertThat(comments).containsAll(commentsList);
+ }
+
+ @Test
+ @DisplayName("Добавление комментария")
+ void addComment() {
+ Book book = getBook(ID_1);
+
+ when(commentDao.save(any(Comment.class))).then(new Answer() {
+ @Override
+ public Comment answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Comment comment = (Comment) invocationOnMock.getArgument(0);
+ comment.setId("2");
+ return comment;
+ }
+ });
+ when(bookDao.findById(anyString())).thenReturn(Optional.of(book));
+
+ Comment comment = commentManageService.addComment(ID_1, COMMENT_USER_NAME, COMMENT_TEXT);
+
+ assertThat(comment).isNotNull();
+ assertThat(comment.getId()).isEqualTo(ID_2);
+ }
+
+ @Test
+ @DisplayName("Получение комментариев по Id книги")
+ void getCommentsByBookId() {
+ List commentList = getCommentList();
+ Optional book = Optional.of(getBook(ID_1));
+
+ when(bookDao.findById(anyString())).thenReturn(book);
+ when(commentDao.findByBook(book.orElse(null))).thenReturn(commentList);
+
+ List comments = commentManageService.getCommentsByBookId(ID_1);
+
+ assertThat(comments).isNotNull();
+ assertThat(comments).isEqualTo(commentList);
+ }
+
+ @Test
+ @DisplayName("Обновление комментария")
+ void updateComment() {
+ when(commentDao.save(any(Comment.class))).then(new Answer() {
+
+ @Override
+ public Comment answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Comment comment = (Comment) invocationOnMock.getArgument(0);
+ comment.setId(ID_1);
+ return comment;
+ }
+ });
+ when(commentDao.findById(anyString())).thenReturn(Optional.of(getComment(ID_1, ID_1)));
+
+ Comment comment = commentManageService.updateComment(ID_1, COMMENT_UPDATED_USER_NAME, COMMENT_UPDATED_TEXT);
+
+ assertThat(comment).isNotNull();
+ assertThat(comment.getId()).isEqualTo(ID_1);
+ }
+
+ @Test
+ @DisplayName("Обновление комментария объектом")
+ void updateCommentByObject() {
+ when(commentDao.save(any(Comment.class))).then(new Answer() {
+
+ @Override
+ public Comment answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Comment comment = (Comment) invocationOnMock.getArgument(0);
+ comment.setId(ID_1);
+ return comment;
+ }
+ });
+ Comment comment = commentManageService.updateComment(getComment(ID_1, ID_1));
+
+ assertThat(comment).isNotNull();
+ assertThat(comment.getId()).isEqualTo(ID_1);
+ }
+
+ @Test
+ @DisplayName("Удаление комментария")
+ void deleteComment() {
+ doThrow(DataIntegrityViolationException.class).when(commentDao).deleteById(ID_1);
+ assertThrows(DataIntegrityViolationException.class, () -> {
+ commentManageService.deleteComment(ID_1);
+ });
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/app/service/GenreManageSeviceImplTest.java b/src/test/java/ru/otus/mkulikov/app/service/GenreManageSeviceImplTest.java
new file mode 100644
index 0000000..9a4572a
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/app/service/GenreManageSeviceImplTest.java
@@ -0,0 +1,138 @@
+package ru.otus.mkulikov.app.service;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.stubbing.Answer;
+import org.springframework.dao.DataIntegrityViolationException;
+import ru.otus.mkulikov.app.dao.GenreDao;
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenre;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenreList;
+
+/**
+ * Created by IntelliJ IDEA.
+ * Developer: Maksim Kulikov
+ * Date: 30.05.2019
+ * Time: 15:56
+ */
+
+@DisplayName("Класс GenreManageSevice")
+@ExtendWith(MockitoExtension.class)
+class GenreManageSeviceImplTest {
+
+ private final String ID_1 = "1";
+ private final String ID_2 = "2";
+
+ private final int OBJECT_COUNT_3 = 3;
+
+ private final String GENRE_UPDATED_NAME = "UpdatedName";
+ private final String GENRE_NAME = "GenreName";
+
+ @Mock
+ private GenreDao genreDao;
+
+ @InjectMocks
+ private GenreManageServiceImpl genreManageService;
+
+ @Test
+ @DisplayName("Получение жанра по id")
+ void getGenreById() {
+ when(genreDao.findById(anyString())).thenReturn(Optional.of(getGenre(ID_1)));
+ Genre genre = genreManageService.getGenreById(ID_1);
+
+ assertThat(genre).isNotNull();
+ assertThat(genre).isEqualTo(getGenre(ID_1));
+ }
+
+ @Test
+ @DisplayName("Получение всех жанров")
+ void getGenres() {
+ when(genreDao.findAll()).thenReturn(getGenreList());
+ List authors = genreManageService.getGenres();
+
+ assertThat(authors).isNotNull();
+ assertThat(authors).hasSize(OBJECT_COUNT_3);
+ assertThat(authors).containsAll(getGenreList());
+ }
+
+ @Test
+ @DisplayName("Добавление жанра")
+ void addGenre() {
+ when(genreDao.save(any(Genre.class))).then(new Answer() {
+ @Override
+ public Genre answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Genre genre = (Genre) invocationOnMock.getArgument(0);
+ genre.setId(ID_2);
+ return genre;
+ }
+ });
+
+ Genre genre = genreManageService.addGenre(GENRE_NAME);
+
+ assertThat(genre).isNotNull();
+ assertThat(genre.getId()).isEqualTo(ID_2);
+ }
+
+ @Test
+ @DisplayName("Обновление жанра")
+ void updateGenre() {
+ when(genreDao.save(any(Genre.class))).then(new Answer() {
+
+ @Override
+ public Genre answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Genre genre = (Genre) invocationOnMock.getArgument(0);
+ genre.setId(ID_1);
+ return genre;
+ }
+ });
+ when(genreDao.findById(anyString())).thenReturn(Optional.of(getGenre(ID_1)));
+
+ Genre genre = genreManageService.updateGenre(ID_1, GENRE_UPDATED_NAME);
+
+ assertThat(genre).isNotNull();
+ assertThat(genre.getId()).isEqualTo(ID_1);
+ }
+
+ @Test
+ @DisplayName("Обновление жанра объектом")
+ void updateGenreByObject() {
+ when(genreDao.save(any(Genre.class))).then(new Answer() {
+
+ @Override
+ public Genre answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Genre genre = (Genre) invocationOnMock.getArgument(0);
+ genre.setId(ID_1);
+ return genre;
+ }
+ });
+ Genre genre = genreManageService.updateGenre(getGenre(ID_1));
+
+ assertThat(genre).isNotNull();
+ assertThat(genre.getId()).isEqualTo(ID_1);
+ }
+
+ @Test
+ @DisplayName("Удаление жанра, который используется в таблице книг")
+ void deleteGenre() {
+ doThrow(DataIntegrityViolationException.class).when(genreDao).deleteById(ID_1);
+ assertThrows(DataIntegrityViolationException.class, () -> {
+ genreManageService.deleteGenre(ID_1);
+ });
+ }
+
+}
diff --git a/src/test/java/ru/otus/mkulikov/generators/GenerateAuthor.java b/src/test/java/ru/otus/mkulikov/generators/GenerateAuthor.java
new file mode 100644
index 0000000..53adc55
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/generators/GenerateAuthor.java
@@ -0,0 +1,29 @@
+package ru.otus.mkulikov.generators;
+
+import ru.otus.mkulikov.app.model.Author;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GenerateAuthor {
+
+ private static final String ID_1 = "1";
+ private static final String ID_2 = "2";
+ private static final String ID_3 = "3";
+
+ private static final String SURNAME = "Surname";
+ private static final String FIRST_NAME = "FirstName";
+ private static final String SECOND_NAME = "SecondName";
+
+ public static List getAuthorList() {
+ List authors = new ArrayList();
+ authors.add(getAuthor(ID_1));
+ authors.add(getAuthor(ID_2));
+ authors.add(getAuthor(ID_3));
+ return authors;
+ }
+
+ public static Author getAuthor(String id) {
+ return new Author(id, SURNAME + id, FIRST_NAME + id, SECOND_NAME + id);
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/generators/GenerateBook.java b/src/test/java/ru/otus/mkulikov/generators/GenerateBook.java
new file mode 100644
index 0000000..6e56762
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/generators/GenerateBook.java
@@ -0,0 +1,40 @@
+package ru.otus.mkulikov.generators;
+
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthor;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenre;
+
+public class GenerateBook {
+
+ private static final String ID_1 = "1";
+ private static final String ID_2 = "2";
+ private static final String ID_3 = "3";
+
+ private static final String DATE_TIME = "2019-01-01 10:01:01";
+
+ private static final String BOOK_NAME = "BookName";
+ private static final String BOOK_DESCRIPTION = "Description";
+
+ public static Book getBook(String id) {
+ Author author = getAuthor(id);
+ Genre genre = getGenre(id);
+ Date date = DateUtil.stringToDateTime(DATE_TIME);
+ return new Book(id, date, BOOK_NAME, author, genre, BOOK_DESCRIPTION);
+ }
+
+ public static List getBooksList() {
+ List list = new ArrayList<>();
+ list.add(getBook(ID_1));
+ list.add(getBook(ID_2));
+ list.add(getBook(ID_3));
+ return list;
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/generators/GenerateComment.java b/src/test/java/ru/otus/mkulikov/generators/GenerateComment.java
new file mode 100644
index 0000000..960b0d0
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/generators/GenerateComment.java
@@ -0,0 +1,46 @@
+package ru.otus.mkulikov.generators;
+
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.utils.DateUtil;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static ru.otus.mkulikov.generators.GenerateBook.getBook;
+
+public class GenerateComment {
+
+ private static final String ID_1 = "1";
+ private static final String ID_2 = "2";
+ private static final String ID_3 = "3";
+ private static final String ID_4 = "4";
+
+ private static final String DATE_TIME = "2019-01-01 10:01:01";
+
+ private static final String COMMENT_USER_NAME = "user";
+ private static final String COMMENT_TEXT = "text";
+
+ public static Comment getComment(String id, String bookId) {
+ Date date = DateUtil.stringToDateTime(DATE_TIME);
+ Book book = getBook(bookId);
+ return new Comment(id, book, date, COMMENT_USER_NAME + id, COMMENT_TEXT + id);
+ }
+
+ public static List getCommentList() {
+ List comments = new ArrayList();
+ comments.add(getComment(ID_1, ID_1));
+ comments.add(getComment(ID_2, ID_1));
+ comments.add(getComment(ID_3, ID_2));
+ comments.add(getComment(ID_4, ID_2));
+ return comments;
+ }
+
+ public static List getCommentList(String bookId) {
+ List comments = new ArrayList();
+ comments.add(getComment(ID_1, bookId));
+ comments.add(getComment(ID_2, bookId));
+ return comments;
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/generators/GenerateGenre.java b/src/test/java/ru/otus/mkulikov/generators/GenerateGenre.java
new file mode 100644
index 0000000..fb929cc
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/generators/GenerateGenre.java
@@ -0,0 +1,27 @@
+package ru.otus.mkulikov.generators;
+
+import ru.otus.mkulikov.app.model.Genre;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GenerateGenre {
+
+ private static final String ID_1 = "1";
+ private static final String ID_2 = "2";
+ private static final String ID_3 = "3";
+
+ private static final String GENRE_NAME = "GenreName";
+
+ public static List getGenreList() {
+ List genres = new ArrayList();
+ genres.add(getGenre(ID_1));
+ genres.add(getGenre(ID_2));
+ genres.add(getGenre(ID_3));
+ return genres;
+ }
+
+ public static Genre getGenre(String id) {
+ return new Genre(id, GENRE_NAME + id);
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/rest/AuthorControllerTest.java b/src/test/java/ru/otus/mkulikov/rest/AuthorControllerTest.java
new file mode 100644
index 0000000..d81fa6c
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/rest/AuthorControllerTest.java
@@ -0,0 +1,92 @@
+package ru.otus.mkulikov.rest;
+
+import org.junit.Test;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import ru.otus.mkulikov.app.model.Author;
+import ru.otus.mkulikov.app.service.AuthorManageService;
+import ru.otus.mkulikov.app.service.BookManageService;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthor;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthorList;
+import static ru.otus.mkulikov.generators.GenerateBook.getBooksList;
+
+@RunWith(SpringRunner.class)
+@DisplayName("Класс rest-контроллеров для автора")
+@WebMvcTest(AuthorController.class)
+public class AuthorControllerTest {
+
+ @MockBean
+ private AuthorManageService authorManageService;
+ @MockBean
+ private BookManageService bookManageService;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ @DisplayName("Получение всех авторов")
+ public void getAll() throws Exception {
+ when(authorManageService.getAuthors()).thenReturn(getAuthorList());
+
+ this.mockMvc.perform(get("/author/list"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Authors:")));
+ }
+
+ @Test
+ @DisplayName("Получение автора по id")
+ public void getAuthorById() throws Exception {
+ when(authorManageService.getAuthorById(anyString())).thenReturn(getAuthor("1"));
+
+ this.mockMvc.perform(get("/author").param("id", "1"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Author detail:")));
+ }
+
+ @Test
+ @DisplayName("Добавление автора")
+ public void addAuthor() throws Exception {
+ when(authorManageService.addAuthor(anyString(), anyString(), anyString())).thenReturn(getAuthor("1"));
+
+ this.mockMvc.perform(get("/author/new"))
+ .andDo(print())
+ .andExpect(redirectedUrl("/author?id=1"));
+ }
+
+ @Test
+ @DisplayName("Изменение автора")
+ public void edit() throws Exception {
+ when(authorManageService.updateAuthor(any(Author.class))).thenReturn(getAuthor("1"));
+
+ this.mockMvc.perform(post("/author/edit").requestAttr("Author", getAuthor("1")))
+ .andDo(print())
+ .andExpect(redirectedUrl("/author/list"));
+ }
+
+ @Test
+ @DisplayName("Удаление автора")
+ public void delete() throws Exception {
+ when(bookManageService.getBookByAuthorId(anyString())).thenReturn(getBooksList());
+ when(authorManageService.deleteAuthor(anyString())).thenReturn(1);
+
+ this.mockMvc.perform(post("/author/delete").param("id", "1"))
+ .andDo(print())
+ .andExpect(redirectedUrl("/author/list"));
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/rest/BookControllerTest.java b/src/test/java/ru/otus/mkulikov/rest/BookControllerTest.java
new file mode 100644
index 0000000..598bd55
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/rest/BookControllerTest.java
@@ -0,0 +1,103 @@
+package ru.otus.mkulikov.rest;
+
+import org.junit.Test;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import ru.otus.mkulikov.app.model.Book;
+import ru.otus.mkulikov.app.service.AuthorManageService;
+import ru.otus.mkulikov.app.service.BookManageService;
+import ru.otus.mkulikov.app.service.CommentManageService;
+import ru.otus.mkulikov.app.service.GenreManageService;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static ru.otus.mkulikov.generators.GenerateAuthor.getAuthorList;
+import static ru.otus.mkulikov.generators.GenerateBook.getBook;
+import static ru.otus.mkulikov.generators.GenerateComment.getCommentList;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenreList;
+
+@RunWith(SpringRunner.class)
+@DisplayName("Класс rest-контроллеров для книг")
+@WebMvcTest(BookController.class)
+public class BookControllerTest {
+
+ @MockBean
+ private BookManageService bookManageService;
+ @MockBean
+ private AuthorManageService authorManageService;
+ @MockBean
+ private GenreManageService genreManageService;
+ @MockBean
+ private CommentManageService commentManageService;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ @DisplayName("Получение всех книг")
+ public void getAll() throws Exception {
+ this.mockMvc.perform(get("/book/list"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Books:")));
+ }
+
+ @Test
+ @DisplayName("Получение книги по id")
+ public void getById() throws Exception {
+ when(bookManageService.getBookById(anyString())).thenReturn(getBook("1"));
+ when(authorManageService.getAuthors()).thenReturn(getAuthorList());
+ when(genreManageService.getGenres()).thenReturn(getGenreList());
+
+ this.mockMvc.perform(get("/book").param("id", "1"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Book detail:")));
+ }
+
+ @Test
+ @DisplayName("Добавление книги")
+ public void addBook() throws Exception {
+ when(bookManageService.addBook(anyString(), anyString(), anyString(), anyString())).thenReturn(getBook("1"));
+ when(authorManageService.getAuthors()).thenReturn(getAuthorList());
+ when(genreManageService.getGenres()).thenReturn(getGenreList());
+
+ this.mockMvc.perform(get("/book/new"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Book detail:")));
+ }
+
+ @Test
+ @DisplayName("Изменение книги")
+ public void edit() throws Exception {
+ when(bookManageService.updateBook(any(Book.class))).thenReturn(getBook("1"));
+
+ this.mockMvc.perform(post("/book/edit").requestAttr("Book", getBook("1")))
+ .andDo(print())
+ .andExpect(redirectedUrl("/book/list"));
+ }
+
+ @Test
+ @DisplayName("Удаление книги")
+ public void delete() throws Exception {
+ when(commentManageService.getCommentsByBookId(anyString())).thenReturn(getCommentList("1"));
+ when(commentManageService.deleteComments(getCommentList())).thenReturn(1);
+ when(bookManageService.deleteBook(anyString())).thenReturn(1);
+
+ this.mockMvc.perform(post("/book/delete").param("id", "1"))
+ .andDo(print())
+ .andExpect(redirectedUrl("/book/list"));
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/rest/CommentControllerTest.java b/src/test/java/ru/otus/mkulikov/rest/CommentControllerTest.java
new file mode 100644
index 0000000..6e0c084
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/rest/CommentControllerTest.java
@@ -0,0 +1,91 @@
+package ru.otus.mkulikov.rest;
+
+import org.junit.Test;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import ru.otus.mkulikov.app.model.Comment;
+import ru.otus.mkulikov.app.service.BookManageService;
+import ru.otus.mkulikov.app.service.CommentManageService;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static ru.otus.mkulikov.generators.GenerateBook.getBooksList;
+import static ru.otus.mkulikov.generators.GenerateComment.getComment;
+
+@RunWith(SpringRunner.class)
+@DisplayName("Класс rest-контроллеров для комментариев")
+@WebMvcTest(CommentController.class)
+public class CommentControllerTest {
+
+ @MockBean
+ private BookManageService bookManageService;
+ @MockBean
+ private CommentManageService commentManageService;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ @DisplayName("Получение всех комментариев")
+ public void getAll() throws Exception {
+ this.mockMvc.perform(get("/comment/list")).andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Comments:")));
+ }
+
+ @Test
+ @DisplayName("Получение комментария по id")
+ public void getById() throws Exception {
+ when(commentManageService.getCommentById(anyString())).thenReturn(getComment("1", "1"));
+ when(bookManageService.getBooks()).thenReturn(getBooksList());
+
+ this.mockMvc.perform(get("/comment").param("id", "1"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Comment detail:")));
+ }
+
+ @Test
+ @DisplayName("Изменение комментария")
+ public void edit() throws Exception {
+ when(bookManageService.getBooks()).thenReturn(getBooksList());
+ when(commentManageService.updateComment(any(Comment.class))).thenReturn(getComment("1", "1"));
+
+ this.mockMvc.perform(post("/comment/edit").requestAttr("Comment", getComment("1", "1")))
+ .andDo(print())
+ .andExpect(redirectedUrl("/comment/list"));
+ }
+
+ @Test
+ @DisplayName("Добавление комментария")
+ public void addComment() throws Exception {
+ when(bookManageService.getBooks()).thenReturn(getBooksList());
+ when(commentManageService.addComment(anyString(), anyString(), anyString())).thenReturn(getComment("1", "1"));
+
+ this.mockMvc.perform(get("/comment/new"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Comment detail:")));
+ }
+
+ @Test
+ @DisplayName("Удаление комментария")
+ public void delete() throws Exception {
+ when(commentManageService.deleteComment(anyString())).thenReturn(1);
+
+ this.mockMvc.perform(post("/comment/delete").param("id", "1"))
+ .andDo(print())
+ .andExpect(redirectedUrl("/comment/list"));
+ }
+}
diff --git a/src/test/java/ru/otus/mkulikov/rest/GenreControllerTest.java b/src/test/java/ru/otus/mkulikov/rest/GenreControllerTest.java
new file mode 100644
index 0000000..31e3f84
--- /dev/null
+++ b/src/test/java/ru/otus/mkulikov/rest/GenreControllerTest.java
@@ -0,0 +1,86 @@
+package ru.otus.mkulikov.rest;
+
+import org.junit.Test;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import ru.otus.mkulikov.app.model.Genre;
+import ru.otus.mkulikov.app.service.BookManageService;
+import ru.otus.mkulikov.app.service.GenreManageService;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static ru.otus.mkulikov.generators.GenerateGenre.getGenre;
+
+@RunWith(SpringRunner.class)
+@DisplayName("Класс rest-контроллеров для жанров")
+@WebMvcTest(GenreController.class)
+public class GenreControllerTest {
+
+ @MockBean
+ private BookManageService bookManageService;
+ @MockBean
+ private GenreManageService genreManageService;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ @DisplayName("Получение всех жанров")
+ public void getAll() throws Exception {
+ this.mockMvc.perform(get("/genre/list")).andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Genres:")));
+ }
+
+ @Test
+ @DisplayName("Получение жанра по id")
+ public void getById() throws Exception {
+ when(genreManageService.getGenreById(anyString())).thenReturn(getGenre("1"));
+
+ this.mockMvc.perform(get("/genre").param("id", "1"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("Genre detail:")));
+ }
+
+ @Test
+ @DisplayName("Добавление жанра")
+ public void addGenre() throws Exception {
+ when(genreManageService.addGenre(anyString())).thenReturn(getGenre("1"));
+
+ this.mockMvc.perform(get("/genre/new"))
+ .andDo(print())
+ .andExpect(redirectedUrl("/genre?id=1"));
+ }
+
+ @Test
+ @DisplayName("Изменение жанра")
+ public void edit() throws Exception {
+ when(genreManageService.updateGenre(any(Genre.class))).thenReturn(getGenre("1"));
+
+ this.mockMvc.perform(post("/genre/edit").requestAttr("Genre", getGenre("1")))
+ .andDo(print())
+ .andExpect(redirectedUrl("/genre/list"));
+ }
+
+ @Test
+ @DisplayName("Удаление жанра")
+ public void delete() throws Exception {
+ when(genreManageService.deleteGenre(anyString())).thenReturn(1);
+
+ this.mockMvc.perform(post("/genre/delete").param("id", "1"))
+ .andDo(print())
+ .andExpect(redirectedUrl("/genre/list"));
+ }
+}
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
new file mode 100644
index 0000000..a47425c
--- /dev/null
+++ b/src/test/resources/application.yml
@@ -0,0 +1,4 @@
+spring:
+ banner:
+ location: "classpath:banner.txt"
+
diff --git a/src/test/resources/banner-test.txt b/src/test/resources/banner-test.txt
new file mode 100644
index 0000000..50a692c
--- /dev/null
+++ b/src/test/resources/banner-test.txt
@@ -0,0 +1,11 @@
+ ___ ___ ___ ___ ___ ___ ___
+ /\__\ /\ \ /\__\ /\ \ /\ \ ___ / /\ / /\ ___
+ /:/ / /::\ \ /:/ / /::\ \ /::\ \ /__/\ / /::\ / /::\ /__/\
+ /:/ / /:/\:\ \ /:/ / /:/\:\ \ /:/\ \ \ \ \:\ / /:/\:\ /__/:/\:\ \ \:\
+ /:/ / ___ /:/ \:\ \ /:/ / /::\~\:\ \ _\:\~\ \ \ \__\:\ / /::\ \:\ _\_ \:\ \:\ \__\:\
+ /:/__/ /\__\ /:/__/ \:\__\ /:/__/ /:/\:\ \:\__\ /\ \:\ \ \__\ *** / /::\ /__/:/\:\ \:\ /__/\ \:\ \:\ / /::\
+ \:\ \ /:/ / \:\ \ /:/ / \:\ \ \:\~\:\ \/__/ \:\ \:\ \/__/ / /:/\:\ \ \:\ \:\_\/ \ \:\ \:\_\/ / /:/\:\
+ \:\ /:/ / \:\ /:/ / \:\ \ \:\ \:\__\ \:\ \:\__\ / /:/__\/ \ \:\ \:\ \ \:\_\:\ / /:/__\/
+ \:\/:/ / \:\/:/ / \:\ \ \:\ \/__/ \:\/:/ / /__/:/ \ \:\_\/ \ \:\/:/ /__/:/
+ \::/ / \::/ / \:\__\ \:\__\ \::/ / \__\/ \ \:\ \ \::/ \__\/
+ \/__/ \/__/ \/__/ \/__/ \/__/ \__\/ \__\/