diff --git a/README.md b/README.md index 118845c09..b7ea20b29 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/bee16f3145654047a0505c62aeefd8a2)](https://app.codacy.com/gh/JavaWebinar/topjava/dashboard) +[//]: # ([![Codacy Badge](https://app.codacy.com/project/badge/Grade/bee16f3145654047a0505c62aeefd8a2)](https://www.codacy.com/gh/JavaWebinar/topjava/dashboard)) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/bee16f3145654047a0505c62aeefd8a2)](https://www.codacy.com/gh/Gorassi/topjava/setting) + Java Enterprise Online Project =============================== @@ -10,13 +12,14 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - [Wiki](https://github.com/JavaOPs/topjava/wiki) - [Wiki Git](https://github.com/JavaOPs/topjava/wiki/Git) - [Wiki IDEA](https://github.com/JavaOPs/topjava/wiki/IDEA) -- [Демо разрабатываемого приложения](http://javaops-demo.ru/topjava) +- [Демо разрабатываемого приложения](http://topjava.herokuapp.com/) -### 25.09: Старт проекта -- Начало проверки [вступительного задания HW0](https://github.com/JavaOPs/topjava#-Домашнее-задание-hw0) +### 26.05: Старт проекта +- Начало проверки [вступительного задания](https://github.com/JavaOPs/topjava#-Домашнее-задание-hw0) -#### 30.09 Дедлайн на сдачу HW0 -### 02.10: 1-е занятие +#### 31.05 Дедлайн на сдачу HW0 +### 02.06: 1-е занятие +#### 03.06 Дедлайн подачи заявки на [дипломную программу](https://javaops.ru/view/register/diploma) - Разбор домашнего задания вступительного занятия (вместе с Optional) - Обзор используемых в проекте технологий. Интеграция ПО - Maven @@ -25,7 +28,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Уровни и зависимости логгирования. JMX - Домашнее задание 1-го занятия (HW1 + Optional) -### 09.10: 2-е занятие +### 09.06: 2-е занятие - Разбор домашнего задания HW1 + Optional - Библиотека vs Фреймворк. Стандартные библиотеки Apache Commons, Guava - Слои приложения. Создание каркаса приложения @@ -33,7 +36,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Пояснения к HW2. Обработка Autowired - Домашнее задание (HW2 + Optional) -### 16.10: 3-е занятие +### 16.06: 3-е занятие - Разбор домашнего задания HW2 + Optional - Жизненный цикл Spring контекста - Тестирование через JUnit @@ -46,7 +49,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Логирование тестов - Домашнее задание (HW3 + Optional) -### 23.10: 4-е занятие +### 23.06: 4-е занятие - Разбор домашнего задания HW3 + Optional - Методы улучшения качества кода - Spring: инициализация и популирование DB @@ -56,7 +59,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Домашнее задание (HW4 + Optional) #### Начало выполнения [выпускного проекта](https://github.com/JavaOPs/topjava/blob/master/graduation.md) -### 30.10: 5-е занятие +### 30.06: 5-е занятие - Обзор JDK 9/17. Миграция Topjava с 1.8 на 17 - Разбор вопросов - Разбор домашнего задания HW4 + Optional @@ -67,7 +70,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Spring кэш - Домашнее задание (HW5 + Optional) -### 06.11: 6-е занятие +### 07.07: 6-е занятие - Разбор домашнего задания HW5 + Optional - Кэш Hibernate - Spring Web @@ -80,7 +83,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + #### Большое ДЗ + выпускной проект + начинаем [курс BootJava](https://javaops.ru/view/bootjava) + подтягиваем "хвосты". -### 20.11: 7-е занятие +### 21.07: 7-е занятие - Разбор домашнего задания HW6 + Optional - Автогенерация DDL по модели - Тестирование Spring MVC @@ -91,7 +94,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Тестирование через SoapUi. UTF-8 - Домашнее задание (HW7 + Optional) -### 27.11: 8-е занятие +### 28.07: 8-е занятие - Разбор домашнего задания HW7 + Optional - WebJars. jQuery и JavaScript frameworks - Bootstrap @@ -100,7 +103,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Добавление Spring Security - Домашнее задание (HW8 + Optional) -### 04.12: 9-е занятие +### 04.08: 9-е занятие - Разбор домашнего задания HW8 + Optional - Spring Binding - Spring Validation @@ -112,7 +115,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Cookie. Session - Домашнее задание (HW9 + Optional) -### 11.12: 10-е занятие +### 11.08: 10-е занятие - Разбор домашнего задания HW10 + Optional - Кастомизация JSON (@JsonView) и валидации (groups) - Рефакторинг: jQuery конверторы и группы валидации по умолчанию @@ -125,22 +128,23 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Защита от межсайтовой подделки запросов (CSRF) - Домашнее задание (HW10) -### 18.12: 11-е занятие +### 18.08: 11-е занятие - Разбор домашнего задания HW10 + Optional - Локализация datatables, ошибок валидации - Защита от XSS (Cross Site Scripting) - Обработка ошибок 404 (NotFound) - Доступ к AuthorizedUser - Ограничение модификации пользователей -- Деплой приложения [на собственный выделенный сервер](https://github.com/JavaOPs/startup) -- Домашнее задание (HW11): сокрытия полей в Swagger -- Составление резюме. Собеседование. Разработка ПО. Возможные доработки приложения - -### 22.12: Миграция на Spring-Boot 3.5 -- Ревью вашего резюме +- Деплой [приложения в Heroku](http://topjava.herokuapp.com) +- Собеседование. Разработка ПО +- Возможные доработки приложения +- Домашнее задание по проекту: составление резюме + +### 22.08: Миграция на Spring-Boot - Основы Spring Boot. Spring Boot maven plugin - Lombok, база H2, ApplicationRunner - Spring Data REST + HATEOAS - Миграция приложения подсчета калорий на Spring Boot -### 12.01: Дедлайн на сдачу [выпускного проекта](https://github.com/JavaOPs/topjava/blob/master/graduation.md) +### 11.09.22: Дедлайн на сдачу [выпускного проекта](https://github.com/JavaOPs/topjava/blob/master/graduation.md) +### 21.09.22: Получение дипломов для участников [Дипломной программы](https://javaops.ru/view/register/diploma) diff --git a/config/Topjava-soapui-project.xml b/config/Topjava-soapui-project.xml deleted file mode 100644 index f4b00d936..000000000 --- a/config/Topjava-soapui-project.xml +++ /dev/null @@ -1,609 +0,0 @@ - - - - - - - - http://localhost:8080 - - - - - - - - - text/html;charset=utf-8 - 500 401 - - html - - - application/json - 200 - - ns:Response - - - application/json;charset=UTF-8 - 200 - - Response - - - - <xml-fragment/> - - http://localhost:8080 - - http://localhost/topjava/rest/admin/users - - user@yandex.ru - password - No Authorization - Basic - No Authorization - - - - - - - - - - - application/json - - - - text/html;charset=utf-8 - 500 - - html - - - application/json - 201 - - user:Response - - - application/json;charset=UTF-8 - 201 - - user:Response - - - - <xml-fragment/> - - http://localhost:8080 - {"name": "New2", - "email": "new2@yandex.ru", - "password": "passwordNew", - "roles": ["USER"] - } - - http://localhost/topjava/rest/admin/users - - No Authorization - Basic - No Authorization - - - - - - - - - - - - - - - text/html;charset=utf-8 - 500 - - html - - - application/json - 200 - - ns:Response - - - application/json;charset=UTF-8 - 200 - - ns:Response - - - - <xml-fragment/> - - http://localhost:8080 - - http://localhost/topjava/rest/admin/users/100000 - - No Authorization - Basic - No Authorization - - - - - - - - - - - text/html;charset=utf-8 - 405 500 - - html - - - application/json - - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - <xml-fragment/> - - http://localhost:8080 - {"name": "UserUpdated", - "email": "user@yandex.ru", - "password": "passwordNew", - "roles": ["USER"] - } - - http://localhost/topjava/rest/admin/users/100000 - - Basic - Basic - Global HTTP Settings - - - - - - - - - - - - - - - text/html;charset=utf-8 - 500 - - html - - - application/json - 200 - - ns:Response - - - application/json;charset=UTF-8 - 200 - - prof:Response - - - - <xml-fragment/> - - http://localhost:8080 - - http://localhost/topjava/rest/profile - - Basic - Basic - Global HTTP Settings - - - - - - - - - - - application/json - - - - text/html;charset=utf-8 - 500 405 - - html - - - application/json - 201 - - user:Response - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - 200 - - data - - - - <xml-fragment/> - - http://localhost:8080 - {"name": "New777", - "email": "new777@yandex.ru", - "password": "passwordNew", - "roles": ["USER"] - } - - http://localhost/topjava/rest/profile - - No Authorization - Basic - No Authorization - - - - - - - - - - - - 200 - - data - - - text/html;charset=utf-8 - 500 - - html - - - application/json - - - - - 200 - - data - - - - 204 - - data - - - - <xml-fragment/> - - http://localhost:8080 - - http://localhost/topjava/rest/profile - - No Authorization - Basic - No Authorization - - - - - - - - - - - - - - - - 0 - - data - - - application/json;charset=UTF-8 - 200 - - Response - - - - <xml-fragment/> - - - http://localhost:8080 - - http://localhost/topjava/rest/profile/meals - - No Authorization - - - - - - - - - - - application/json - - - - application/json;charset=UTF-8 - 201 - - meal:Response - - - - <xml-fragment/> - - - http://localhost:8080 - { - "dateTime": "2020-02-01T10:00", - "description": "Новый завтрак", - "calories": 777 - } - - http://localhost/topjava/rest/profile/meals - - No Authorization - - - - - - - - - - - - - - - application/json;charset=UTF-8 - 200 - - ns:Response - - - - <xml-fragment/> - - http://localhost:8080 - - http://localhost/topjava/rest/profile/meals/100002 - - No Authorization - - - - - - - - - - - - - 200 - - data - - - application/json - - - - - 200 - - data - - - - 200 - - data - - - - <xml-fragment/> - - UTF-8 - http://localhost:8080 - { - "id": 100002, - "dateTime": "2020-01-30T10:00", - "description": "Обновленный завтрак", - "calories": 500 - } - - http://localhost/topjava/rest/profile/meals/100002 - - No Authorization - - - - - - - - - - - - - startDate - - QUERY - - - - - startTime - - QUERY - - - - - endDate - - QUERY - - - - - endTime - - QUERY - - - - - - - - - application/json;charset=UTF-8 - 200 - - Response - - - - <xml-fragment/> - - http://localhost:8080 - - http://localhost/topjava/rest/profile/meals/filter - - No Authorization - - - - - - - - - - - startDate - startTime - endDate - endTime - - - - - - - - - - - \ No newline at end of file diff --git a/config/curl.md b/config/curl.md deleted file mode 100644 index 76852a8a1..000000000 --- a/config/curl.md +++ /dev/null @@ -1,39 +0,0 @@ -### curl samples (application deployed at application context `topjava`). -> For windows use `Git Bash` - -#### get All Users -`curl -s http://localhost:8080/topjava/rest/admin/users --user admin@gmail.com:admin` - -#### get Users 100001 -`curl -s http://localhost:8080/topjava/rest/admin/users/100001 --user admin@gmail.com:admin` - -#### register User -`curl -s -i -X POST -d '{"name":"New User","email":"test@mail.ru","password":"test-password"}' -H 'Content-Type:application/json;charset=UTF-8' http://localhost:8080/topjava/rest/profile` - -#### get Profile -`curl -s http://localhost:8080/topjava/rest/profile --user test@mail.ru:test-password` - -#### get All Meals -`curl -s http://localhost:8080/topjava/rest/profile/meals --user user@yandex.ru:password` - -#### get Meals 100003 -`curl -s http://localhost:8080/topjava/rest/profile/meals/100003 --user user@yandex.ru:password` - -#### filter Meals -`curl -s "http://localhost:8080/topjava/rest/profile/meals/filter?startDate=2020-01-30&startTime=07:00:00&endDate=2020-01-31&endTime=11:00:00" --user user@yandex.ru:password` - -#### get Meals not found -`curl -s -v http://localhost:8080/topjava/rest/profile/meals/100008 --user user@yandex.ru:password` - -#### delete Meals -`curl -s -X DELETE http://localhost:8080/topjava/rest/profile/meals/100002 --user user@yandex.ru:password` - -#### create Meals -`curl -s -X POST -d '{"dateTime":"2020-02-01T12:00","description":"Created lunch","calories":300}' -H 'Content-Type:application/json;charset=UTF-8' http://localhost:8080/topjava/rest/profile/meals --user user@yandex.ru:password` - -#### update Meals -`curl -s -X PUT -d '{"dateTime":"2020-01-30T07:00", "description":"Updated breakfast", "calories":200}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/profile/meals/100003 --user user@yandex.ru:password` - -#### validate with Error -`curl -s -X POST -d '{}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/admin/users --user admin@gmail.com:admin` -`curl -s -X PUT -d '{"dateTime":"2015-05-30T07:00"}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/profile/meals/100003 --user user@yandex.ru:password` diff --git a/config/db.properties b/config/db.properties deleted file mode 100644 index cdec2d890..000000000 --- a/config/db.properties +++ /dev/null @@ -1,8 +0,0 @@ -database.url=jdbc:postgresql://localhost:5432/topjava -database.username=user -database.password=password -database.init=false -jdbc.initLocation=classpath:db/initDB.sql -jpa.showSql=false -hibernate.format_sql=false -hibernate.use_sql_comments=false \ No newline at end of file diff --git a/config/messages/app.properties b/config/messages/app.properties deleted file mode 100644 index 0afa010e2..000000000 --- a/config/messages/app.properties +++ /dev/null @@ -1,68 +0,0 @@ -app.title=Calories management -app.stackTitle=Application stack: -app.description=Java Enterprise project with registration/authorization and role-based access rights (USER, ADMIN). \ -Admin could create/edit/delete users, users - manage your profile and data (meals) via UI (AJAX) and REST with basic authorization. \ -Meals could be filtered by date and time. Meal record color depends on daily calories sum exceeding "Daily calorie limit" (editable user's profile paramets). \ -All REST interface covered with JUnit tests by Spring MVC Test and Spring Security Test. -app.footer=Spring 5/JPA Enterprise (Topjava) internship application -app.login=Login as -app.profile=profile -app.register=Registration -app.registered=You are registered. Please Sign in. - -user.title=Users -user.edit=Edit user -user.add=Add user -user.name=Name -user.email=Email -user.roles=Roles -user.active=Active -user.registered=Registered -user.password=Password -user.caloriesPerDay=Daily calorie limit - -userTo.name=Name -userTo.email=Email -userTo.password=Password -userTo.caloriesPerDay=Daily calorie limit - -meal.title=Meals -meal.edit=Edit meal -meal.add=Add meal -meal.filter=Filter -meal.startDate=From date (inclusive) -meal.endDate=To date (inclusive) -meal.startTime=From time (inclusive) -meal.endTime=To time (exclusive) -meal.description=Description -meal.dateTime=Date/Time -meal.calories=Calories - -common.add=Add -common.select=Select -common.deleted=Record deleted -common.saved=Record saved -common.enabled=Record enabled -common.disabled=Record disabled -common.confirm=Are you sure? -common.save=Save -common.cancel=Cancel -common.search=Search - -exception.user.duplicateEmail=User with this email already exists -exception.user.updateRestriction=Admin/User update is forbidden -exception.meal.duplicateDateTime=You already have meal with this date/time - -error.appError=Application error -error.dataNotFound=Data not found -error.dataError=Data error -error.validationError=Validation error -error.wrongRequest=Wrong request - -NotEmpty=[{0}] must not be empty -NotBlank=[{0}] must not be empty -NotNull=[{0}] must not be empty -Email= Invalid format of [{0}] -Range=[{0}] must be between {2} and {1} -Length=[{0}] length must be between {2} and {1} -Size=[{0}] size must be between {2} and {1} \ No newline at end of file diff --git a/config/messages/app_ru.properties b/config/messages/app_ru.properties deleted file mode 100644 index cb8a254ea..000000000 --- a/config/messages/app_ru.properties +++ /dev/null @@ -1,68 +0,0 @@ -app.title=Подсчет калорий -app.stackTitle=Стек технологий: -app.description=Java Enterprise проект с регистрацией/авторизацией и правами доступа на основе ролей (USER, ADMIN). \ -Администратор может создавать/редактировать/удалять пользователей, а пользователи - управлять своим профилем и данными (едой) через UI (по AJAX) и по REST интерфейсу с базовой авторизацией. \ -Возможна фильтрация еды по датам и времени. Цвет записи таблицы еды зависит от того, превышает ли сумма калорий за день норму (редактируемый параметр в профиле пользователя). \ -Весь REST интерфейс покрывается JUnit тестами, используя Spring MVC Test и Spring Security Test. -app.footer=Приложение стажировки Spring 5/JPA Enterprise (Topjava) -app.login=Зайти как -app.profile=профиль -app.register=Регистрация -app.registered=Вы зарегистрированы. Введите ваш логин/пароль. - -user.title=Пользователи -user.edit=Редактировать пользователя -user.add=Добавить пользователя -user.name=Имя -user.email=Почта -user.roles=Роли -user.active=Активный -user.registered=Зарегистрирован -user.password=Пароль -user.caloriesPerDay=Норма калорий в день - -userTo.name=Имя -userTo.email=Почта -userTo.password=Пароль -userTo.caloriesPerDay=Норма калорий в день - -meal.title=Моя еда -meal.edit=Редактировать еду -meal.add=Добавить еду -meal.filter=Отфильтровать -meal.startDate=От даты (включая) -meal.endDate=До даты (включая) -meal.startTime=От времени (включая) -meal.endTime=До времени (исключая) -meal.description=Описание -meal.dateTime=Дата/Время -meal.calories=Калории - -common.add=Добавить -common.select=Выбрать -common.deleted=Запись удалена -common.saved=Запись сохранена -common.enabled=Запись активирована -common.disabled=Запись деактивирована -common.confirm=Вы уверены? -common.save=Сохранить -common.cancel=Отменить -common.search=Искать - -exception.user.duplicateEmail=Пользователь с такой почтой уже есть в приложении -exception.user.updateRestriction=Изменение Admin/User запрещено -exception.meal.duplicateDateTime=У вас уже есть еда с такой датой/временем - -error.appError=Ошибка приложения -error.dataNotFound=Данные не найдены -error.dataError=Ошибка в данных -error.validationError=Ошибка проверки данных -error.wrongRequest=Неверный запрос - -NotEmpty=[{0}] не должно быть пустым -NotBlank=[{0}] не должно быть пустым -NotNull=[{0}] не должно быть пустым -Email=Неверный формат [{0}] -Range= [{0}] должно быть между {2} и {1} -Length=Длинна [{0}] должна быть между {2} и {1} -Size=Размер [{0}] должен быть между {2} и {1} \ No newline at end of file diff --git a/pom.xml b/pom.xml index e3ccaaae4..e3464fde6 100644 --- a/pom.xml +++ b/pom.xml @@ -9,116 +9,53 @@ 1.0-SNAPSHOT Calories Management - https://javaops-demo.ru/topjava + http://topjava.herokuapp.com/ - 21 + 1.8 UTF-8 UTF-8 - 5.3.39 - 5.8.16 - 2.7.18 - 2.20.1 - 9.0.111 - 1.21.2 - - 5.6.15.Final - 6.2.5.Final - 3.0.1-b12 - - - 3.10.8 - - - 4.6.2 - 3.7.1 - 2.5.20-1 - 3.1.4 - 1.13.5 + 5.3.20 - 1.5.20 - 2.0.17 + 1.2.11 + 1.7.36 - 42.7.8 + 42.4.0 + + + 5.6.9.Final + 6.2.3.Final + 3.0.1-b12 + - 5.14.1 - 3.27.6 - 3.0 - 2.10.0 + 4.13.2 + 3.23.1 topjava package - - org.apache.maven.plugins - maven-war-plugin - 3.4.0 - org.apache.maven.plugins maven-compiler-plugin - 3.14.1 + 3.8.1 ${java.version} ${java.version} - org.apache.maven.plugins maven-surefire-plugin - 3.5.4 + 2.22.2 -Dfile.encoding=UTF-8 - - - - - org.codehaus.cargo - cargo-maven3-plugin - 1.10.24 - - - tomcat9x - - UTF-8 - tomcat,datajpa - - - - org.postgresql - postgresql - - - - - - - src/main/resources/tomcat/context.xml - conf/Catalina/localhost/ - ${project.build.finalName}.xml - - - - - - ru.javawebinar - topjava - war - - ${project.build.finalName} - - - - - @@ -128,67 +65,47 @@ org.slf4j slf4j-api ${slf4j.version} + compile - ch.qos.logback - logback-classic - ${logback.version} + org.slf4j + jul-to-slf4j + ${slf4j.version} runtime - - - com.google.code.findbugs - annotations - 3.0.1 - compile - - - javax.annotation - javax.annotation-api - 1.3.2 + ch.qos.logback + logback-classic + ${logback.version} + runtime org.springframework - spring-context-support + spring-context + ${spring.version} - org.springframework.data - spring-data-jpa - ${spring-data-jpa.version} + org.springframework + spring-orm + ${spring.version} + - io.springfox - springfox-swagger2 - 2.9.2 + org.postgresql + postgresql + ${postgresql.version} - io.springfox - springfox-swagger-ui - 2.9.2 + org.hsqldb + hsqldb + 2.3.4 - - - org.springframework.security - spring-security-web - ${spring.security.version} - - - org.springframework.security - spring-security-config - ${spring.security.version} - - - org.springframework.security - spring-security-taglibs - ${spring.security.version} - @@ -196,18 +113,11 @@ hibernate-core ${hibernate.version} - - org.hibernate.validator hibernate-validator ${hibernate-validator.version} - - org.jsoup - jsoup - ${jsoup.version} - @@ -217,42 +127,11 @@ provided - - - org.hibernate - hibernate-jcache - ${hibernate.version} - - - javax.cache - cache-api - 1.1.0 - - - org.ehcache - ehcache - runtime - ${ehcache.version} - - - org.glassfish.jaxb - jaxb-runtime - - - - - - - org.glassfish.jaxb - jaxb-runtime - 2.4.0-b180830.0438 - - - org.apache.tomcat - tomcat-servlet-api - ${tomcat.version} + javax.servlet + javax.servlet-api + 4.0.1 provided @@ -263,167 +142,34 @@ - org.springframework - spring-webmvc - - - - - org.webjars - jquery - ${webjars-jquery.version} - - - org.webjars - bootstrap - ${webjars-bootstrap.version} - - - org.webjars - jquery - - - - org.webjars - popper.js - - - - - org.webjars - datatables - ${webjars-datatables.version} - - - org.webjars - jquery - - - - - org.webjars - datetimepicker - ${webjars-datetimepicker.version} - - - org.webjars.bower - noty - ${webjars-noty.version} - - - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-hibernate5 - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} + javax.annotation + javax.annotation-api + 1.3.2 - - org.junit.jupiter - junit-jupiter-engine + junit + junit ${junit.version} test - - org.hamcrest - hamcrest-core - ${hamcrest.version} - test - - - com.jayway.jsonpath - json-path - ${json-path.version} - test - - org.springframework spring-test + ${spring.version} test - - org.springframework.security - spring-security-test - ${spring.security.version} - test - - org.assertj assertj-core ${assertj.version} test - - - - org.junit.platform - junit-platform-launcher - 1.14.1 - test - - - hsqldb - - - org.hsqldb - hsqldb - 2.7.4 - - - - - - postgres - - - org.postgresql - postgresql - ${postgresql.version} - - - org.apache.tomcat - tomcat-jdbc - ${tomcat.version} - provided - - - org.slf4j - jul-to-slf4j - ${slf4j.version} - runtime - - - - true - - - - - org.springframework - spring-framework-bom - ${spring.version} - pom - import - - - \ No newline at end of file + diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java deleted file mode 100644 index b51930e87..000000000 --- a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java +++ /dev/null @@ -1,37 +0,0 @@ -package ru.javawebinar.topjava; - -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.util.UsersUtil; - -import java.io.Serial; - -public class AuthorizedUser extends org.springframework.security.core.userdetails.User { - @Serial - private static final long serialVersionUID = 1L; - - private UserTo userTo; - - public AuthorizedUser(User user) { - super(user.getEmail(), user.getPassword(), user.isEnabled(), true, true, true, user.getRoles()); - setTo(UsersUtil.asTo(user)); - } - - public int getId() { - return userTo.id(); - } - - public void setTo(UserTo newTo) { - newTo.setPassword(null); - userTo = newTo; - } - - public UserTo getUserTo() { - return userTo; - } - - @Override - public String toString() { - return userTo.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/HasId.java b/src/main/java/ru/javawebinar/topjava/HasId.java deleted file mode 100644 index 2ba30235c..000000000 --- a/src/main/java/ru/javawebinar/topjava/HasId.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.javawebinar.topjava; - -import org.springframework.util.Assert; - -public interface HasId { - Integer getId(); - - void setId(Integer id); - - default boolean isNew() { - return getId() == null; - } - - // doesn't work for hibernate lazy proxy - default int id() { - Assert.notNull(getId(), "Entity must has id"); - return getId(); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/HasIdAndEmail.java b/src/main/java/ru/javawebinar/topjava/HasIdAndEmail.java deleted file mode 100644 index 6389876b5..000000000 --- a/src/main/java/ru/javawebinar/topjava/HasIdAndEmail.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.javawebinar.topjava; - -public interface HasIdAndEmail extends HasId { - String getEmail(); -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/Profiles.java b/src/main/java/ru/javawebinar/topjava/Profiles.java deleted file mode 100644 index 80dc6ef11..000000000 --- a/src/main/java/ru/javawebinar/topjava/Profiles.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.javawebinar.topjava; - -import org.springframework.util.ClassUtils; - -public class Profiles { - public static final String - JDBC = "jdbc", - JPA = "jpa", - DATAJPA = "datajpa"; - - public static final String REPOSITORY_IMPLEMENTATION = DATAJPA; - - public static final String - POSTGRES_DB = "postgres", - HSQL_DB = "hsqldb", - VDS = "vds"; - - // Get DB profile depending on DB driver in classpath - public static String getActiveDbProfile() { - if (ClassUtils.isPresent("org.postgresql.Driver", null)) { - return POSTGRES_DB; - } else if (ClassUtils.isPresent("org.hsqldb.jdbcDriver", null)) { - return HSQL_DB; - } else { - throw new IllegalStateException("Could not find DB driver"); - } - } -} diff --git a/src/main/java/ru/javawebinar/topjava/View.java b/src/main/java/ru/javawebinar/topjava/View.java deleted file mode 100644 index f1623c053..000000000 --- a/src/main/java/ru/javawebinar/topjava/View.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.javawebinar.topjava; - -import javax.validation.groups.Default; - -public class View { - // Validate only form UI/REST - public interface Web extends Default {} - - // Validate only when DB save/update - public interface Persist extends Default {} -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index ad5ded601..d9343a074 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,26 +1,18 @@ package ru.javawebinar.topjava.model; -import io.swagger.annotations.ApiModelProperty; -import ru.javawebinar.topjava.HasId; +import org.springframework.util.Assert; import javax.persistence.*; -import static org.hibernate.proxy.HibernateProxyHelper.getClassWithoutInitializingProxy; - @MappedSuperclass // http://stackoverflow.com/questions/594597/hibernate-annotations-which-is-better-field-or-property-access @Access(AccessType.FIELD) -//@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, isGetterVisibility = NONE, setterVisibility = NONE) -public abstract class AbstractBaseEntity implements HasId { +public abstract class AbstractBaseEntity { public static final int START_SEQ = 100000; @Id @SequenceGenerator(name = "global_seq", sequenceName = "global_seq", allocationSize = 1, initialValue = START_SEQ) - // @Column(name = "id", unique = true, nullable = false, columnDefinition = "integer default nextval('global_seq')") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq") -// See https://hibernate.atlassian.net/browse/HHH-3718 and https://hibernate.atlassian.net/browse/HHH-12034 -// Proxy initialization when accessing its identifier managed now by JPA_PROXY_COMPLIANCE setting - @ApiModelProperty(hidden = true) protected Integer id; protected AbstractBaseEntity() { @@ -30,31 +22,42 @@ protected AbstractBaseEntity(Integer id) { this.id = id; } - @Override public void setId(Integer id) { this.id = id; } - @Override public Integer getId() { return id; } + public int id() { + Assert.notNull(id, "Entity must have id"); + return id; + } + + public boolean isNew() { + return this.id == null; + } + @Override public String toString() { - return getClass().getSimpleName() + ":" + getId(); + return getClass().getSimpleName() + ":" + id; } - // https://stackoverflow.com/a/78077907/548473 @Override - public final boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClassWithoutInitializingProxy(this) != getClassWithoutInitializingProxy(o)) return false; - return getId() != null && getId().equals(((AbstractBaseEntity) o).getId()); + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractBaseEntity that = (AbstractBaseEntity) o; + return id != null && id.equals(that.id); } @Override - public final int hashCode() { - return getClassWithoutInitializingProxy(this).hashCode(); + public int hashCode() { + return id == null ? 0 : id; } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java index 2539be1fe..0b32aac58 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractNamedEntity.java @@ -2,18 +2,16 @@ import javax.persistence.Column; import javax.persistence.MappedSuperclass; -import ru.javawebinar.topjava.View; -import ru.javawebinar.topjava.util.validation.NoHtml; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; + @MappedSuperclass public abstract class AbstractNamedEntity extends AbstractBaseEntity { @NotBlank @Size(min = 2, max = 128) @Column(name = "name", nullable = false) - @NoHtml(groups = {View.Web.class}) protected String name; protected AbstractNamedEntity() { diff --git a/src/main/java/ru/javawebinar/topjava/model/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 50a2c1d6a..788ae8f6f 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -1,65 +1,28 @@ package ru.javawebinar.topjava.model; -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; -import org.hibernate.validator.constraints.Range; -import org.springframework.format.annotation.DateTimeFormat; -import ru.javawebinar.topjava.View; -import ru.javawebinar.topjava.util.DateTimeUtil; -import ru.javawebinar.topjava.util.validation.NoHtml; - -import javax.persistence.*; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -@NamedQueries({ - @NamedQuery(name = Meal.ALL_SORTED, query = "SELECT m FROM Meal m WHERE m.user.id=:userId ORDER BY m.dateTime DESC"), - @NamedQuery(name = Meal.DELETE, query = "DELETE FROM Meal m WHERE m.id=:id AND m.user.id=:userId"), - @NamedQuery(name = Meal.GET_BETWEEN, query = """ - SELECT m FROM Meal m - WHERE m.user.id=:userId AND m.dateTime >= :startDateTime AND m.dateTime < :endDateTime ORDER BY m.dateTime DESC - """), -// @NamedQuery(name = Meal.UPDATE, query = "UPDATE Meal m SET m.dateTime = :datetime, m.calories= :calories," + -// "m.description=:desc where m.id=:id and m.user.id=:userId") -}) -@Entity -@Table(name = "meal", uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "date_time"}, name = "meal_unique_user_datetime_idx")}) public class Meal extends AbstractBaseEntity { - public static final String ALL_SORTED = "Meal.getAll"; - public static final String DELETE = "Meal.delete"; - public static final String GET_BETWEEN = "Meal.getBetween"; - - @Column(name = "date_time", nullable = false) - @NotNull - @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN) private LocalDateTime dateTime; - @Column(name = "description", nullable = false) - @NotBlank - @Size(min = 2, max = 120) - @NoHtml(groups = {View.Web.class}) private String description; - @Column(name = "calories", nullable = false) - @NotNull - @Range(min = 10, max = 5000) - private Integer calories; + private int calories; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id", nullable = false) - @OnDelete(action = OnDeleteAction.CASCADE) - @JsonIgnore - @NotNull(groups = View.Persist.class) private User user; public Meal() { } + public Meal(LocalDateTime dateTime, String description, int calories) { + this(null, dateTime, description, calories); + } + public Meal(Integer id, LocalDateTime dateTime, String description, int calories) { super(id); this.dateTime = dateTime; @@ -95,7 +58,7 @@ public void setDescription(String description) { this.description = description; } - public void setCalories(Integer calories) { + public void setCalories(int calories) { this.calories = calories; } diff --git a/src/main/java/ru/javawebinar/topjava/model/Role.java b/src/main/java/ru/javawebinar/topjava/model/Role.java index 27f3e5231..acb7a276f 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Role.java +++ b/src/main/java/ru/javawebinar/topjava/model/Role.java @@ -1,14 +1,6 @@ package ru.javawebinar.topjava.model; -import org.springframework.security.core.GrantedAuthority; - -public enum Role implements GrantedAuthority { +public enum Role { USER, - ADMIN; - -// https://stackoverflow.com/a/19542316/548473 - @Override - public String getAuthority() { - return "ROLE_" + name(); - } + ADMIN } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 41cbd5db8..c500ea358 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -1,20 +1,8 @@ package ru.javawebinar.topjava.model; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModelProperty; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.*; import org.hibernate.validator.constraints.Range; import org.springframework.util.CollectionUtils; -import ru.javawebinar.topjava.HasIdAndEmail; -import ru.javawebinar.topjava.View; -import ru.javawebinar.topjava.util.validation.NoHtml; - -import javax.persistence.Entity; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OrderBy; -import javax.persistence.Table; + import javax.persistence.*; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; @@ -22,17 +10,16 @@ import javax.validation.constraints.Size; import java.util.*; -import static ru.javawebinar.topjava.util.UsersUtil.DEFAULT_CALORIES_PER_DAY; +import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @NamedQueries({ @NamedQuery(name = User.DELETE, query = "DELETE FROM User u WHERE u.id=:id"), - @NamedQuery(name = User.BY_EMAIL, query = "SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), - @NamedQuery(name = User.ALL_SORTED, query = "SELECT u FROM User u ORDER BY u.name, u.email"), + @NamedQuery(name = User.BY_EMAIL, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email=?1"), + @NamedQuery(name = User.ALL_SORTED, query = "SELECT u FROM User u LEFT JOIN FETCH u.roles ORDER BY u.name, u.email"), }) @Entity @Table(name = "users") -public class User extends AbstractNamedEntity implements HasIdAndEmail { +public class User extends AbstractNamedEntity { public static final String DELETE = "User.delete"; public static final String BY_EMAIL = "User.getByEmail"; @@ -42,14 +29,11 @@ public class User extends AbstractNamedEntity implements HasIdAndEmail { @Email @NotBlank @Size(max = 128) - @NoHtml(groups = {View.Web.class}) // https://stackoverflow.com/questions/17480809 private String email; @Column(name = "password", nullable = false) @NotBlank @Size(min = 5, max = 128) - // https://stackoverflow.com/a/12505165/548473 - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private String password; @Column(name = "enabled", nullable = false, columnDefinition = "bool default true") @@ -57,31 +41,19 @@ public class User extends AbstractNamedEntity implements HasIdAndEmail { @Column(name = "registered", nullable = false, columnDefinition = "timestamp default now()", updatable = false) @NotNull - @JsonProperty(access = JsonProperty.Access.READ_ONLY) private Date registered = new Date(); - @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Enumerated(EnumType.STRING) - @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), - uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "role"}, name = "uk_user_role")}) + @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id") + ,uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "role"}/* , name = "uk_user_roles"*/)}) @Column(name = "role") @ElementCollection(fetch = FetchType.EAGER) - // @Fetch(FetchMode.SUBSELECT) - @BatchSize(size = 200) - @JoinColumn - @OnDelete(action = OnDeleteAction.CASCADE) private Set roles; @Column(name = "calories_per_day", nullable = false, columnDefinition = "int default 2000") @Range(min = 10, max = 10000) private int caloriesPerDay = DEFAULT_CALORIES_PER_DAY; - @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")//, cascade = CascadeType.REMOVE, orphanRemoval = true) - @OrderBy("dateTime DESC") - @OnDelete(action = OnDeleteAction.CASCADE) //https://stackoverflow.com/a/44988100/548473 - @ApiModelProperty(hidden = true) - private List meals; - public User() { } @@ -89,8 +61,8 @@ public User(User u) { this(u.id, u.name, u.email, u.password, u.caloriesPerDay, u.enabled, u.registered, u.roles); } - public User(Integer id, String name, String email, String password, int caloriesPerDay, Role... roles) { - this(id, name, email, password, caloriesPerDay, true, new Date(), List.of(roles)); + public User(Integer id, String name, String email, String password, Role... roles) { + this(id, name, email, password, DEFAULT_CALORIES_PER_DAY, true, new Date(), Arrays.asList((roles))); } public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Date registered, Collection roles) { @@ -103,7 +75,6 @@ public User(Integer id, String name, String email, String password, int calories setRoles(roles); } - @Override public String getEmail() { return email; } @@ -152,14 +123,6 @@ public String getPassword() { return password; } - public List getMeals() { - return meals; - } - - public void setMeals(List meals) { - this.meals = meals; - } - @Override public String toString() { return "User{" + diff --git a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java index 1ad7f8d94..9461d5f9f 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/MealRepository.java @@ -20,8 +20,4 @@ public interface MealRepository { // ORDERED dateTime desc List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId); - - default Meal getWithUser(int id, int userId) { - throw new UnsupportedOperationException(); - } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java index 9fecbddaa..138369789 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/UserRepository.java @@ -18,8 +18,4 @@ public interface UserRepository { User getByEmail(String email); List getAll(); - - default User getWithMeals(int id) { - throw new UnsupportedOperationException(); - } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java deleted file mode 100644 index 9aeef134f..000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudMealRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.javawebinar.topjava.repository.datajpa; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.transaction.annotation.Transactional; -import ru.javawebinar.topjava.model.Meal; - -import java.time.LocalDateTime; -import java.util.List; - -@Transactional(readOnly = true) -public interface CrudMealRepository extends JpaRepository { - - @Modifying - @Transactional - @Query("DELETE FROM Meal m WHERE m.id=:id AND m.user.id=:userId") - int delete(@Param("id") int id, @Param("userId") int userId); - - @Query("SELECT m FROM Meal m WHERE m.user.id=:userId ORDER BY m.dateTime DESC") - List getAll(@Param("userId") int userId); - - @Query("SELECT m from Meal m WHERE m.user.id=:userId AND m.dateTime >= :startDate AND m.dateTime < :endDate ORDER BY m.dateTime DESC") - List getBetweenHalfOpen(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate, @Param("userId") int userId); - - @Query("SELECT m FROM Meal m JOIN FETCH m.user WHERE m.id = ?1 and m.user.id = ?2") - Meal getWithUser(int id, int userId); -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java deleted file mode 100644 index f3f362bdf..000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.javawebinar.topjava.repository.datajpa; - -import org.springframework.data.jpa.repository.*; -import org.springframework.data.repository.query.Param; -import org.springframework.transaction.annotation.Transactional; -import ru.javawebinar.topjava.model.User; - -import javax.persistence.QueryHint; - -@Transactional(readOnly = true) -public interface CrudUserRepository extends JpaRepository { - @Transactional - @Modifying - @Query("DELETE FROM User u WHERE u.id=:id") - int delete(@Param("id") int id); - - // https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#hql-distinct - @QueryHints({ - @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false") - }) - User getByEmail(String email); - - // https://stackoverflow.com/a/46013654/548473 - @EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD) - @Query("SELECT u FROM User u WHERE u.id=?1") - User getWithMeals(int id); -} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java deleted file mode 100644 index b5f4e3eeb..000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaMealRepository.java +++ /dev/null @@ -1,58 +0,0 @@ -package ru.javawebinar.topjava.repository.datajpa; - -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.repository.MealRepository; - -import java.time.LocalDateTime; -import java.util.List; - -@Repository -public class DataJpaMealRepository implements MealRepository { - - private final CrudMealRepository crudMealRepository; - private final CrudUserRepository crudUserRepository; - - public DataJpaMealRepository(CrudMealRepository crudMealRepository, CrudUserRepository crudUserRepository) { - this.crudMealRepository = crudMealRepository; - this.crudUserRepository = crudUserRepository; - } - - @Override - @Transactional - public Meal save(Meal meal, int userId) { - if (!meal.isNew() && get(meal.id(), userId) == null) { - return null; - } - meal.setUser(crudUserRepository.getReferenceById(userId)); - return crudMealRepository.save(meal); - } - - @Override - public boolean delete(int id, int userId) { - return crudMealRepository.delete(id, userId) != 0; - } - - @Override - public Meal get(int id, int userId) { - return crudMealRepository.findById(id) - .filter(meal -> meal.getUser().getId() == userId) - .orElse(null); - } - - @Override - public List getAll(int userId) { - return crudMealRepository.getAll(userId); - } - - @Override - public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { - return crudMealRepository.getBetweenHalfOpen(startDateTime, endDateTime, userId); - } - - @Override - public Meal getWithUser(int id, int userId) { - return crudMealRepository.getWithUser(id, userId); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java deleted file mode 100644 index 608c855e0..000000000 --- a/src/main/java/ru/javawebinar/topjava/repository/datajpa/DataJpaUserRepository.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.javawebinar.topjava.repository.datajpa; - -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Repository; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.repository.UserRepository; - -import java.util.List; - -@Repository -public class DataJpaUserRepository implements UserRepository { - private static final Sort SORT_NAME_EMAIL = Sort.by(Sort.Direction.ASC, "name", "email"); - - private final CrudUserRepository crudRepository; - - public DataJpaUserRepository(CrudUserRepository crudRepository) { - this.crudRepository = crudRepository; - } - - @Override - public User save(User user) { - return crudRepository.save(user); - } - - @Override - public boolean delete(int id) { - return crudRepository.delete(id) != 0; - } - - @Override - public User get(int id) { - return crudRepository.findById(id).orElse(null); - } - - @Override - public User getByEmail(String email) { - return crudRepository.getByEmail(email); - } - - @Override - public List getAll() { - return crudRepository.findAll(SORT_NAME_EMAIL); - } - - @Override - public User getWithMeals(int id) { - return crudRepository.getWithMeals(id); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java index 924a2b781..fa26d5663 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.repository.jdbc; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; @@ -8,16 +9,13 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.repository.MealRepository; -import ru.javawebinar.topjava.util.validation.ValidationUtil; import java.time.LocalDateTime; import java.util.List; @Repository -@Transactional(readOnly = true) public class JdbcMealRepository implements MealRepository { private static final RowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(Meal.class); @@ -28,9 +26,10 @@ public class JdbcMealRepository implements MealRepository { private final SimpleJdbcInsert insertMeal; + @Autowired public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.insertMeal = new SimpleJdbcInsert(jdbcTemplate) - .withTableName("meal") + .withTableName("meals") .usingGeneratedKeyColumns("id"); this.jdbcTemplate = jdbcTemplate; @@ -38,10 +37,7 @@ public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate } @Override - @Transactional public Meal save(Meal meal, int userId) { - ValidationUtil.validate(meal); - MapSqlParameterSource map = new MapSqlParameterSource() .addValue("id", meal.getId()) .addValue("description", meal.getDescription()) @@ -54,7 +50,7 @@ public Meal save(Meal meal, int userId) { meal.setId(newId.intValue()); } else { if (namedParameterJdbcTemplate.update("" + - "UPDATE meal " + + "UPDATE meals " + " SET description=:description, calories=:calories, date_time=:date_time " + " WHERE id=:id AND user_id=:user_id", map) == 0) { return null; @@ -64,28 +60,27 @@ public Meal save(Meal meal, int userId) { } @Override - @Transactional public boolean delete(int id, int userId) { - return jdbcTemplate.update("DELETE FROM meal WHERE id=? AND user_id=?", id, userId) != 0; + return jdbcTemplate.update("DELETE FROM meals WHERE id=? AND user_id=?", id, userId) != 0; } @Override public Meal get(int id, int userId) { List meals = jdbcTemplate.query( - "SELECT * FROM meal WHERE id = ? AND user_id = ?", ROW_MAPPER, id, userId); + "SELECT * FROM meals WHERE id = ? AND user_id = ?", ROW_MAPPER, id, userId); return DataAccessUtils.singleResult(meals); } @Override public List getAll(int userId) { return jdbcTemplate.query( - "SELECT * FROM meal WHERE user_id=? ORDER BY date_time DESC", ROW_MAPPER, userId); + "SELECT * FROM meals WHERE user_id=? ORDER BY date_time DESC", ROW_MAPPER, userId); } @Override public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { return jdbcTemplate.query( - "SELECT * FROM meal WHERE user_id=? AND date_time >= ? AND date_time < ? ORDER BY date_time DESC", + "SELECT * FROM meals WHERE user_id=? AND date_time >= ? AND date_time < ? ORDER BY date_time DESC", ROW_MAPPER, userId, startDateTime, endDateTime); } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java index 4074ff2c7..412bfbebc 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java @@ -8,17 +8,12 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.CollectionUtils; -import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; -import ru.javawebinar.topjava.util.validation.ValidationUtil; -import java.util.*; +import java.util.List; @Repository -@Transactional(readOnly = true) public class JdbcUserRepository implements UserRepository { private static final BeanPropertyRowMapper ROW_MAPPER = BeanPropertyRowMapper.newInstance(User.class); @@ -40,34 +35,21 @@ public JdbcUserRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate } @Override - @Transactional public User save(User user) { - ValidationUtil.validate(user); - BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user); if (user.isNew()) { Number newKey = insertUser.executeAndReturnKey(parameterSource); user.setId(newKey.intValue()); - insertRoles(user); - } else { - if (namedParameterJdbcTemplate.update(""" - UPDATE users SET name=:name, email=:email, password=:password, - registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id - """, parameterSource) == 0) { - return null; - } - // Simplest implementation. - // More complicated : get user roles from DB and compare them with user.roles (assume that roles are changed rarely). - // If roles are changed, calculate difference in java and delete/insert them. - deleteRoles(user); - insertRoles(user); + } else if (namedParameterJdbcTemplate.update( + "UPDATE users SET name=:name, email=:email, password=:password, " + + "registered=:registered, enabled=:enabled, calories_per_day=:caloriesPerDay WHERE id=:id", parameterSource) == 0) { + return null; } return user; } @Override - @Transactional public boolean delete(int id) { return jdbcTemplate.update("DELETE FROM users WHERE id=?", id) != 0; } @@ -75,49 +57,18 @@ public boolean delete(int id) { @Override public User get(int id) { List users = jdbcTemplate.query("SELECT * FROM users WHERE id=?", ROW_MAPPER, id); - return setRoles(DataAccessUtils.singleResult(users)); + return DataAccessUtils.singleResult(users); } @Override public User getByEmail(String email) { // return jdbcTemplate.queryForObject("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); List users = jdbcTemplate.query("SELECT * FROM users WHERE email=?", ROW_MAPPER, email); - return setRoles(DataAccessUtils.singleResult(users)); + return DataAccessUtils.singleResult(users); } @Override public List getAll() { - List users = jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); - - Map> map = new HashMap<>(); - jdbcTemplate.query("SELECT * FROM user_role", rs -> { - map.computeIfAbsent(rs.getInt("user_id"), userId -> EnumSet.noneOf(Role.class)) - .add(Role.valueOf(rs.getString("role"))); - }); - users.forEach(u -> u.setRoles(map.get(u.getId()))); - return users; - } - - private void insertRoles(User u) { - Set roles = u.getRoles(); - if (!CollectionUtils.isEmpty(roles)) { - jdbcTemplate.batchUpdate("INSERT INTO user_role (user_id, role) VALUES (?, ?)", roles, roles.size(), - (ps, role) -> { - ps.setInt(1, u.id()); - ps.setString(2, role.name()); - }); - } - } - - private void deleteRoles(User u) { - jdbcTemplate.update("DELETE FROM user_role WHERE user_id=?", u.getId()); - } - - private User setRoles(User u) { - if (u != null) { - List roles = jdbcTemplate.queryForList("SELECT role FROM user_role WHERE user_id=?", Role.class, u.getId()); - u.setRoles(roles); - } - return u; + return jdbcTemplate.query("SELECT * FROM users ORDER BY name, email", ROW_MAPPER); } } diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java index 6df9fd99f..9cc19a4dc 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java @@ -1,62 +1,37 @@ package ru.javawebinar.topjava.repository.jpa; import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.MealRepository; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; import java.time.LocalDateTime; import java.util.List; @Repository -@Transactional(readOnly = true) public class JpaMealRepository implements MealRepository { - @PersistenceContext - private EntityManager em; - @Override - @Transactional public Meal save(Meal meal, int userId) { - meal.setUser(em.getReference(User.class, userId)); - if (meal.isNew()) { - em.persist(meal); - return meal; - } - return get(meal.id(), userId) == null ? null : em.merge(meal); + return null; } @Override - @Transactional public boolean delete(int id, int userId) { - return em.createNamedQuery(Meal.DELETE) - .setParameter("id", id) - .setParameter("userId", userId) - .executeUpdate() != 0; + return false; } @Override public Meal get(int id, int userId) { - Meal meal = em.find(Meal.class, id); - return meal != null && meal.getUser().getId() == userId ? meal : null; + return null; } @Override public List getAll(int userId) { - return em.createNamedQuery(Meal.ALL_SORTED, Meal.class) - .setParameter("userId", userId) - .getResultList(); + return null; } @Override public List getBetweenHalfOpen(LocalDateTime startDateTime, LocalDateTime endDateTime, int userId) { - return em.createNamedQuery(Meal.GET_BETWEEN, Meal.class) - .setParameter("userId", userId) - .setParameter("startDateTime", startDateTime) - .setParameter("endDateTime", endDateTime) - .getResultList(); + return null; } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java index 22fa8f4e3..3a1bbddc4 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaUserRepository.java @@ -1,6 +1,5 @@ package ru.javawebinar.topjava.repository.jpa; -import org.hibernate.jpa.QueryHints; import org.springframework.dao.support.DataAccessUtils; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -62,7 +61,6 @@ public boolean delete(int id) { public User getByEmail(String email) { List users = em.createNamedQuery(User.BY_EMAIL, User.class) .setParameter(1, email) - .setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false) .getResultList(); return DataAccessUtils.singleResult(users); } diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index b4ebae213..df874378e 100644 --- a/src/main/java/ru/javawebinar/topjava/service/MealService.java +++ b/src/main/java/ru/javawebinar/topjava/service/MealService.java @@ -11,7 +11,7 @@ import static ru.javawebinar.topjava.util.DateTimeUtil.atStartOfDayOrMin; import static ru.javawebinar.topjava.util.DateTimeUtil.atStartOfNextDayOrMax; -import static ru.javawebinar.topjava.util.validation.ValidationUtil.checkNotFound; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; @Service public class MealService { @@ -23,11 +23,11 @@ public MealService(MealRepository repository) { } public Meal get(int id, int userId) { - return checkNotFound(repository.get(id, userId), id); + return checkNotFoundWithId(repository.get(id, userId), id); } public void delete(int id, int userId) { - checkNotFound(repository.delete(id, userId), id); + checkNotFoundWithId(repository.delete(id, userId), id); } public List getBetweenInclusive(@Nullable LocalDate startDate, @Nullable LocalDate endDate, int userId) { @@ -40,15 +40,11 @@ public List getAll(int userId) { public void update(Meal meal, int userId) { Assert.notNull(meal, "meal must not be null"); - checkNotFound(repository.save(meal, userId), meal.id()); + checkNotFoundWithId(repository.save(meal, userId), meal.id()); } public Meal create(Meal meal, int userId) { Assert.notNull(meal, "meal must not be null"); return repository.save(meal, userId); } - - public Meal getWithUser(int id, int userId) { - return checkNotFound(repository.getWithUser(id, userId), id); - } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/service/UserService.java b/src/main/java/ru/javawebinar/topjava/service/UserService.java index e4aaed9ea..09ccee68c 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -1,65 +1,35 @@ package ru.javawebinar.topjava.service; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Scope; -import org.springframework.context.annotation.ScopedProxyMode; -import org.springframework.core.env.Environment; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; -import ru.javawebinar.topjava.AuthorizedUser; -import ru.javawebinar.topjava.Profiles; -import ru.javawebinar.topjava.model.AbstractBaseEntity; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.repository.UserRepository; -import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.util.UsersUtil; -import ru.javawebinar.topjava.util.exception.UpdateRestrictionException; import java.util.List; -import static ru.javawebinar.topjava.util.UsersUtil.prepareToSave; -import static ru.javawebinar.topjava.util.validation.ValidationUtil.checkNotFound; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFound; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; -@Service("userService") -@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) -public class UserService implements UserDetailsService { +@Service +public class UserService { private final UserRepository repository; - private final PasswordEncoder passwordEncoder; - private boolean modificationRestriction; - - @Autowired - @SuppressWarnings("deprecation") - public void setEnvironment(Environment environment) { - modificationRestriction = environment.acceptsProfiles(Profiles.VDS); - } - - public UserService(UserRepository repository, PasswordEncoder passwordEncoder) { + public UserService(UserRepository repository) { this.repository = repository; - this.passwordEncoder = passwordEncoder; } - @CacheEvict(value = "users", allEntries = true) public User create(User user) { Assert.notNull(user, "user must not be null"); - return prepareAndSave(user); + return repository.save(user); } - @CacheEvict(value = "users", allEntries = true) public void delete(int id) { - checkModificationAllowed(id); - checkNotFound(repository.delete(id), id); + checkNotFoundWithId(repository.delete(id), id); } public User get(int id) { - return checkNotFound(repository.get(id), id); + return checkNotFoundWithId(repository.get(id), id); } public User getByEmail(String email) { @@ -67,57 +37,12 @@ public User getByEmail(String email) { return checkNotFound(repository.getByEmail(email), "email=" + email); } - @Cacheable("users") public List getAll() { return repository.getAll(); } - @CacheEvict(value = "users", allEntries = true) public void update(User user) { Assert.notNull(user, "user must not be null"); -// checkNotFound : check works only for JDBC, disabled - checkModificationAllowed(user.id()); - prepareAndSave(user); - } - - - @CacheEvict(value = "users", allEntries = true) - @Transactional - public void update(UserTo userTo) { - checkModificationAllowed(userTo.id()); - User user = get(userTo.id()); - prepareAndSave(UsersUtil.updateFromTo(user, userTo)); - } - - @CacheEvict(value = "users", allEntries = true) - @Transactional - public void enable(int id, boolean enabled) { - checkModificationAllowed(id); - User user = get(id); - user.setEnabled(enabled); - repository.save(user); // !! need only for JDBC implementation - } - - @Override - public AuthorizedUser loadUserByUsername(String email) throws UsernameNotFoundException { - User user = repository.getByEmail(email.toLowerCase()); - if (user == null) { - throw new UsernameNotFoundException("User " + email + " is not found"); - } - return new AuthorizedUser(user); - } - - private User prepareAndSave(User user) { - return repository.save(prepareToSave(user, passwordEncoder)); - } - - public User getWithMeals(int id) { - return checkNotFound(repository.getWithMeals(id), id); - } - - protected void checkModificationAllowed(int id) { - if (modificationRestriction && id < AbstractBaseEntity.START_SEQ + 2) { - throw new UpdateRestrictionException(); - } + checkNotFoundWithId(repository.save(user), user.id()); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/to/BaseTo.java b/src/main/java/ru/javawebinar/topjava/to/BaseTo.java deleted file mode 100644 index 7ccb8d970..000000000 --- a/src/main/java/ru/javawebinar/topjava/to/BaseTo.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.javawebinar.topjava.to; - -import io.swagger.annotations.ApiModelProperty; -import ru.javawebinar.topjava.HasId; - -public abstract class BaseTo implements HasId { - @ApiModelProperty(hidden = true) - protected Integer id; - - public BaseTo() { - } - - public BaseTo(Integer id) { - this.id = id; - } - - @Override - public Integer getId() { - return id; - } - - @Override - public void setId(Integer id) { - this.id = id; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/to/MealTo.java b/src/main/java/ru/javawebinar/topjava/to/MealTo.java index 059f14a44..d14feae79 100644 --- a/src/main/java/ru/javawebinar/topjava/to/MealTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/MealTo.java @@ -1,10 +1,9 @@ package ru.javawebinar.topjava.to; -import java.beans.ConstructorProperties; import java.time.LocalDateTime; -import java.util.Objects; -public class MealTo extends BaseTo { +public class MealTo { + private final Integer id; private final LocalDateTime dateTime; @@ -14,15 +13,18 @@ public class MealTo extends BaseTo { private final boolean excess; - @ConstructorProperties({"id", "dateTime", "description", "calories", "excess"}) public MealTo(Integer id, LocalDateTime dateTime, String description, int calories, boolean excess) { - super(id); + this.id = id; this.dateTime = dateTime; this.description = description; this.calories = calories; this.excess = excess; } + public Integer getId() { + return id; + } + public LocalDateTime getDateTime() { return dateTime; } @@ -39,23 +41,6 @@ public boolean isExcess() { return excess; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MealTo mealTo = (MealTo) o; - return calories == mealTo.calories && - excess == mealTo.excess && - Objects.equals(id, mealTo.id) && - Objects.equals(dateTime, mealTo.dateTime) && - Objects.equals(description, mealTo.description); - } - - @Override - public int hashCode() { - return Objects.hash(id, dateTime, description, calories, excess); - } - @Override public String toString() { return "MealTo{" + diff --git a/src/main/java/ru/javawebinar/topjava/to/UserTo.java b/src/main/java/ru/javawebinar/topjava/to/UserTo.java deleted file mode 100644 index 4fc5aec92..000000000 --- a/src/main/java/ru/javawebinar/topjava/to/UserTo.java +++ /dev/null @@ -1,91 +0,0 @@ -package ru.javawebinar.topjava.to; - -import org.hibernate.validator.constraints.Range; -import ru.javawebinar.topjava.HasIdAndEmail; -import ru.javawebinar.topjava.util.UsersUtil; -import ru.javawebinar.topjava.util.validation.NoHtml; - -import javax.validation.constraints.Email; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.io.Serial; -import java.io.Serializable; - -public class UserTo extends BaseTo implements HasIdAndEmail, Serializable { - @Serial - private static final long serialVersionUID = 1L; - - @NotBlank - @Size(min = 2, max = 100) - @NoHtml - private String name; - - @Email - @NotBlank - @Size(max = 100) - @NoHtml // https://stackoverflow.com/questions/17480809 - private String email; - - @NotBlank - @Size(min = 5, max = 32) - private String password; - - @Range(min = 10, max = 10000) - @NotNull - private Integer caloriesPerDay = UsersUtil.DEFAULT_CALORIES_PER_DAY; - - public UserTo() { - } - - public UserTo(Integer id, String name, String email, String password, int caloriesPerDay) { - super(id); - this.name = name; - this.email = email; - this.password = password; - this.caloriesPerDay = caloriesPerDay; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public void setCaloriesPerDay(Integer caloriesPerDay) { - this.caloriesPerDay = caloriesPerDay; - } - - public Integer getCaloriesPerDay() { - return caloriesPerDay; - } - - @Override - public String toString() { - return "UserTo{" + - "id=" + id + - ", name='" + name + '\'' + - ", email='" + email + '\'' + - ", caloriesPerDay='" + caloriesPerDay + '\'' + - '}'; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 09052faa5..a4665e2ae 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -7,10 +7,10 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; public class DateTimeUtil { - public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm"; - public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN); + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); // DB doesn't support LocalDate.MIN/MAX private static final LocalDateTime MIN_DATE = LocalDateTime.of(1, 1, 1, 0, 0); @@ -24,7 +24,7 @@ public static LocalDateTime atStartOfDayOrMin(LocalDate localDate) { } public static LocalDateTime atStartOfNextDayOrMax(LocalDate localDate) { - return localDate != null ? localDate.plusDays(1).atStartOfDay() : MAX_DATE; + return localDate != null ? localDate.plus(1, ChronoUnit.DAYS).atStartOfDay() : MAX_DATE; } public static String toString(LocalDateTime ldt) { diff --git a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java index af07e74d3..d767652eb 100644 --- a/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/MealsUtil.java @@ -12,6 +12,7 @@ import java.util.stream.Collectors; public class MealsUtil { + public static final int DEFAULT_CALORIES_PER_DAY = 2000; private MealsUtil() { } @@ -34,10 +35,10 @@ private static List filterByPredicate(Collection meals, int calori return meals.stream() .filter(filter) .map(meal -> createTo(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay)) - .toList(); + .collect(Collectors.toList()); } - public static MealTo createTo(Meal meal, boolean excess) { + private static MealTo createTo(Meal meal, boolean excess) { return new MealTo(meal.getId(), meal.getDateTime(), meal.getDescription(), meal.getCalories(), excess); } } diff --git a/src/main/java/ru/javawebinar/topjava/util/UsersUtil.java b/src/main/java/ru/javawebinar/topjava/util/UsersUtil.java deleted file mode 100644 index bdb4291f7..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/UsersUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.javawebinar.topjava.util; - -import org.springframework.security.crypto.password.PasswordEncoder; -import ru.javawebinar.topjava.model.Role; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.to.UserTo; - -public class UsersUtil { - - public static final int DEFAULT_CALORIES_PER_DAY = 2000; - - public static User createNewFromTo(UserTo userTo) { - return new User(null, userTo.getName(), userTo.getEmail().toLowerCase(), userTo.getPassword(), userTo.getCaloriesPerDay(), Role.USER); - } - - public static UserTo asTo(User user) { - return new UserTo(user.getId(), user.getName(), user.getEmail(), user.getPassword(), user.getCaloriesPerDay()); - } - - public static User updateFromTo(User user, UserTo userTo) { - user.setName(userTo.getName()); - user.setEmail(userTo.getEmail().toLowerCase()); - user.setCaloriesPerDay(userTo.getCaloriesPerDay()); - user.setPassword(userTo.getPassword()); - return user; - } - - public static User prepareToSave(User user, PasswordEncoder passwordEncoder) { - user.setPassword(passwordEncoder.encode(user.getPassword())); - user.setEmail(user.getEmail().toLowerCase()); - return user; - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java new file mode 100644 index 000000000..5212eea75 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -0,0 +1,46 @@ +package ru.javawebinar.topjava.util; + + +import ru.javawebinar.topjava.model.AbstractBaseEntity; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +public class ValidationUtil { + + private ValidationUtil() { + } + + public static T checkNotFoundWithId(T object, int id) { + checkNotFoundWithId(object != null, id); + return object; + } + + public static void checkNotFoundWithId(boolean found, int id) { + checkNotFound(found, "id=" + id); + } + + public static T checkNotFound(T object, String msg) { + checkNotFound(object != null, msg); + return object; + } + + public static void checkNotFound(boolean found, String msg) { + if (!found) { + throw new NotFoundException("Not found entity with " + msg); + } + } + + public static void checkNew(AbstractBaseEntity entity) { + if (!entity.isNew()) { + throw new IllegalArgumentException(entity + " must be new (id=null)"); + } + } + + public static void assureIdConsistent(AbstractBaseEntity entity, int id) { +// conservative when you reply, but accept liberally (http://stackoverflow.com/a/32728226/548473) + if (entity.isNew()) { + entity.setId(id); + } else if (entity.id() != id) { + throw new IllegalArgumentException(entity + " must be with id=" + id); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ApplicationException.java b/src/main/java/ru/javawebinar/topjava/util/exception/ApplicationException.java deleted file mode 100644 index 83bf0fed5..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/exception/ApplicationException.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.javawebinar.topjava.util.exception; - -public class ApplicationException extends RuntimeException { - - private final ErrorType type; - private final String msgCode; - - public ApplicationException(String msgCode, ErrorType type) { - this.msgCode = msgCode; - this.type = type; - } - - public String getMsgCode() { - return msgCode; - } - - public ErrorType getType() { - return type; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java deleted file mode 100644 index 6a1f7a16a..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorInfo.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.javawebinar.topjava.util.exception; - -public class ErrorInfo { - private final String url; - private final ErrorType type; - private final String typeMessage; - private final String[] details; - - public ErrorInfo(CharSequence url, ErrorType type, String typeMessage, String... details) { - this.url = url.toString(); - this.type = type; - this.typeMessage = typeMessage; - this.details = details; - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java b/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java deleted file mode 100644 index 5c16cf8ff..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/exception/ErrorType.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.javawebinar.topjava.util.exception; - -import org.springframework.http.HttpStatus; - -public enum ErrorType { - APP_ERROR("error.appError", HttpStatus.INTERNAL_SERVER_ERROR), - // http://stackoverflow.com/a/22358422/548473 - DATA_NOT_FOUND("error.dataNotFound", HttpStatus.UNPROCESSABLE_ENTITY), - DATA_ERROR("error.dataError", HttpStatus.CONFLICT), - VALIDATION_ERROR("error.validationError", HttpStatus.UNPROCESSABLE_ENTITY), - WRONG_REQUEST("error.wrongRequest", HttpStatus.BAD_REQUEST); - - private final String errorCode; - private final HttpStatus status; - - ErrorType(String errorCode, HttpStatus status) { - this.errorCode = errorCode; - this.status = status; - } - - public String getErrorCode() { - return errorCode; - } - - public HttpStatus getStatus() { - return status; - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/IllegalRequestDataException.java b/src/main/java/ru/javawebinar/topjava/util/exception/IllegalRequestDataException.java deleted file mode 100644 index 2b144f91c..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/exception/IllegalRequestDataException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.javawebinar.topjava.util.exception; - -public class IllegalRequestDataException extends RuntimeException { - public IllegalRequestDataException(String msg) { - super(msg); - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/util/exception/UpdateRestrictionException.java b/src/main/java/ru/javawebinar/topjava/util/exception/UpdateRestrictionException.java deleted file mode 100644 index abf3d36fe..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/exception/UpdateRestrictionException.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.javawebinar.topjava.util.exception; - -public class UpdateRestrictionException extends ApplicationException { - public static final String EXCEPTION_UPDATE_RESTRICTION = "exception.user.updateRestriction"; - - public UpdateRestrictionException() { - super(EXCEPTION_UPDATE_RESTRICTION, ErrorType.VALIDATION_ERROR); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/util/validation/NoHtml.java b/src/main/java/ru/javawebinar/topjava/util/validation/NoHtml.java deleted file mode 100644 index b1b2a3b87..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/validation/NoHtml.java +++ /dev/null @@ -1,23 +0,0 @@ -package ru.javawebinar.topjava.util.validation; - -import javax.validation.Constraint; -import javax.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -@Documented -@Constraint(validatedBy = NoHtmlValidator.class) -@Target({METHOD, FIELD}) -@Retention(RUNTIME) -public @interface NoHtml { - String message() default "Unsafe html content"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} diff --git a/src/main/java/ru/javawebinar/topjava/util/validation/NoHtmlValidator.java b/src/main/java/ru/javawebinar/topjava/util/validation/NoHtmlValidator.java deleted file mode 100644 index 26d52d7ef..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/validation/NoHtmlValidator.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.javawebinar.topjava.util.validation; - -import org.jsoup.Jsoup; -import org.jsoup.safety.Safelist; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; - -public class NoHtmlValidator implements ConstraintValidator { - @Override - public boolean isValid(String value, ConstraintValidatorContext ctx) { - return value == null || Jsoup.isValid(value, Safelist.none()); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/util/validation/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/validation/ValidationUtil.java deleted file mode 100644 index 12840a142..000000000 --- a/src/main/java/ru/javawebinar/topjava/util/validation/ValidationUtil.java +++ /dev/null @@ -1,92 +0,0 @@ -package ru.javawebinar.topjava.util.validation; - -import org.slf4j.Logger; -import org.springframework.core.NestedExceptionUtils; -import org.springframework.lang.NonNull; -import ru.javawebinar.topjava.HasId; -import ru.javawebinar.topjava.util.exception.ErrorType; -import ru.javawebinar.topjava.util.exception.IllegalRequestDataException; -import ru.javawebinar.topjava.util.exception.NotFoundException; - -import javax.servlet.http.HttpServletRequest; -import javax.validation.*; -import java.util.Set; - -public class ValidationUtil { - - private static final Validator validator; - - static { - // From Javadoc: implementations are thread-safe and instances are typically cached and reused. - ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); - // From Javadoc: implementations of this interface must be thread-safe - validator = factory.getValidator(); - } - - private ValidationUtil() { - } - - public static void validate(T bean) { - // https://alexkosarev.name/2018/07/30/bean-validation-api/ - Set> violations = validator.validate(bean); - if (!violations.isEmpty()) { - throw new ConstraintViolationException(violations); - } - } - - public static T checkNotFound(T object, int id) { - checkNotFound(object != null, id); - return object; - } - - public static void checkNotFound(boolean found, int id) { - checkNotFound(found, "id=" + id); - } - - public static T checkNotFound(T object, String msg) { - checkNotFound(object != null, msg); - return object; - } - - public static void checkNotFound(boolean found, String msg) { - if (!found) { - throw new NotFoundException("Not found entity with " + msg); - } - } - - public static void checkIsNew(HasId bean) { - if (!bean.isNew()) { - throw new IllegalRequestDataException(bean + " must be new (id=null)"); - } - } - - public static void assureIdConsistent(HasId bean, int id) { -// conservative when you reply, but accept liberally (http://stackoverflow.com/a/32728226/548473) - if (bean.isNew()) { - bean.setId(id); - } else if (bean.id() != id) { - throw new IllegalRequestDataException(bean + " must be with id=" + id); - } - } - - // https://stackoverflow.com/a/65442410/548473 - @NonNull - public static Throwable getRootCause(@NonNull Throwable t) { - Throwable rootCause = NestedExceptionUtils.getRootCause(t); - return rootCause != null ? rootCause : t; - } - - public static String getMessage(Throwable e) { - return e.getLocalizedMessage() != null ? e.getLocalizedMessage() : e.getClass().getName(); - } - - public static Throwable logAndGetRootCause(Logger log, HttpServletRequest req, Exception e, boolean logStackTrace, ErrorType errorType) { - Throwable rootCause = ValidationUtil.getRootCause(e); - if (logStackTrace) { - log.error(errorType + " at request " + req.getRequestURL(), rootCause); - } else { - log.warn("{} at request {}: {}", errorType, req.getRequestURL(), rootCause.toString()); - } - return rootCause; - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java deleted file mode 100644 index 543595bd2..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ /dev/null @@ -1,94 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.http.ResponseEntity; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.validation.BindException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import ru.javawebinar.topjava.util.validation.ValidationUtil; -import ru.javawebinar.topjava.util.exception.*; - -import javax.servlet.http.HttpServletRequest; -import java.util.Map; - -import static ru.javawebinar.topjava.util.exception.ErrorType.*; - -@RestControllerAdvice(annotations = RestController.class) -@Order(Ordered.HIGHEST_PRECEDENCE + 5) -public class ExceptionInfoHandler { - private static final Logger log = LoggerFactory.getLogger(ExceptionInfoHandler.class); - - public static final String EXCEPTION_DUPLICATE_EMAIL = "exception.user.duplicateEmail"; - public static final String EXCEPTION_DUPLICATE_DATETIME = "exception.meal.duplicateDateTime"; - - private static final Map CONSTRAINTS_I18N_MAP = Map.of( - "users_unique_email_idx", EXCEPTION_DUPLICATE_EMAIL, - "meal_unique_user_datetime_idx", EXCEPTION_DUPLICATE_DATETIME); - - private final MessageSourceAccessor messageSourceAccessor; - - public ExceptionInfoHandler(MessageSourceAccessor messageSourceAccessor) { - this.messageSourceAccessor = messageSourceAccessor; - } - - @ExceptionHandler(NotFoundException.class) - public ResponseEntity notFoundError(HttpServletRequest req, NotFoundException e) { - return logAndGetErrorInfo(req, e, false, DATA_NOT_FOUND); - } - - @ExceptionHandler(ApplicationException.class) - public ResponseEntity updateRestrictionError(HttpServletRequest req, ApplicationException appEx) { - return logAndGetErrorInfo(req, appEx, false, appEx.getType(), messageSourceAccessor.getMessage(appEx.getMsgCode())); - } - - @ExceptionHandler(DataIntegrityViolationException.class) - public ResponseEntity conflict(HttpServletRequest req, DataIntegrityViolationException e) { - String rootMsg = ValidationUtil.getRootCause(e).getMessage(); - if (rootMsg != null) { - String lowerCaseMsg = rootMsg.toLowerCase(); - for (Map.Entry entry : CONSTRAINTS_I18N_MAP.entrySet()) { - if (lowerCaseMsg.contains(entry.getKey())) { - return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR, messageSourceAccessor.getMessage(entry.getValue())); - } - } - } - return logAndGetErrorInfo(req, e, true, DATA_ERROR); - } - - @ExceptionHandler(BindException.class) - public ResponseEntity bindValidationError(HttpServletRequest req, BindException e) { - String[] details = e.getBindingResult().getFieldErrors().stream() - .map(messageSourceAccessor::getMessage) - .toArray(String[]::new); - - return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR, details); - } - - @ExceptionHandler({IllegalRequestDataException.class, MethodArgumentTypeMismatchException.class, HttpMessageNotReadableException.class}) - public ResponseEntity validationError(HttpServletRequest req, Exception e) { - return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR); - } - - @ExceptionHandler(Exception.class) - public ResponseEntity internalError(HttpServletRequest req, Exception e) { - return logAndGetErrorInfo(req, e, true, APP_ERROR); - } - - // https://stackoverflow.com/questions/538870/should-private-helper-methods-be-static-if-they-can-be-static - private ResponseEntity logAndGetErrorInfo(HttpServletRequest req, Exception e, boolean logStackTrace, ErrorType errorType, String... details) { - Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, logStackTrace, errorType); - return ResponseEntity.status(errorType.getStatus()) - .body(new ErrorInfo(req.getRequestURL(), errorType, - messageSourceAccessor.getMessage(errorType.getErrorCode()), - details.length != 0 ? details : new String[]{ValidationUtil.getMessage(rootCause)}) - ); - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java b/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java deleted file mode 100644 index 521985ae0..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.NoHandlerFoundException; -import ru.javawebinar.topjava.util.exception.ApplicationException; -import ru.javawebinar.topjava.util.exception.ErrorType; -import ru.javawebinar.topjava.util.validation.ValidationUtil; - -import javax.servlet.http.HttpServletRequest; -import java.util.Map; - -@ControllerAdvice -public class GlobalExceptionHandler { - private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); - - private final MessageSourceAccessor messageSourceAccessor; - - public GlobalExceptionHandler(MessageSourceAccessor messageSourceAccessor) { - this.messageSourceAccessor = messageSourceAccessor; - } - - @ExceptionHandler(NoHandlerFoundException.class) - public ModelAndView wrongRequest(HttpServletRequest req, NoHandlerFoundException e) { - return logAndGetExceptionView(req, e, false, ErrorType.WRONG_REQUEST, null); - } - - @ExceptionHandler(ApplicationException.class) - public ModelAndView updateRestrictionException(HttpServletRequest req, ApplicationException appEx) { - return logAndGetExceptionView(req, appEx, false, appEx.getType(), appEx.getMsgCode()); - } - - @ExceptionHandler(Exception.class) - public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { - log.error("Exception at request " + req.getRequestURL(), e); - return logAndGetExceptionView(req, e, true, ErrorType.APP_ERROR, null); - } - - private ModelAndView logAndGetExceptionView(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType, String code) { - Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, logException, errorType); - - ModelAndView mav = new ModelAndView("exception", - Map.of("exception", rootCause, "message", code != null ? messageSourceAccessor.getMessage(code) : ValidationUtil.getMessage(rootCause), - "typeMessage", messageSourceAccessor.getMessage(errorType.getErrorCode()), - "status", errorType.getStatus())); - mav.setStatus(errorType.getStatus()); - return mav; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/MealServlet.java b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java new file mode 100644 index 000000000..a8f609938 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/MealServlet.java @@ -0,0 +1,94 @@ +package ru.javawebinar.topjava.web; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.util.StringUtils; +import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.web.meal.MealRestController; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; +import java.util.Objects; + +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalDate; +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalTime; + +public class MealServlet extends HttpServlet { + + private ConfigurableApplicationContext springContext; + private MealRestController mealController; + + @Override + public void init() { + springContext = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/spring-db.xml"); + mealController = springContext.getBean(MealRestController.class); + } + + @Override + public void destroy() { + springContext.close(); + super.destroy(); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.setCharacterEncoding("UTF-8"); + Meal meal = new Meal( + LocalDateTime.parse(request.getParameter("dateTime")), + request.getParameter("description"), + Integer.parseInt(request.getParameter("calories"))); + + if (StringUtils.hasLength(request.getParameter("id"))) { + mealController.update(meal, getId(request)); + } else { + mealController.create(meal); + } + response.sendRedirect("meals"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String action = request.getParameter("action"); + + switch (action == null ? "all" : action) { + case "delete": + int id = getId(request); + mealController.delete(id); + response.sendRedirect("meals"); + break; + case "create": + case "update": + final Meal meal = "create".equals(action) ? + new Meal(LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES), "", 1000) : + mealController.get(getId(request)); + request.setAttribute("meal", meal); + request.getRequestDispatcher("/mealForm.jsp").forward(request, response); + break; + case "filter": + LocalDate startDate = parseLocalDate(request.getParameter("startDate")); + LocalDate endDate = parseLocalDate(request.getParameter("endDate")); + LocalTime startTime = parseLocalTime(request.getParameter("startTime")); + LocalTime endTime = parseLocalTime(request.getParameter("endTime")); + request.setAttribute("meals", mealController.getBetween(startDate, startTime, endDate, endTime)); + request.getRequestDispatcher("/meals.jsp").forward(request, response); + break; + case "all": + default: + request.setAttribute("meals", mealController.getAll()); + request.getRequestDispatcher("/meals.jsp").forward(request, response); + break; + } + } + + private int getId(HttpServletRequest request) { + String paramId = Objects.requireNonNull(request.getParameter("id")); + return Integer.parseInt(paramId); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/RootController.java b/src/main/java/ru/javawebinar/topjava/web/RootController.java deleted file mode 100644 index c6fbe3a78..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/RootController.java +++ /dev/null @@ -1,40 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import springfox.documentation.annotations.ApiIgnore; - -@ApiIgnore -@Controller -public class RootController { - private static final Logger log = LoggerFactory.getLogger(RootController.class); - - @GetMapping("/") - public String root() { - log.info("root"); - return "redirect:meals"; - } - - // @Secured("ROLE_ADMIN") - @PreAuthorize("hasRole('ADMIN')") - @GetMapping("/users") - public String getUsers() { - log.info("users"); - return "users"; - } - - @GetMapping("/login") - public String login() { - log.info("login"); - return "login"; - } - - @GetMapping("/meals") - public String getMeals() { - log.info("meals"); - return "meals"; - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java index ac5b99f05..4bad5863e 100644 --- a/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java +++ b/src/main/java/ru/javawebinar/topjava/web/SecurityUtil.java @@ -1,34 +1,25 @@ package ru.javawebinar.topjava.web; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import ru.javawebinar.topjava.AuthorizedUser; +import ru.javawebinar.topjava.model.AbstractBaseEntity; -import static java.util.Objects.requireNonNull; +import static ru.javawebinar.topjava.util.MealsUtil.DEFAULT_CALORIES_PER_DAY; public class SecurityUtil { - private SecurityUtil() { - } + private static int id = AbstractBaseEntity.START_SEQ; - public static AuthorizedUser safeGet() { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth == null) { - return null; - } - Object principal = auth.getPrincipal(); - return (principal instanceof AuthorizedUser) ? (AuthorizedUser) principal : null; + private SecurityUtil() { } - public static AuthorizedUser get() { - return requireNonNull(safeGet(), "No authorized user found"); + public static int authUserId() { + return id; } - public static int authUserId() { - return get().getUserTo().id(); + public static void setAuthUserId(int id) { + SecurityUtil.id = id; } public static int authUserCaloriesPerDay() { - return get().getUserTo().getCaloriesPerDay(); + return DEFAULT_CALORIES_PER_DAY; } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/UserServlet.java b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java new file mode 100644 index 000000000..226023400 --- /dev/null +++ b/src/main/java/ru/javawebinar/topjava/web/UserServlet.java @@ -0,0 +1,28 @@ +package ru.javawebinar.topjava.web; + +import org.slf4j.Logger; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.slf4j.LoggerFactory.getLogger; + +public class UserServlet extends HttpServlet { + private static final Logger log = getLogger(UserServlet.class); + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + int userId = Integer.parseInt(request.getParameter("userId")); + SecurityUtil.setAuthUserId(userId); + response.sendRedirect("meals"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + log.debug("forward to users"); + request.getRequestDispatcher("/users.jsp").forward(request, response); + } +} diff --git a/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java b/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java deleted file mode 100644 index 15448ce8c..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java +++ /dev/null @@ -1,36 +0,0 @@ -package ru.javawebinar.topjava.web.converter; - -import org.springframework.format.Formatter; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; -import java.util.Locale; - -public class DateTimeFormatters { - public static class LocalDateFormatter implements Formatter { - - @Override - public LocalDate parse(String text, Locale locale) { - return LocalDate.parse(text); - } - - @Override - public String print(LocalDate lt, Locale locale) { - return lt.format(DateTimeFormatter.ISO_LOCAL_DATE); - } - } - - public static class LocalTimeFormatter implements Formatter { - - @Override - public LocalTime parse(String text, Locale locale) { - return LocalTime.parse(text); - } - - @Override - public String print(LocalTime lt, Locale locale) { - return lt.format(DateTimeFormatter.ISO_LOCAL_TIME); - } - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java b/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java deleted file mode 100644 index 8237df93b..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/json/JacksonObjectMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package ru.javawebinar.topjava.web.json; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - -/** - *

- * Handling Hibernate lazy-loading - * - * @link https://github.com/FasterXML/jackson - * @link https://github.com/FasterXML/jackson-datatype-hibernate - * @link https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers - */ -public class JacksonObjectMapper extends ObjectMapper { - - private static final ObjectMapper MAPPER = new JacksonObjectMapper(); - - private JacksonObjectMapper() { - registerModule(new Hibernate5Module()); - - registerModule(new JavaTimeModule()); - configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - - setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); - setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - setSerializationInclusion(JsonInclude.Include.NON_NULL); - } - - public static ObjectMapper getMapper() { - return MAPPER; - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java deleted file mode 100644 index d3bf1ac04..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java +++ /dev/null @@ -1,49 +0,0 @@ -package ru.javawebinar.topjava.web.json; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectReader; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import static ru.javawebinar.topjava.web.json.JacksonObjectMapper.getMapper; - -public class JsonUtil { - - public static List readValues(String json, Class clazz) { - ObjectReader reader = getMapper().readerFor(clazz); - try { - return reader.readValues(json).readAll(); - } catch (IOException e) { - throw new IllegalArgumentException("Invalid read array from JSON:\n'" + json + "'", e); - } - } - - public static T readValue(String json, Class clazz) { - try { - return getMapper().readValue(json, clazz); - } catch (IOException e) { - throw new IllegalArgumentException("Invalid read from JSON:\n'" + json + "'", e); - } - } - - public static String writeValue(T obj) { - try { - return getMapper().writeValueAsString(obj); - } catch (JsonProcessingException e) { - throw new IllegalStateException("Invalid write to JSON:\n'" + obj + "'", e); - } - } - - public static String writeAdditionProps(T obj, String addName, Object addValue) { - return writeAdditionProps(obj, Map.of(addName, addValue)); - } - - public static String writeAdditionProps(T obj, Map addProps) { - Map map = getMapper().convertValue(obj, new TypeReference<>() {}); - map.putAll(addProps); - return writeValue(map); - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java deleted file mode 100644 index 63d9f9415..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java +++ /dev/null @@ -1,72 +0,0 @@ -package ru.javawebinar.topjava.web.meal; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.lang.Nullable; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.service.MealService; -import ru.javawebinar.topjava.to.MealTo; -import ru.javawebinar.topjava.util.MealsUtil; -import ru.javawebinar.topjava.web.SecurityUtil; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; - -import static ru.javawebinar.topjava.util.validation.ValidationUtil.assureIdConsistent; -import static ru.javawebinar.topjava.util.validation.ValidationUtil.checkIsNew; - -public abstract class AbstractMealController { - private final Logger log = LoggerFactory.getLogger(getClass()); - - @Autowired - private MealService service; - - public Meal get(int id) { - int userId = SecurityUtil.authUserId(); - log.info("get meal {} for user {}", id, userId); - return service.get(id, userId); - } - - public void delete(int id) { - int userId = SecurityUtil.authUserId(); - log.info("delete meal {} for user {}", id, userId); - service.delete(id, userId); - } - - public List getAll() { - int userId = SecurityUtil.authUserId(); - log.info("getAll for user {}", userId); - return MealsUtil.getTos(service.getAll(userId), SecurityUtil.authUserCaloriesPerDay()); - } - - public Meal create(Meal meal) { - int userId = SecurityUtil.authUserId(); - log.info("create {} for user {}", meal, userId); - checkIsNew(meal); - return service.create(meal, userId); - } - - public void update(Meal meal, int id) { - int userId = SecurityUtil.authUserId(); - log.info("update {} for user {}", meal, userId); - assureIdConsistent(meal, id); - service.update(meal, userId); - } - - /** - *

    Filter separately - *
  1. by date
  2. - *
  3. by time for every date
  4. - *
- */ - public List getBetween(@Nullable LocalDate startDate, @Nullable LocalTime startTime, - @Nullable LocalDate endDate, @Nullable LocalTime endTime) { - int userId = SecurityUtil.authUserId(); - log.info("getBetween dates({} - {}) time({} - {}) for user {}", startDate, endDate, startTime, endTime, userId); - - List mealsDateFiltered = service.getBetweenInclusive(startDate, endDate, userId); - return MealsUtil.getFilteredTos(mealsDateFiltered, SecurityUtil.authUserCaloriesPerDay(), startTime, endTime); - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java index 0d3eb7e83..bbfe35e3f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -1,70 +1,76 @@ package ru.javawebinar.topjava.web.meal; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import ru.javawebinar.topjava.View; +import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.model.Meal; +import ru.javawebinar.topjava.service.MealService; import ru.javawebinar.topjava.to.MealTo; +import ru.javawebinar.topjava.util.MealsUtil; +import ru.javawebinar.topjava.web.SecurityUtil; -import java.net.URI; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; -@RestController -@RequestMapping(value = MealRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) -public class MealRestController extends AbstractMealController { - static final String REST_URL = "/rest/profile/meals"; +import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; - @Override - @GetMapping("/{id}") - public Meal get(@PathVariable int id) { - return super.get(id); - } +@Controller +public class MealRestController { + private static final Logger log = LoggerFactory.getLogger(MealRestController.class); + + private final MealService service; - @Override - @DeleteMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(@PathVariable int id) { - super.delete(id); + public MealRestController(MealService service) { + this.service = service; } - @Override - @GetMapping - public List getAll() { - return super.getAll(); + public Meal get(int id) { + int userId = SecurityUtil.authUserId(); + log.info("get meal {} for user {}", id, userId); + return service.get(id, userId); } - @Override - @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) - @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@Validated(View.Web.class) @RequestBody Meal meal, @PathVariable int id) { - super.update(meal, id); + public void delete(int id) { + int userId = SecurityUtil.authUserId(); + log.info("delete meal {} for user {}", id, userId); + service.delete(id, userId); } - @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity createWithLocation(@Validated(View.Web.class) @RequestBody Meal meal) { - Meal created = super.create(meal); + public List getAll() { + int userId = SecurityUtil.authUserId(); + log.info("getAll for user {}", userId); + return MealsUtil.getTos(service.getAll(userId), SecurityUtil.authUserCaloriesPerDay()); + } - URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() - .path(REST_URL + "/{id}") - .buildAndExpand(created.getId()).toUri(); + public Meal create(Meal meal) { + int userId = SecurityUtil.authUserId(); + checkNew(meal); + log.info("create {} for user {}", meal, userId); + return service.create(meal, userId); + } - return ResponseEntity.created(uriOfNewResource).body(created); + public void update(Meal meal, int id) { + int userId = SecurityUtil.authUserId(); + assureIdConsistent(meal, id); + log.info("update {} for user {}", meal, userId); + service.update(meal, userId); } - @Override - @GetMapping("/filter") - public List getBetween( - @RequestParam @Nullable LocalDate startDate, - @RequestParam @Nullable LocalTime startTime, - @RequestParam @Nullable LocalDate endDate, - @RequestParam @Nullable LocalTime endTime) { - return super.getBetween(startDate, startTime, endDate, endTime); + /** + *
    Filter separately + *
  1. by date
  2. + *
  3. by time for every date
  4. + *
+ */ + public List getBetween(@Nullable LocalDate startDate, @Nullable LocalTime startTime, + @Nullable LocalDate endDate, @Nullable LocalTime endTime) { + int userId = SecurityUtil.authUserId(); + log.info("getBetween dates({} - {}) time({} - {}) for user {}", startDate, endDate, startTime, endTime, userId); + + List mealsDateFiltered = service.getBetweenInclusive(startDate, endDate, userId); + return MealsUtil.getFilteredTos(mealsDateFiltered, SecurityUtil.authUserCaloriesPerDay(), startTime, endTime); } -} +} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java b/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java deleted file mode 100644 index 28a59fca7..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealUIController.java +++ /dev/null @@ -1,60 +0,0 @@ -package ru.javawebinar.topjava.web.meal; - -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.lang.Nullable; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import ru.javawebinar.topjava.View; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.to.MealTo; -import springfox.documentation.annotations.ApiIgnore; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; - -@ApiIgnore -@RestController -@RequestMapping(value = "/profile/meals", produces = MediaType.APPLICATION_JSON_VALUE) -public class MealUIController extends AbstractMealController { - - @Override - @GetMapping - public List getAll() { - return super.getAll(); - } - - @Override - @GetMapping("/{id}") - public Meal get(@PathVariable int id) { - return super.get(id); - } - - @Override - @DeleteMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(@PathVariable int id) { - super.delete(id); - } - - @PostMapping - @ResponseStatus(HttpStatus.NO_CONTENT) - public void createOrUpdate(@Validated(View.Web.class) Meal meal) { - if (meal.isNew()) { - super.create(meal); - } else { - super.update(meal, meal.getId()); - } - } - - @Override - @GetMapping("/filter") - public List getBetween( - @RequestParam @Nullable LocalDate startDate, - @RequestParam @Nullable LocalTime startTime, - @RequestParam @Nullable LocalDate endDate, - @RequestParam @Nullable LocalTime endTime) { - return super.getBetween(startDate, startTime, endDate, endTime); - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java index 77a09cdc6..0000f1c1e 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -3,17 +3,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.service.UserService; -import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.util.UsersUtil; import java.util.List; -import static ru.javawebinar.topjava.util.validation.ValidationUtil.assureIdConsistent; -import static ru.javawebinar.topjava.util.validation.ValidationUtil.checkIsNew; +import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; public abstract class AbstractUserController { protected final Logger log = LoggerFactory.getLogger(getClass()); @@ -21,14 +17,6 @@ public abstract class AbstractUserController { @Autowired private UserService service; - @Autowired - private UniqueMailValidator emailValidator; - - @InitBinder - protected void initBinder(WebDataBinder binder) { - binder.addValidators(emailValidator); - } - public List getAll() { log.info("getAll"); return service.getAll(); @@ -39,15 +27,9 @@ public User get(int id) { return service.get(id); } - public User create(UserTo userTo) { - log.info("create {}", userTo); - checkIsNew(userTo); - return service.create(UsersUtil.createNewFromTo(userTo)); - } - public User create(User user) { log.info("create {}", user); - checkIsNew(user); + checkNew(user); return service.create(user); } @@ -62,24 +44,8 @@ public void update(User user, int id) { service.update(user); } - public void update(UserTo userTo, int id) { - log.info("update {} with id={}", userTo, id); - assureIdConsistent(userTo, id); - service.update(userTo); - } - public User getByMail(String email) { log.info("getByEmail {}", email); return service.getByEmail(email); } - - public User getWithMeals(int id) { - log.info("getWithMeals {}", id); - return service.getWithMeals(id); - } - - public void enable(int id, boolean enabled) { - log.info(enabled ? "enable {}" : "disable {}", id); - service.enable(id, enabled); - } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java index 896b22050..b37a8ed6c 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminRestController.java @@ -1,73 +1,40 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import ru.javawebinar.topjava.View; +import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.model.User; -import java.net.URI; import java.util.List; -@RestController -@RequestMapping(value = AdminRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) +@Controller public class AdminRestController extends AbstractUserController { - static final String REST_URL = "/rest/admin/users"; - @Override - @GetMapping public List getAll() { return super.getAll(); } @Override - @GetMapping("/{id}") - public User get(@PathVariable int id) { + public User get(int id) { return super.get(id); } - @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity createWithLocation(@Validated(View.Web.class) @RequestBody User user) { - User created = super.create(user); - URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() - .path(REST_URL + "/{id}") - .buildAndExpand(created.getId()).toUri(); - return ResponseEntity.created(uriOfNewResource).body(created); + @Override + public User create(User user) { + return super.create(user); } @Override - @DeleteMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(@PathVariable int id) { + public void delete(int id) { super.delete(id); } @Override - @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) - @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@Validated(View.Web.class) @RequestBody User user, @PathVariable int id) { + public void update(User user, int id) { super.update(user, id); } @Override - @GetMapping("/by-email") - public User getByMail(@RequestParam String email) { + public User getByMail(String email) { return super.getByMail(email); } - - @GetMapping("/{id}/with-meals") - public User getWithMeals(@PathVariable int id) { - return super.getWithMeals(id); - } - - @Override - @PatchMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void enable(@PathVariable int id, @RequestParam boolean enabled) { - super.enable(id, enabled); - } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java deleted file mode 100644 index af939854a..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.javawebinar.topjava.web.user; - -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import ru.javawebinar.topjava.View; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.to.UserTo; -import springfox.documentation.annotations.ApiIgnore; - -import java.util.List; - -@ApiIgnore -@RestController -@RequestMapping(value = "/admin/users", produces = MediaType.APPLICATION_JSON_VALUE) -public class AdminUIController extends AbstractUserController { - - @Override - @GetMapping - public List getAll() { - return super.getAll(); - } - - @Override - @GetMapping("/{id}") - public User get(@PathVariable int id) { - return super.get(id); - } - - @Override - @DeleteMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(@PathVariable int id) { - super.delete(id); - } - - @PostMapping - public void createOrUpdate(@Validated(View.Web.class) UserTo userTo) { - if (userTo.isNew()) { - super.create(userTo); - } else { - super.update(userTo, userTo.id()); - } - } - - @Override - @PostMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void enable(@PathVariable int id, @RequestParam boolean enabled) { - super.enable(id, enabled); - } -} diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java index c33b8eb25..7d3702c31 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/ProfileRestController.java @@ -1,58 +1,22 @@ package ru.javawebinar.topjava.web.user; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import ru.javawebinar.topjava.AuthorizedUser; -import ru.javawebinar.topjava.View; +import org.springframework.stereotype.Controller; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.to.UserTo; -import springfox.documentation.annotations.ApiIgnore; -import java.net.URI; +import static ru.javawebinar.topjava.web.SecurityUtil.authUserId; -@RestController -@RequestMapping(value = ProfileRestController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE) +@Controller public class ProfileRestController extends AbstractUserController { - static final String REST_URL = "/rest/profile"; - @GetMapping - public User get(@AuthenticationPrincipal @ApiIgnore AuthorizedUser authUser) { - return super.get(authUser.getId()); + public User get() { + return super.get(authUserId()); } - @DeleteMapping - @ResponseStatus(HttpStatus.NO_CONTENT) - public void delete(@AuthenticationPrincipal @ApiIgnore AuthorizedUser authUser) { - super.delete(authUser.getId()); + public void delete() { + super.delete(authUserId()); } - @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - @ResponseStatus(HttpStatus.CREATED) - public ResponseEntity register(@Validated(View.Web.class) @RequestBody UserTo userTo) { - User created = super.create(userTo); - URI uriOfNewResource = ServletUriComponentsBuilder.fromCurrentContextPath() - .path(REST_URL).build().toUri(); - return ResponseEntity.created(uriOfNewResource).body(created); - } - - @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - @ResponseStatus(HttpStatus.NO_CONTENT) - public void update(@Validated(View.Web.class) @RequestBody UserTo userTo, @ApiIgnore @AuthenticationPrincipal AuthorizedUser authUser) { - super.update(userTo, authUser.getId()); - } - - @GetMapping("/text") - public String testUTF() { - return "Русский текст"; - } - - @GetMapping("/with-meals") - public User getWithMeals( @ApiIgnore @AuthenticationPrincipal AuthorizedUser authUser) { - return super.getWithMeals(authUser.getId()); + public void update(User user) { + super.update(user, authUserId()); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java deleted file mode 100644 index e83ee01d1..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/user/ProfileUIController.java +++ /dev/null @@ -1,56 +0,0 @@ -package ru.javawebinar.topjava.web.user; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.validation.BindingResult; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.support.SessionStatus; -import ru.javawebinar.topjava.AuthorizedUser; -import ru.javawebinar.topjava.View; -import ru.javawebinar.topjava.to.UserTo; -import springfox.documentation.annotations.ApiIgnore; - -@ApiIgnore -@Controller -@RequestMapping("/profile") -public class ProfileUIController extends AbstractUserController { - - @GetMapping - public String profile(ModelMap model, @AuthenticationPrincipal AuthorizedUser authUser) { - model.addAttribute("userTo", authUser.getUserTo()); - return "profile"; - } - - @PostMapping - public String updateProfile(@Validated(View.Web.class) UserTo userTo, BindingResult result, SessionStatus status, @AuthenticationPrincipal AuthorizedUser authUser) { - if (result.hasErrors()) { - return "profile"; - } - super.update(userTo, authUser.getId()); - authUser.setTo(userTo); - status.setComplete(); - return "redirect:/meals"; - } - - @GetMapping("/register") - public String register(ModelMap model) { - model.addAttribute("userTo", new UserTo()); - model.addAttribute("register", true); - return "profile"; - } - - @PostMapping("/register") - public String saveRegister(@Validated(View.Web.class) UserTo userTo, BindingResult result, SessionStatus status, ModelMap model) { - if (result.hasErrors()) { - model.addAttribute("register", true); - return "profile"; - } - super.create(userTo); - status.setComplete(); - return "redirect:/login?message=app.registered&username=" + userTo.getEmail(); - } -} \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/web/user/UniqueMailValidator.java b/src/main/java/ru/javawebinar/topjava/web/user/UniqueMailValidator.java deleted file mode 100644 index d72b8aa5c..000000000 --- a/src/main/java/ru/javawebinar/topjava/web/user/UniqueMailValidator.java +++ /dev/null @@ -1,54 +0,0 @@ -package ru.javawebinar.topjava.web.user; - - -import org.springframework.lang.Nullable; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import org.springframework.validation.Errors; -import ru.javawebinar.topjava.HasIdAndEmail; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.repository.UserRepository; -import ru.javawebinar.topjava.web.ExceptionInfoHandler; -import ru.javawebinar.topjava.web.SecurityUtil; - -import javax.servlet.http.HttpServletRequest; - -@Component -public class UniqueMailValidator implements org.springframework.validation.Validator { - - private final UserRepository repository; - private final HttpServletRequest request; - - public UniqueMailValidator(UserRepository repository, @Nullable HttpServletRequest request) { - this.repository = repository; - this.request = request; - } - - @Override - public boolean supports(Class clazz) { - return HasIdAndEmail.class.isAssignableFrom(clazz); - } - - @Override - public void validate(Object target, Errors errors) { - HasIdAndEmail user = ((HasIdAndEmail) target); - if (StringUtils.hasText(user.getEmail())) { - User dbUser = repository.getByEmail(user.getEmail().toLowerCase()); - if (dbUser != null) { - Assert.notNull(request, "HttpServletRequest missed"); - if (request.getMethod().equals("PUT") || (request.getMethod().equals("POST") && user.getId() != null)) { // update for REST(PUT) and UI(POST) - int dbId = dbUser.id(); - // it is ok, if update ourselves - if (user.getId() != null && dbId == user.id()) return; - - // workaround for update with user.id=null in request body - // ValidationUtil.assureIdConsistent (id setter) called after this validation - String requestURI = request.getRequestURI(); - if (requestURI.endsWith("/" + dbId) || (dbId == SecurityUtil.get().getId() && requestURI.contains("/profile"))) return; - } - errors.rejectValue("email", ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL); - } - } - } -} diff --git a/src/main/resources/cache/ehcache.xml b/src/main/resources/cache/ehcache.xml deleted file mode 100644 index 05589f71f..000000000 --- a/src/main/resources/cache/ehcache.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - 5 - - 5000 - - - - - - - 1 - - - - diff --git a/src/main/resources/db/hsqldb.properties b/src/main/resources/db/hsqldb.properties index 17c03ef4e..6d26d7909 100644 --- a/src/main/resources/db/hsqldb.properties +++ b/src/main/resources/db/hsqldb.properties @@ -1,8 +1,9 @@ -#database.url=jdbc:hsqldb:file:D:/temp/topjava +database.url=jdbc:hsqldb:file:C:/PostgerSQL/13/topjava +#database.url=jdbc:hsqldb:mem:topjava -database.url=jdbc:hsqldb:mem:topjava database.username=sa database.password= +database.driverClassName=org.hsqldb.jdbcDriver database.init=true jdbc.initLocation=classpath:db/initDB_hsql.sql diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql index 4bf3d8446..a6ad843ea 100644 --- a/src/main/resources/db/initDB.sql +++ b/src/main/resources/db/initDB.sql @@ -1,5 +1,5 @@ -DROP TABLE IF EXISTS user_role; -DROP TABLE IF EXISTS meal; +DROP TABLE IF EXISTS user_roles; +DROP TABLE IF EXISTS meals; DROP TABLE IF EXISTS users; DROP SEQUENCE IF EXISTS global_seq; @@ -15,9 +15,9 @@ CREATE TABLE users enabled BOOL DEFAULT TRUE NOT NULL, calories_per_day INTEGER DEFAULT 2000 NOT NULL ); -CREATE UNIQUE INDEX users_unique_email_idx ON users (email); +-- CREATE UNIQUE INDEX users_unique_email_idx ON users (email); -CREATE TABLE user_role +CREATE TABLE user_roles ( user_id INTEGER NOT NULL, role VARCHAR NOT NULL, @@ -25,7 +25,7 @@ CREATE TABLE user_role FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); -CREATE TABLE meal +CREATE TABLE meals ( id INTEGER PRIMARY KEY DEFAULT nextval('global_seq'), user_id INTEGER NOT NULL, @@ -34,4 +34,4 @@ CREATE TABLE meal calories INT NOT NULL, FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); -CREATE UNIQUE INDEX meal_unique_user_datetime_idx ON meal (user_id, date_time); \ No newline at end of file +CREATE UNIQUE INDEX meals_unique_user_datetime_idx ON meals (user_id, date_time); \ No newline at end of file diff --git a/src/main/resources/db/initDB_hsql.sql b/src/main/resources/db/initDB_hsql.sql index 9e0e195e6..37f2da1bf 100644 --- a/src/main/resources/db/initDB_hsql.sql +++ b/src/main/resources/db/initDB_hsql.sql @@ -1,5 +1,5 @@ -DROP TABLE user_role IF EXISTS; -DROP TABLE meal IF EXISTS; +DROP TABLE user_roles IF EXISTS; +DROP TABLE meals IF EXISTS; DROP TABLE users IF EXISTS; DROP SEQUENCE global_seq IF EXISTS; @@ -18,15 +18,15 @@ CREATE TABLE users CREATE UNIQUE INDEX users_unique_email_idx ON USERS (email); -CREATE TABLE user_role +CREATE TABLE user_roles ( user_id INTEGER NOT NULL, - role VARCHAR(255) NOT NULL, + role VARCHAR(255), CONSTRAINT user_roles_idx UNIQUE (user_id, role), FOREIGN KEY (user_id) REFERENCES USERS (id) ON DELETE CASCADE ); -CREATE TABLE meal +CREATE TABLE meals ( id INTEGER GENERATED BY DEFAULT AS SEQUENCE GLOBAL_SEQ PRIMARY KEY, date_time TIMESTAMP NOT NULL, @@ -35,5 +35,5 @@ CREATE TABLE meal user_id INTEGER NOT NULL, FOREIGN KEY (user_id) REFERENCES USERS (id) ON DELETE CASCADE ); -CREATE UNIQUE INDEX meal_unique_user_datetime_idx - ON meal (user_id, date_time) \ No newline at end of file +CREATE UNIQUE INDEX meals_unique_user_datetime_idx + ON meals (user_id, date_time) \ No newline at end of file diff --git a/src/main/resources/db/populateDB.sql b/src/main/resources/db/populateDB.sql index 8d66cc0e5..f29b325f2 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -1,19 +1,18 @@ -DELETE FROM user_role; -DELETE FROM meal; +DELETE FROM user_roles; +DELETE FROM meals; DELETE FROM users; ALTER SEQUENCE global_seq RESTART WITH 100000; -INSERT INTO users (name, email, password, calories_per_day) -VALUES ('User', 'user@yandex.ru', '{noop}password', 2005), - ('Admin', 'admin@gmail.com', '{noop}admin', 1900), - ('Guest', 'guest@gmail.com', '{noop}guest', 2000); +INSERT INTO users (name, email, password) +VALUES ('User', 'user@yandex.ru', 'password'), + ('Admin', 'admin@gmail.com', 'admin'), + ('Guest', 'guest@gmail.com', 'guest'); -INSERT INTO user_role (role, user_id) +INSERT INTO user_roles (role, user_id) VALUES ('USER', 100000), - ('ADMIN', 100001), - ('USER', 100001); + ('ADMIN', 100001); -INSERT INTO meal (date_time, description, calories, user_id) +INSERT INTO meals (date_time, description, calories, user_id) VALUES ('2020-01-30 10:00:00', 'Завтрак', 500, 100000), ('2020-01-30 13:00:00', 'Обед', 1000, 100000), ('2020-01-30 20:00:00', 'Ужин', 500, 100000), @@ -22,4 +21,4 @@ VALUES ('2020-01-30 10:00:00', 'Завтрак', 500, 100000), ('2020-01-31 13:00:00', 'Обед', 1000, 100000), ('2020-01-31 20:00:00', 'Ужин', 510, 100000), ('2020-01-31 14:00:00', 'Админ ланч', 510, 100001), - ('2020-01-31 21:00:00', 'Админ ужин', 1500, 100001); + ('2020-01-31 21:00:00', 'Админ ужин', 1500, 100001); \ No newline at end of file diff --git a/src/main/resources/db/postgres.properties b/src/main/resources/db/postgres.properties index c56854a9b..cbf1dc815 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -1,10 +1,14 @@ -database.url=jdbc:postgresql://localhost:5432/topjava -database.username=user -database.password=password +#database.url=jdbc:postgresql://ec2-34-248-169-69.eu-west-1.compute.amazonaws.com:5432/d1ohm99dookbqn?ssl=true&sslmode=require&sslfactory=org.postgresql.ssl.NonValidatingFactory +#database.username=qhazsiozndzrzc +#database.password=749f7852a65b5ec57bde033af8fde7f8b782a3ef802921acd4613b133d62559e + +database.url=jdbc:postgresql://localhost:5433/topjava +database.username=postgres +database.password= +database.driverClassName=org.postgresql.Driver database.init=true jdbc.initLocation=classpath:db/initDB.sql jpa.showSql=true hibernate.format_sql=true -#https://hibernate.atlassian.net/browse/HHH-13280 -hibernate.use_sql_comments=false \ No newline at end of file +hibernate.use_sql_comments=true diff --git a/src/main/resources/db/tomcat.properties b/src/main/resources/db/tomcat.properties deleted file mode 100644 index e11f0725f..000000000 --- a/src/main/resources/db/tomcat.properties +++ /dev/null @@ -1,5 +0,0 @@ -database.init=false -jdbc.initLocation=classpath:db/initDB.sql -jpa.showSql=true -hibernate.format_sql=true -hibernate.use_sql_comments=true \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index e2b565616..c7bffc3a9 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -16,18 +16,14 @@ UTF-8 - %d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%class{50}.%M:%L) - %msg%n + %d{HH:mm:ss.SSS} %-5level %class{50}.%M:%L - %msg%n - - - - - \ No newline at end of file + diff --git a/src/main/resources/spring/spring-app.xml b/src/main/resources/spring/spring-app.xml index 3a75ebb82..c32552d11 100644 --- a/src/main/resources/spring/spring-app.xml +++ b/src/main/resources/spring/spring-app.xml @@ -1,16 +1,10 @@ - - - + - \ No newline at end of file diff --git a/src/main/resources/spring/spring-cache.xml b/src/main/resources/spring/spring-cache.xml deleted file mode 100644 index 73325fee0..000000000 --- a/src/main/resources/spring/spring-cache.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/spring/spring-db.xml b/src/main/resources/spring/spring-db.xml index 52c5d0df3..966bf4931 100644 --- a/src/main/resources/spring/spring-db.xml +++ b/src/main/resources/spring/spring-db.xml @@ -4,79 +4,69 @@ xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" - xmlns:jpa="http://www.springframework.org/schema/data/jpa" - xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd - http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd - http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + --> \ No newline at end of file diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml deleted file mode 100644 index 1610c6270..000000000 --- a/src/main/resources/spring/spring-mvc.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - - text/plain;charset=UTF-8 - text/html;charset=UTF-8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/spring/spring-security.xml b/src/main/resources/spring/spring-security.xml deleted file mode 100644 index 8988177c4..000000000 --- a/src/main/resources/spring/spring-security.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/tomcat/context.xml b/src/main/resources/tomcat/context.xml deleted file mode 100644 index 9311d5904..000000000 --- a/src/main/resources/tomcat/context.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - WEB-INF/web.xml - ${catalina.base}/conf/web.xml - - - - - - - - - diff --git a/src/main/webapp/WEB-INF/jsp/exception.jsp b/src/main/webapp/WEB-INF/jsp/exception.jsp deleted file mode 100644 index 00a590c87..000000000 --- a/src/main/webapp/WEB-INF/jsp/exception.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%@ page isErrorPage="true" contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - - - - - - -
-
-
-

${status}

-

${typeMessage}

-

${message}

-
-
- - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp deleted file mode 100644 index b355dc254..000000000 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ /dev/null @@ -1,51 +0,0 @@ -<%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> - - - diff --git a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp deleted file mode 100644 index cf1331fd5..000000000 --- a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp +++ /dev/null @@ -1,8 +0,0 @@ -<%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%--https://getbootstrap.com/docs/4.0/examples/sticky-footer/--%> -
-
- -
-
\ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp deleted file mode 100644 index cc302e868..000000000 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ /dev/null @@ -1,29 +0,0 @@ -<%@page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> - - - - - - - - <spring:message code="app.title"/> - - - - - - - - - - - <%--http://stackoverflow.com/a/24070373/548473--%> - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp b/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp deleted file mode 100644 index c0e9db6b2..000000000 --- a/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp +++ /dev/null @@ -1,14 +0,0 @@ -<%@ page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/login.jsp b/src/main/webapp/WEB-INF/jsp/login.jsp deleted file mode 100644 index 1944ccca3..000000000 --- a/src/main/webapp/WEB-INF/jsp/login.jsp +++ /dev/null @@ -1,77 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> - - - - - - -
-
- -
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
-
- -
-
- -
- » - - -
-
- -
-
- - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/meals.jsp b/src/main/webapp/WEB-INF/jsp/meals.jsp deleted file mode 100644 index f66313bb3..000000000 --- a/src/main/webapp/WEB-INF/jsp/meals.jsp +++ /dev/null @@ -1,117 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="fn" uri="http://topjava.javawebinar.ru/functions" %> - - - - - - - -
-
-

- <%--https://getbootstrap.com/docs/4.0/components/card/--%> -
-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- -
-
- - - - - - - - - - - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/profile.jsp b/src/main/webapp/WEB-INF/jsp/profile.jsp deleted file mode 100644 index 9b6f6af17..000000000 --- a/src/main/webapp/WEB-INF/jsp/profile.jsp +++ /dev/null @@ -1,44 +0,0 @@ -<%@ page contentType="text/html" pageEncoding="UTF-8" %> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="topjava" tagdir="/WEB-INF/tags" %> - - - - - - - -
-
- <%--@elvariable id="userTo" type="ru.javawebinar.topjava.to.UserTo"--%> -
-
-

${userTo.name}

- - - - - - - - -
- - - - - -
-
-
-
-
-
- - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/users.jsp b/src/main/webapp/WEB-INF/jsp/users.jsp deleted file mode 100644 index 498bc4943..000000000 --- a/src/main/webapp/WEB-INF/jsp/users.jsp +++ /dev/null @@ -1,84 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> - - - - - - - - -
-
-

- - - - - - - - - - - - - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/tags/inputField.tag b/src/main/webapp/WEB-INF/tags/inputField.tag deleted file mode 100644 index bc481be2d..000000000 --- a/src/main/webapp/WEB-INF/tags/inputField.tag +++ /dev/null @@ -1,15 +0,0 @@ -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> - -<%@ attribute name="name" required="true" description="Name of corresponding property in bean object" %> -<%@ attribute name="labelCode" required="true" description="Field label" %> -<%@ attribute name="inputType" required="false" description="Input type" %> - - -
- - -
${status.errorMessage}
-
-
\ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 558c28967..bd98d3bf3 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -4,67 +4,26 @@ http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> - TopJava + Topjava - - spring.profiles.default - postgres,datajpa - - - - contextConfigLocation - - classpath:spring/spring-app.xml - classpath:spring/spring-db.xml - - - - - - org.springframework.web.context.ContextLoaderListener - - mvc-dispatcher - org.springframework.web.servlet.DispatcherServlet - - contextConfigLocation - classpath:spring/spring-mvc.xml - - - throwExceptionIfNoHandlerFound - true - - 1 + userServlet + ru.javawebinar.topjava.web.UserServlet + 0 - mvc-dispatcher - / + userServlet + /users - - encodingFilter - org.springframework.web.filter.CharacterEncodingFilter - - encoding - UTF-8 - - - forceEncoding - true - - - - encodingFilter - /* - + + mealServlet + ru.javawebinar.topjava.web.MealServlet + 0 + + + mealServlet + /meals + - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - springSecurityFilterChain - /* - diff --git a/src/main/webapp/css/style.css b/src/main/webapp/css/style.css new file mode 100644 index 000000000..cfffdcb53 --- /dev/null +++ b/src/main/webapp/css/style.css @@ -0,0 +1,24 @@ +dl { + background: none repeat scroll 0 0 #FAFAFA; + margin: 8px 0; + padding: 0; +} + +dt { + display: inline-block; + width: 170px; +} + +dd { + display: inline-block; + margin-left: 8px; + vertical-align: top; +} + +tr[data-meal-excess="false"] { + color: green; +} + +tr[data-meal-excess="true"] { + color: red; +} diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 000000000..57d710a81 --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,18 @@ + + + + Java Enterprise (Topjava) + + +

Проект Java Enterprise (Topjava)

+
+
+ Meals of  + + +
+ + diff --git a/src/main/webapp/mealForm.jsp b/src/main/webapp/mealForm.jsp new file mode 100644 index 000000000..98a6f4873 --- /dev/null +++ b/src/main/webapp/mealForm.jsp @@ -0,0 +1,34 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + Meal + + + +
+

Home

+
+

${param.action == 'create' ? 'Create meal' : 'Edit meal'}

+ +
+ +
+
DateTime:
+
+
+
+
Description:
+
+
+
+
Calories:
+
+
+ + +
+
+ + diff --git a/src/main/webapp/meals.jsp b/src/main/webapp/meals.jsp new file mode 100644 index 000000000..7d9bf3e42 --- /dev/null +++ b/src/main/webapp/meals.jsp @@ -0,0 +1,66 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="fn" uri="http://topjava.javawebinar.ru/functions" %> + + + Meals + + + +
+

Home

+
+

Meals

+
+ +
+
From Date (inclusive):
+
+
+
+
To Date (inclusive):
+
+
+
+
From Time (inclusive):
+
+
+
+
To Time (exclusive):
+
+
+ +
+
+ Add Meal +

+ + + + + + + + + + + + + + + + + + + + +
DateDescriptionCalories
+ <%--${meal.dateTime.toLocalDate()} ${meal.dateTime.toLocalTime()}--%> + <%--<%=TimeUtil.toString(meal.getDateTime())%>--%> + <%--${fn:replace(meal.dateTime, 'T', ' ')}--%> + ${fn:formatDateTime(meal.dateTime)} + ${meal.description}${meal.calories}UpdateDelete
+
+ + \ No newline at end of file diff --git a/src/main/webapp/resources/css/style.css b/src/main/webapp/resources/css/style.css deleted file mode 100644 index 4d989107e..000000000 --- a/src/main/webapp/resources/css/style.css +++ /dev/null @@ -1,55 +0,0 @@ -tr[data-meal-excess="false"] { - color: green; -} - -tr[data-meal-excess="true"] { - color: red; -} - -.fa { - cursor: pointer; -} - -tr[data-user-enabled="false"] { - opacity: 0.3; -} - -.error, .message { - padding: 10px; - border-radius: 4px; - font-size: 16px; -} - -.error { - color: #a94442; - background-color: #f2dede; - border: 1px solid #ebccd1; -} - -.message { - color: #2f9635; - background-color: #c6fbc2; - border: 1px solid #9feba6; -} - -/*https://stackoverflow.com/a/53855189/548473*/ -#noty_layout__bottomRight { - width: 385px !important; -} - -/*https://getbootstrap.com/docs/4.0/examples/sticky-footer/sticky-footer.css*/ -html { - position: relative; - min-height: 100%; -} -body { - margin-bottom: 60px !important; /* Margin bottom by footer height */ -} -.footer { - position: absolute; - bottom: 0; - width: 100%; - height: 60px; /* Set the fixed height of the footer here */ - line-height: 60px; /* Vertically center the text there */ - background-color: #f5f5f5; -} diff --git a/src/main/webapp/resources/images/icon-meal.png b/src/main/webapp/resources/images/icon-meal.png deleted file mode 100644 index bb9dc2d73..000000000 Binary files a/src/main/webapp/resources/images/icon-meal.png and /dev/null differ diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js deleted file mode 100644 index 9fe11e041..000000000 --- a/src/main/webapp/resources/js/topjava.common.js +++ /dev/null @@ -1,119 +0,0 @@ -let form; - -function makeEditable(datatableOpts) { - ctx.datatableApi = $("#datatable").DataTable( - { - ...datatableOpts, // https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Spread_syntax - "ajax": { - "url": ctx.ajaxUrl, - "dataSrc": "" - }, - "paging": false, - "info": true, - "language": { - "search": i18n["common.search"] - } - } - ); - form = $('#detailsForm'); - - $(document).ajaxError(function (event, jqXHR, options, jsExc) { - failNoty(jqXHR); - }); - - // solve problem with cache in IE: https://stackoverflow.com/a/4303862/548473 - $.ajaxSetup({cache: false}); - - var token = $("meta[name='_csrf']").attr("content"); - var header = $("meta[name='_csrf_header']").attr("content"); - $(document).ajaxSend(function (e, xhr, options) { - xhr.setRequestHeader(header, token); - }); -} - -function add() { - $("#modalTitle").html(i18n["addTitle"]); - form.find(":input").val(""); - $("#editRow").modal(); -} - -function updateRow(id) { - form.find(":input").val(""); - $("#modalTitle").html(i18n["editTitle"]); - $.get(ctx.ajaxUrl + id, function (data) { - $.each(data, function (key, value) { - form.find("input[name='" + key + "']").val(value); - }); - $('#editRow').modal(); - }); -} - -function deleteRow(id) { - if (confirm(i18n['common.confirm'])) { - $.ajax({ - url: ctx.ajaxUrl + id, - type: "DELETE" - }).done(function () { - ctx.updateTable(); - successNoty("common.deleted"); - }); - } -} - -function updateTableByData(data) { - ctx.datatableApi.clear().rows.add(data).draw(); -} - -function save() { - $.ajax({ - type: "POST", - url: ctx.ajaxUrl, - data: form.serialize() - }).done(function () { - $("#editRow").modal("hide"); - ctx.updateTable(); - successNoty("common.saved"); - }); -} - -let failedNote; - -function closeNoty() { - if (failedNote) { - failedNote.close(); - failedNote = undefined; - } -} - -function successNoty(key) { - closeNoty(); - new Noty({ - text: `  ${i18n[key]}`, - type: 'success', - layout: "bottomRight", - timeout: 1000 - }).show(); -} - -function renderEditBtn(data, type, row) { - if (type === "display") { - return ``; - } -} - -function renderDeleteBtn(data, type, row) { - if (type === "display") { - return ``; - } -} - -function failNoty(jqXHR) { - closeNoty(); - var errorInfo = jqXHR.responseJSON; - failedNote = new Noty({ - text: `  ${errorInfo.typeMessage}
${errorInfo.details.join("
")}`, - type: "error", - layout: "bottomRight" - }); - failedNote.show() -} \ No newline at end of file diff --git a/src/main/webapp/resources/js/topjava.meals.js b/src/main/webapp/resources/js/topjava.meals.js deleted file mode 100644 index 16d991e9c..000000000 --- a/src/main/webapp/resources/js/topjava.meals.js +++ /dev/null @@ -1,118 +0,0 @@ -const mealAjaxUrl = "profile/meals/"; - -// https://stackoverflow.com/a/5064235/548473 -const ctx = { - ajaxUrl: mealAjaxUrl, - updateTable: function () { - $.ajax({ - type: "GET", - url: mealAjaxUrl + "filter", - data: $("#filter").serialize() - }).done(updateTableByData); - } -}; - -function clearFilter() { - $("#filter")[0].reset(); - $.get(mealAjaxUrl, updateTableByData); -} - -// http://api.jquery.com/jQuery.ajax/#using-converters -$.ajaxSetup({ - converters: { - "text json": function (stringData) { - return JSON.parse(stringData, - function (key, value) { - return (key === 'dateTime') ? value.substring(0, 16).replace('T', ' ') : value; - } - ); - } - } -}); - -$(function () { - makeEditable({ - "columns": [ - { - "data": "dateTime" - }, - { - "data": "description" - }, - { - "data": "calories" - }, - { - "render": renderEditBtn, - "defaultContent": "", - "orderable": false - }, - { - "render": renderDeleteBtn, - "defaultContent": "", - "orderable": false - } - ], - "order": [ - [ - 0, - "desc" - ] - ], - "createdRow": function (row, data, dataIndex) { - $(row).attr("data-meal-excess", data.excess); - } - }); - - $.datetimepicker.setLocale(localeCode); - -// http://xdsoft.net/jqplugins/datetimepicker/ - var startDate = $('#startDate'); - var endDate = $('#endDate'); - const dateOptions = { - timepicker: false, - format: 'Y-m-d', - formatDate: 'Y-m-d', - }; - startDate.datetimepicker({ - ...dateOptions, - onShow: function (ct) { - this.setOptions({ - maxDate: endDate.val() ? endDate.val() : false - }) - } - }); - endDate.datetimepicker({ - ...dateOptions, - onShow: function (ct) { - this.setOptions({ - minDate: startDate.val() ? startDate.val() : false - }) - } - }); - - var startTime = $('#startTime'); - var endTime = $('#endTime'); - startTime.datetimepicker({ - datepicker: false, - format: 'H:i', - onShow: function (ct) { - this.setOptions({ - maxTime: endTime.val() ? endTime.val() : false - }) - } - }); - endTime.datetimepicker({ - datepicker: false, - format: 'H:i', - onShow: function (ct) { - this.setOptions({ - minTime: startTime.val() ? startTime.val() : false - }) - } - }); - - $('#dateTime').datetimepicker({ - format: 'Y-m-d H:i' - }); -}); \ No newline at end of file diff --git a/src/main/webapp/resources/js/topjava.users.js b/src/main/webapp/resources/js/topjava.users.js deleted file mode 100644 index fb4db9efd..000000000 --- a/src/main/webapp/resources/js/topjava.users.js +++ /dev/null @@ -1,86 +0,0 @@ -const userAjaxUrl = "admin/users/"; - -// https://stackoverflow.com/a/5064235/548473 -const ctx = { - ajaxUrl: userAjaxUrl, - updateTable: function () { - $.get(userAjaxUrl, updateTableByData); - } -} - -function enable(chkbox, id) { - const enabled = chkbox.is(":checked"); -// https://stackoverflow.com/a/22213543/548473 - $.ajax({ - url: userAjaxUrl + id, - type: "POST", - data: `enabled=${enabled}` - }).done(function () { - chkbox.closest("tr").attr("data-user-enabled", enabled); - successNoty(enabled ? "common.enabled" : "common.disabled"); - }).fail(function () { - $(chkbox).prop("checked", !enabled); - }); -} - -// $(document).ready(function () { -$(function () { - makeEditable({ - "columns": [ - { - "data": "name" - }, - { - "data": "email", - "render": function (data, type, row) { - if (type === "display") { - return `${data}`; - } - return data; - } - }, - { - "data": "roles" - }, - { - "data": "enabled", - "render": function (data, type, row) { - if (type === "display") { - return ``; - } - return data; - } - }, - { - "data": "registered", - "render": function (date, type, row) { - if (type === "display") { - return date.substring(0, 10); - } - return date; - } - }, - { - "orderable": false, - "defaultContent": "", - "render": renderEditBtn - }, - { - "orderable": false, - "defaultContent": "", - "render": renderDeleteBtn - } - ], - "order": [ - [ - 0, - "asc" - ] - ], - "createdRow": function (row, data, dataIndex) { - if (!data.enabled) { - $(row).attr("data-user-enabled", false); - } - } - }); -}); \ No newline at end of file diff --git a/src/main/webapp/users.jsp b/src/main/webapp/users.jsp new file mode 100644 index 000000000..650c8dda4 --- /dev/null +++ b/src/main/webapp/users.jsp @@ -0,0 +1,11 @@ +<%@ page contentType="text/html;charset=UTF-8" %> + + + Users + + +

Home

+
+

Users

+ + \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java deleted file mode 100644 index 43f143cc7..000000000 --- a/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.javawebinar.topjava; - -import org.springframework.lang.NonNull; -import org.springframework.test.context.support.DefaultActiveProfilesResolver; - -import java.util.Arrays; - -//http://stackoverflow.com/questions/23871255/spring-profiles-simple-example-of-activeprofilesresolver -public class ActiveDbProfileResolver extends DefaultActiveProfilesResolver { - @Override - public @NonNull - String[] resolve(@NonNull Class aClass) { - // https://stackoverflow.com/a/52438829/548473 - String[] activeProfiles = super.resolve(aClass); - String[] activeProfilesWithDb = Arrays.copyOf(activeProfiles, activeProfiles.length + 1); - activeProfilesWithDb[activeProfiles.length] = Profiles.getActiveDbProfile(); - return activeProfilesWithDb; - } -} diff --git a/src/test/java/ru/javawebinar/topjava/MatcherFactory.java b/src/test/java/ru/javawebinar/topjava/MatcherFactory.java index 15e01e155..200e27e5f 100644 --- a/src/test/java/ru/javawebinar/topjava/MatcherFactory.java +++ b/src/test/java/ru/javawebinar/topjava/MatcherFactory.java @@ -1,13 +1,6 @@ package ru.javawebinar.topjava; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.ResultMatcher; -import ru.javawebinar.topjava.web.json.JsonUtil; - -import java.io.UnsupportedEncodingException; -import java.util.List; -import java.util.function.BiConsumer; +import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; @@ -15,69 +8,30 @@ * Factory for creating test matchers. *

* Comparing actual and expected objects via AssertJ - * Support converting json MvcResult to objects for comparation. */ public class MatcherFactory { - - public static Matcher usingAssertions(Class clazz, BiConsumer assertion, BiConsumer, Iterable> iterableAssertion) { - return new Matcher<>(clazz, assertion, iterableAssertion); - } - - public static Matcher usingEqualsComparator(Class clazz) { - return usingAssertions(clazz, - (a, e) -> assertThat(a).isEqualTo(e), - (a, e) -> assertThat(a).isEqualTo(e)); - } - - public static Matcher usingIgnoringFieldsComparator(Class clazz, String... fieldsToIgnore) { - return usingAssertions(clazz, - (a, e) -> assertThat(a).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(e), - (a, e) -> assertThat(a).usingRecursiveFieldByFieldElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(e)); + public static Matcher usingIgnoringFieldsComparator(String... fieldsToIgnore) { + return new Matcher<>(fieldsToIgnore); } public static class Matcher { - private final Class clazz; - private final BiConsumer assertion; - private final BiConsumer, Iterable> iterableAssertion; + private final String[] fieldsToIgnore; - private Matcher(Class clazz, BiConsumer assertion, BiConsumer, Iterable> iterableAssertion) { - this.clazz = clazz; - this.assertion = assertion; - this.iterableAssertion = iterableAssertion; + private Matcher(String... fieldsToIgnore) { + this.fieldsToIgnore = fieldsToIgnore; } public void assertMatch(T actual, T expected) { - assertion.accept(actual, expected); + assertThat(actual).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(expected); } @SafeVarargs public final void assertMatch(Iterable actual, T... expected) { - assertMatch(actual, List.of(expected)); + assertMatch(actual, Arrays.asList(expected)); } public void assertMatch(Iterable actual, Iterable expected) { - iterableAssertion.accept(actual, expected); - } - - public ResultMatcher contentJson(T expected) { - return result -> assertMatch(JsonUtil.readValue(getContent(result), clazz), expected); - } - - @SafeVarargs - public final ResultMatcher contentJson(T... expected) { - return contentJson(List.of(expected)); - } - - public ResultMatcher contentJson(Iterable expected) { - return result -> assertMatch(JsonUtil.readValues(getContent(result), clazz), expected); - } - - public T readFromJson(ResultActions action) throws UnsupportedEncodingException { - return JsonUtil.readValue(getContent(action.andReturn()), clazz); - } - - private static String getContent(MvcResult result) throws UnsupportedEncodingException { - return result.getResponse().getContentAsString(); + assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(expected); } } } diff --git a/src/test/java/ru/javawebinar/topjava/MealTestData.java b/src/test/java/ru/javawebinar/topjava/MealTestData.java index 6ee8b66dd..5190f5082 100644 --- a/src/test/java/ru/javawebinar/topjava/MealTestData.java +++ b/src/test/java/ru/javawebinar/topjava/MealTestData.java @@ -1,18 +1,17 @@ package ru.javawebinar.topjava; import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.to.MealTo; import java.time.Month; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.List; import static java.time.LocalDateTime.of; import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class MealTestData { - public static final MatcherFactory.Matcher MEAL_MATCHER = MatcherFactory.usingIgnoringFieldsComparator(Meal.class, "user"); - public static MatcherFactory.Matcher TO_MATCHER = MatcherFactory.usingEqualsComparator(MealTo.class); + public static final MatcherFactory.Matcher MEAL_MATCHER = MatcherFactory.usingIgnoringFieldsComparator(); public static final int NOT_FOUND = 10; public static final int MEAL1_ID = START_SEQ + 3; @@ -28,7 +27,7 @@ public class MealTestData { public static final Meal adminMeal1 = new Meal(ADMIN_MEAL_ID, of(2020, Month.JANUARY, 31, 14, 0), "Админ ланч", 510); public static final Meal adminMeal2 = new Meal(ADMIN_MEAL_ID + 1, of(2020, Month.JANUARY, 31, 21, 0), "Админ ужин", 1500); - public static final List meals = List.of(meal7, meal6, meal5, meal4, meal3, meal2, meal1); + public static final List meals = Arrays.asList(meal7, meal6, meal5, meal4, meal3, meal2, meal1); public static Meal getNew() { return new Meal(null, of(2020, Month.FEBRUARY, 1, 18, 0), "Созданный ужин", 300); diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index a9e46b208..de77f507c 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -14,19 +14,17 @@ import java.util.Arrays; import java.util.List; -import static ru.javawebinar.topjava.TestUtil.mockAuthorize; -import static ru.javawebinar.topjava.UserTestData.user; - public class SpringMain { public static void main(String[] args) { // java 7 automatic resource management (ARM) - try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/inmemory.xml")) { + try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/inmemory.xml")) { System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); +// System.out.println("Bean definition names 1: "); +// Arrays.stream(appCtx.getBeanDefinitionNames()).sorted().forEach(System.out::println); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); - adminUserController.create(new User(null, "userName", "email@mail.ru", "password", 2000, Role.ADMIN)); - System.out.println(); - - mockAuthorize(user); + // adminUserController.create(new User(null, "userName", "email@mail.ru", "password", Role.USER)); + adminUserController.getAll().forEach(System.out::println); + System.out.println("ln 25 " ); MealRestController mealController = appCtx.getBean(MealRestController.class); List filteredMealsWithExcess = diff --git a/src/test/java/ru/javawebinar/topjava/TestUtil.java b/src/test/java/ru/javawebinar/topjava/TestUtil.java deleted file mode 100644 index 3688cd60e..000000000 --- a/src/test/java/ru/javawebinar/topjava/TestUtil.java +++ /dev/null @@ -1,23 +0,0 @@ -package ru.javawebinar.topjava; - -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; -import org.springframework.test.web.servlet.request.RequestPostProcessor; -import ru.javawebinar.topjava.model.User; - -public class TestUtil { - - public static void mockAuthorize(User user) { - SecurityContextHolder.getContext().setAuthentication( - new UsernamePasswordAuthenticationToken(new AuthorizedUser(user), null, user.getRoles())); - } - - public static RequestPostProcessor userHttpBasic(User user) { - return SecurityMockMvcRequestPostProcessors.httpBasic(user.getEmail(), user.getPassword()); - } - - public static RequestPostProcessor userAuth(User user) { - return SecurityMockMvcRequestPostProcessors.authentication(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())); - } -} diff --git a/src/test/java/ru/javawebinar/topjava/TimingExtension.java b/src/test/java/ru/javawebinar/topjava/TimingExtension.java deleted file mode 100644 index cee6ae92c..000000000 --- a/src/test/java/ru/javawebinar/topjava/TimingExtension.java +++ /dev/null @@ -1,36 +0,0 @@ -package ru.javawebinar.topjava; - -import org.junit.jupiter.api.extension.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StopWatch; - -public class TimingExtension implements - BeforeTestExecutionCallback, AfterTestExecutionCallback, BeforeAllCallback, AfterAllCallback { - - private static final Logger log = LoggerFactory.getLogger("result"); - - private StopWatch stopWatch; - - @Override - public void beforeAll(ExtensionContext extensionContext) { - stopWatch = new StopWatch("Execution time of " + extensionContext.getRequiredTestClass().getSimpleName()); - } - - @Override - public void beforeTestExecution(ExtensionContext extensionContext) { - String testName = extensionContext.getDisplayName(); - log.info("\nStart " + testName); - stopWatch.start(testName); - } - - @Override - public void afterTestExecution(ExtensionContext extensionContext) { - stopWatch.stop(); - } - - @Override - public void afterAll(ExtensionContext extensionContext) { - log.info('\n' + stopWatch.prettyPrint() + '\n'); - } -} diff --git a/src/test/java/ru/javawebinar/topjava/UserTestData.java b/src/test/java/ru/javawebinar/topjava/UserTestData.java index d2b8aad6e..bfaba979d 100644 --- a/src/test/java/ru/javawebinar/topjava/UserTestData.java +++ b/src/test/java/ru/javawebinar/topjava/UserTestData.java @@ -2,39 +2,23 @@ import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.web.json.JsonUtil; import java.util.Collections; import java.util.Date; -import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; -import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.model.AbstractBaseEntity.START_SEQ; public class UserTestData { - public static final MatcherFactory.Matcher USER_MATCHER = MatcherFactory.usingIgnoringFieldsComparator(User.class, "registered", "meals", "password"); - public static MatcherFactory.Matcher USER_WITH_MEALS_MATCHER = - MatcherFactory.usingAssertions(User.class, -// No need use ignoringAllOverriddenEquals, see https://assertj.github.io/doc/#breaking-changes - (a, e) -> assertThat(a).usingRecursiveComparison().ignoringFields("registered", "meals.user", "password").isEqualTo(e), - (a, e) -> { - throw new UnsupportedOperationException(); - }); + public static final MatcherFactory.Matcher USER_MATCHER = MatcherFactory.usingIgnoringFieldsComparator("registered", "roles"); public static final int USER_ID = START_SEQ; public static final int ADMIN_ID = START_SEQ + 1; public static final int GUEST_ID = START_SEQ + 2; public static final int NOT_FOUND = 10; - public static final User user = new User(USER_ID, "User", "user@yandex.ru", "password", 2005, Role.USER); - public static final User admin = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", 1900, Role.ADMIN, Role.USER); - public static final User guest = new User(GUEST_ID, "Guest", "guest@gmail.com", "guest", 2000); - - static { - user.setMeals(meals); - admin.setMeals(List.of(adminMeal2, adminMeal1)); - } + public static final User user = new User(USER_ID, "User", "user@yandex.ru", "password", Role.USER); + public static final User admin = new User(ADMIN_ID, "Admin", "admin@gmail.com", "admin", Role.ADMIN); + public static final User guest = new User(GUEST_ID, "Guest", "guest@gmail.com", "guest"); public static User getNew() { return new User(null, "New", "new@gmail.com", "newPass", 1555, false, new Date(), Collections.singleton(Role.USER)); @@ -42,10 +26,7 @@ public static User getNew() { public static User getUpdated() { User updated = new User(user); - -// In case of update with user.id=null in body needs workaround -// ValidationUtil.assureIdConsistent called after validation -// updated.setEmail("update@gmail.com"); + updated.setEmail("update@gmail.com"); updated.setName("UpdatedName"); updated.setCaloriesPerDay(330); updated.setPassword("newPass"); @@ -53,8 +34,4 @@ public static User getUpdated() { updated.setRoles(Collections.singletonList(Role.ADMIN)); return updated; } - - public static String jsonWithPassword(User user, String passw) { - return JsonUtil.writeAdditionProps(user, "password", passw); - } } diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java index 5c65ced86..05527fca8 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryMealRepository.java @@ -15,6 +15,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; +import java.util.stream.Collectors; @Repository public class InMemoryMealRepository implements MealRepository { @@ -24,7 +25,7 @@ public class InMemoryMealRepository implements MealRepository { private final Map> usersMealsMap = new ConcurrentHashMap<>(); { - var userMeals = new InMemoryBaseRepository(); + InMemoryBaseRepository userMeals = new InMemoryBaseRepository<>(); MealTestData.meals.forEach(userMeals::put); usersMealsMap.put(UserTestData.USER_ID, userMeals); } @@ -33,7 +34,7 @@ public class InMemoryMealRepository implements MealRepository { @Override public Meal save(Meal meal, int userId) { Objects.requireNonNull(meal, "meal must not be null"); - var meals = usersMealsMap.computeIfAbsent(userId, uId -> new InMemoryBaseRepository<>()); + InMemoryBaseRepository meals = usersMealsMap.computeIfAbsent(userId, uId -> new InMemoryBaseRepository<>()); return meals.save(meal); } @@ -49,13 +50,13 @@ public void preDestroy() { @Override public boolean delete(int id, int userId) { - var meals = usersMealsMap.get(userId); + InMemoryBaseRepository meals = usersMealsMap.get(userId); return meals != null && meals.delete(id); } @Override public Meal get(int id, int userId) { - var meals = usersMealsMap.get(userId); + InMemoryBaseRepository meals = usersMealsMap.get(userId); return meals == null ? null : meals.get(id); } @@ -70,11 +71,11 @@ public List getAll(int userId) { } private List filterByPredicate(int userId, Predicate filter) { - var meals = usersMealsMap.get(userId); + InMemoryBaseRepository meals = usersMealsMap.get(userId); return meals == null ? Collections.emptyList() : meals.getCollection().stream() .filter(filter) .sorted(Comparator.comparing(Meal::getDateTime).reversed()) - .toList(); + .collect(Collectors.toList()); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java index f3585dfff..7e2fadbf8 100644 --- a/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java +++ b/src/test/java/ru/javawebinar/topjava/repository/inmemory/InMemoryUserRepository.java @@ -7,6 +7,7 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import static ru.javawebinar.topjava.UserTestData.*; @@ -26,7 +27,7 @@ public void init() { public List getAll() { return getCollection().stream() .sorted(Comparator.comparing(User::getName).thenComparing(User::getEmail)) - .toList(); + .collect(Collectors.toList()); } @Override diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java deleted file mode 100644 index b8713e6b9..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.javawebinar.topjava.service; - -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import ru.javawebinar.topjava.ActiveDbProfileResolver; -import ru.javawebinar.topjava.TimingExtension; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; - -@SpringJUnitConfig(locations = { - "classpath:spring/spring-app.xml", - "classpath:spring/spring-db.xml" -}) -//@ExtendWith(SpringExtension.class) -@ActiveProfiles(resolver = ActiveDbProfileResolver.class) -@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8"), executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) -@ExtendWith(TimingExtension.class) -public abstract class AbstractServiceTest { - - // Check root cause with AssertJ: https://github.com/junit-team/junit-framework/issues/2129#issuecomment-565712630 - // Check root cause in JUnit: https://github.com/junit-team/junit4/pull/778 - protected void validateRootCause(Class rootExceptionClass, Runnable runnable) { - assertThatExceptionOfType(Throwable.class) - .isThrownBy(runnable::run) - .satisfiesAnyOf( - ex -> assertThat(ex).isInstanceOf(rootExceptionClass), - ex -> assertThat(ex).hasRootCauseInstanceOf(rootExceptionClass)); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java deleted file mode 100644 index 100d5637e..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package ru.javawebinar.topjava.service; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataAccessException; -import ru.javawebinar.topjava.model.Role; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.util.exception.NotFoundException; - -import javax.validation.ConstraintViolationException; -import java.util.Date; -import java.util.List; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.*; -import static ru.javawebinar.topjava.UserTestData.*; - -public abstract class AbstractUserServiceTest extends AbstractServiceTest { - - @Autowired - protected UserService service; - - @Test - void create() { - User created = service.create(getNew()); - int newId = created.id(); - User newUser = getNew(); - newUser.setId(newId); - USER_MATCHER.assertMatch(created, newUser); - USER_MATCHER.assertMatch(service.get(newId), newUser); - } - - @Test - void duplicateMailCreate() { - assertThrows(DataAccessException.class, () -> - service.create(new User(null, "Duplicate", "user@yandex.ru", "newPass", 2000, Role.USER))); - } - - @Test - void delete() { - service.delete(USER_ID); - assertThrows(NotFoundException.class, () -> service.get(USER_ID)); - } - - @Test - void deletedNotFound() { - assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND)); - } - - @Test - void get() { - User user = service.get(ADMIN_ID); - USER_MATCHER.assertMatch(user, admin); - } - - @Test - void getNotFound() { - assertThrows(NotFoundException.class, () -> service.get(NOT_FOUND)); - } - - @Test - void getByEmail() { - User user = service.getByEmail("admin@gmail.com"); - USER_MATCHER.assertMatch(user, admin); - } - - @Test - void update() { - User updated = getUpdated(); - service.update(updated); - USER_MATCHER.assertMatch(service.get(USER_ID), getUpdated()); - } - - @Test - void getAll() { - List all = service.getAll(); - USER_MATCHER.assertMatch(all, admin, guest, user); - } - - @Test - void createWithException() throws Exception { - validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, " ", "mail@yandex.ru", "password", 2000, Role.USER))); - validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", " ", "password", 2000, Role.USER))); - validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", "password", 9, true, new Date(), Set.of()))); - validateRootCause(ConstraintViolationException.class, () -> service.create(new User(null, "User", "mail@yandex.ru", "password", 10001, true, new Date(), Set.of()))); - } - - @Test - void enable() { - service.enable(USER_ID, false); - assertFalse(service.get(USER_ID).isEnabled()); - service.enable(USER_ID, true); - assertTrue(service.get(USER_ID).isEnabled()); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java similarity index 60% rename from src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java rename to src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java index 6b00e4481..12a4ba266 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractMealServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/MealServiceTest.java @@ -1,45 +1,53 @@ package ru.javawebinar.topjava.service; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.SpringRunner; import ru.javawebinar.topjava.model.Meal; import ru.javawebinar.topjava.util.exception.NotFoundException; -import javax.validation.ConstraintViolationException; import java.time.LocalDate; import java.time.Month; -import static java.time.LocalDateTime.of; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.Assert.assertThrows; import static ru.javawebinar.topjava.MealTestData.*; import static ru.javawebinar.topjava.UserTestData.ADMIN_ID; import static ru.javawebinar.topjava.UserTestData.USER_ID; -public abstract class AbstractMealServiceTest extends AbstractServiceTest { +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringRunner.class) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +public class MealServiceTest { @Autowired - protected MealService service; + private MealService service; @Test - void delete() { + public void delete() { service.delete(MEAL1_ID, USER_ID); assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, USER_ID)); } @Test - void deleteNotFound() { + public void deleteNotFound() { assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND, USER_ID)); } @Test - void deleteNotOwn() { + public void deleteNotOwn() { assertThrows(NotFoundException.class, () -> service.delete(MEAL1_ID, ADMIN_ID)); } @Test - void create() { + public void create() { Meal created = service.create(getNew(), USER_ID); int newId = created.id(); Meal newMeal = getNew(); @@ -49,48 +57,47 @@ void create() { } @Test - void duplicateDateTimeCreate() { + public void duplicateDateTimeCreate() { assertThrows(DataAccessException.class, () -> service.create(new Meal(null, meal1.getDateTime(), "duplicate", 100), USER_ID)); } @Test - void get() { + public void get() { Meal actual = service.get(ADMIN_MEAL_ID, ADMIN_ID); MEAL_MATCHER.assertMatch(actual, adminMeal1); } @Test - void getNotFound() { + public void getNotFound() { assertThrows(NotFoundException.class, () -> service.get(NOT_FOUND, USER_ID)); } @Test - void getNotOwn() { + public void getNotOwn() { assertThrows(NotFoundException.class, () -> service.get(MEAL1_ID, ADMIN_ID)); } @Test - void update() { + public void update() { Meal updated = getUpdated(); service.update(updated, USER_ID); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), getUpdated()); } @Test - void updateNotOwn() { - NotFoundException exception = assertThrows(NotFoundException.class, () -> service.update(getUpdated(), ADMIN_ID)); - Assertions.assertEquals("Not found entity with id=" + MEAL1_ID, exception.getMessage()); + public void updateNotOwn() { + assertThrows(NotFoundException.class, () -> service.update(meal1, ADMIN_ID)); MEAL_MATCHER.assertMatch(service.get(MEAL1_ID, USER_ID), meal1); } @Test - void getAll() { + public void getAll() { MEAL_MATCHER.assertMatch(service.getAll(USER_ID), meals); } @Test - void getBetweenInclusive() { + public void getBetweenInclusive() { MEAL_MATCHER.assertMatch(service.getBetweenInclusive( LocalDate.of(2020, Month.JANUARY, 30), LocalDate.of(2020, Month.JANUARY, 30), USER_ID), @@ -98,15 +105,7 @@ void getBetweenInclusive() { } @Test - void getBetweenWithNullDates() { + public void getBetweenWithNullDates() { MEAL_MATCHER.assertMatch(service.getBetweenInclusive(null, null, USER_ID), meals); } - - @Test - void createWithException() throws Exception { - validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), " ", 300), USER_ID)); - validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, null, "Description", 300), USER_ID)); - validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 9), USER_ID)); - validateRootCause(ConstraintViolationException.class, () -> service.create(new Meal(null, of(2015, Month.JUNE, 1, 18, 0), "Description", 5001), USER_ID)); - } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java new file mode 100644 index 000000000..b318695cc --- /dev/null +++ b/src/test/java/ru/javawebinar/topjava/service/UserServiceTest.java @@ -0,0 +1,88 @@ +package ru.javawebinar.topjava.service; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.SpringRunner; +import ru.javawebinar.topjava.UserTestData; +import ru.javawebinar.topjava.model.Role; +import ru.javawebinar.topjava.model.User; +import ru.javawebinar.topjava.util.exception.NotFoundException; + +import java.util.List; + +import static org.junit.Assert.assertThrows; +import static ru.javawebinar.topjava.UserTestData.*; + +@ContextConfiguration({ + "classpath:spring/spring-app.xml", + "classpath:spring/spring-db.xml" +}) +@RunWith(SpringRunner.class) +@Sql(scripts = "classpath:db/populateDB.sql", config = @SqlConfig(encoding = "UTF-8")) +public class UserServiceTest { + + @Autowired + private UserService service; + + @Test + public void create() { + User created = service.create(getNew()); + int newId = created.id(); + User newUser = getNew(); + newUser.setId(newId); + USER_MATCHER.assertMatch(created, newUser); + USER_MATCHER.assertMatch(service.get(newId), newUser); + } + + @Test + public void duplicateMailCreate() { + assertThrows(DataAccessException.class, () -> + service.create(new User(null, "Duplicate", "user@yandex.ru", "newPass", Role.USER))); + } + + @Test + public void delete() { + service.delete(USER_ID); + assertThrows(NotFoundException.class, () -> service.get(USER_ID)); + } + + @Test + public void deletedNotFound() { + assertThrows(NotFoundException.class, () -> service.delete(NOT_FOUND)); + } + + @Test + public void get() { + User user = service.get(USER_ID); + USER_MATCHER.assertMatch(user, UserTestData.user); + } + + @Test + public void getNotFound() { + assertThrows(NotFoundException.class, () -> service.get(NOT_FOUND)); + } + + @Test + public void getByEmail() { + User user = service.getByEmail("admin@gmail.com"); + USER_MATCHER.assertMatch(user, admin); + } + + @Test + public void update() { + User updated = getUpdated(); + service.update(updated); + USER_MATCHER.assertMatch(service.get(USER_ID), getUpdated()); + } + + @Test + public void getAll() { + List all = service.getAll(); + USER_MATCHER.assertMatch(all, admin, guest, user); + } +} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java deleted file mode 100644 index 161c93fb5..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaMealServiceTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.javawebinar.topjava.service.datajpa; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.MealTestData; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.service.AbstractMealServiceTest; -import ru.javawebinar.topjava.util.exception.NotFoundException; - -import static ru.javawebinar.topjava.MealTestData.*; -import static ru.javawebinar.topjava.Profiles.DATAJPA; -import static ru.javawebinar.topjava.UserTestData.*; - -@ActiveProfiles(DATAJPA) -class DataJpaMealServiceTest extends AbstractMealServiceTest { - @Test - void getWithUser() { - Meal adminMeal = service.getWithUser(ADMIN_MEAL_ID, ADMIN_ID); - MEAL_MATCHER.assertMatch(adminMeal, adminMeal1); - USER_MATCHER.assertMatch(adminMeal.getUser(), admin); - } - - @Test - void getWithUserNotFound() { - Assertions.assertThrows(NotFoundException.class, - () -> service.getWithUser(MealTestData.NOT_FOUND, ADMIN_ID)); - } -} diff --git a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java deleted file mode 100644 index 3638e07e9..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/datajpa/DataJpaUserServiceTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.javawebinar.topjava.service.datajpa; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.service.AbstractUserServiceTest; -import ru.javawebinar.topjava.util.exception.NotFoundException; - -import static ru.javawebinar.topjava.Profiles.DATAJPA; -import static ru.javawebinar.topjava.UserTestData.*; - -@ActiveProfiles(DATAJPA) -class DataJpaUserServiceTest extends AbstractUserServiceTest { - @Test - void getWithMeals() { - User actual = service.getWithMeals(ADMIN_ID); - USER_WITH_MEALS_MATCHER.assertMatch(actual, admin); - } - - @Test - void getWithMealsNotFound() { - Assertions.assertThrows(NotFoundException.class, - () -> service.getWithMeals(NOT_FOUND)); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java deleted file mode 100644 index aef588264..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcMealServiceTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.javawebinar.topjava.service.jdbc; - -import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.service.AbstractMealServiceTest; - -import static ru.javawebinar.topjava.Profiles.JDBC; - -@ActiveProfiles(JDBC) -class JdbcMealServiceTest extends AbstractMealServiceTest { -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java deleted file mode 100644 index 62ca7668c..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/jdbc/JdbcUserServiceTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.javawebinar.topjava.service.jdbc; - -import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.service.AbstractUserServiceTest; - -import static ru.javawebinar.topjava.Profiles.JDBC; - -@ActiveProfiles(JDBC) -class JdbcUserServiceTest extends AbstractUserServiceTest { -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java deleted file mode 100644 index aaf5dcda9..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaMealServiceTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.javawebinar.topjava.service.jpa; - -import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.service.AbstractMealServiceTest; - -import static ru.javawebinar.topjava.Profiles.JPA; - -@ActiveProfiles(JPA) -class JpaMealServiceTest extends AbstractMealServiceTest { -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java deleted file mode 100644 index 6d1cd9154..000000000 --- a/src/test/java/ru/javawebinar/topjava/service/jpa/JpaUserServiceTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.javawebinar.topjava.service.jpa; - -import org.springframework.test.context.ActiveProfiles; -import ru.javawebinar.topjava.service.AbstractUserServiceTest; - -import static ru.javawebinar.topjava.Profiles.JPA; - -@ActiveProfiles(JPA) -class JpaUserServiceTest extends AbstractUserServiceTest { -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java deleted file mode 100644 index 962f6ba39..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.junit.jupiter.api.Assumptions; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.core.env.Environment; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.ResultMatcher; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.filter.CharacterEncodingFilter; -import ru.javawebinar.topjava.ActiveDbProfileResolver; -import ru.javawebinar.topjava.Profiles; -import ru.javawebinar.topjava.util.exception.ErrorType; - -import javax.annotation.PostConstruct; -import java.util.Locale; - -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; - - -@SpringJUnitWebConfig(locations = { - "classpath:spring/spring-app.xml", - "classpath:spring/spring-mvc.xml", - "classpath:spring/spring-db.xml" -}) -//@WebAppConfiguration -//@ExtendWith(SpringExtension.class) -@Transactional -@ActiveProfiles(resolver = ActiveDbProfileResolver.class, profiles = Profiles.REPOSITORY_IMPLEMENTATION) -public abstract class AbstractControllerTest { - private static final Locale RU_LOCALE = new Locale("ru"); - private static final CharacterEncodingFilter CHARACTER_ENCODING_FILTER = new CharacterEncodingFilter(); - - static { - CHARACTER_ENCODING_FILTER.setEncoding("UTF-8"); - CHARACTER_ENCODING_FILTER.setForceEncoding(true); - } - - private MockMvc mockMvc; - - @Autowired - private Environment env; - - @Autowired - private WebApplicationContext webApplicationContext; - - @Autowired - protected MessageSourceAccessor messageSourceAccessor; - - protected void assumeDataJpa() { - Assumptions.assumeTrue(env.acceptsProfiles(org.springframework.core.env.Profiles.of(Profiles.DATAJPA)), "DATA-JPA only"); - } - - @PostConstruct - private void postConstruct() { - mockMvc = MockMvcBuilders - .webAppContextSetup(webApplicationContext) - .addFilter(CHARACTER_ENCODING_FILTER) - .apply(springSecurity()) - .build(); - } - - protected ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception { - return mockMvc.perform(builder); - } - - private String getMessage(String code) { - return messageSourceAccessor.getMessage(code, RU_LOCALE); - } - - protected ResultMatcher errorType(ErrorType type) { - return jsonPath("$.type").value(type.name()); - } - - protected ResultMatcher detailMessage(String code) { - return jsonPath("$.details").value(getMessage(code)); - } -} diff --git a/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java deleted file mode 100644 index 244399662..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/ResourceControllerTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.junit.jupiter.api.Test; -import org.springframework.http.MediaType; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class ResourceControllerTest extends AbstractControllerTest { - - @Test - void resources() throws Exception { - perform(get("/resources/css/style.css")) - .andDo(print()) - .andExpect(content().contentTypeCompatibleWith(MediaType.valueOf("text/css"))) - .andExpect(status().isOk()); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java deleted file mode 100644 index c200fb4e9..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/RootControllerTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.javawebinar.topjava.web; - -import org.junit.jupiter.api.Test; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static ru.javawebinar.topjava.TestUtil.userAuth; -import static ru.javawebinar.topjava.UserTestData.admin; -import static ru.javawebinar.topjava.UserTestData.user; - -class RootControllerTest extends AbstractControllerTest { - - @Test - void getUsers() throws Exception { - perform(get("/users") - .with(userAuth(admin))) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(view().name("users")) - .andExpect(forwardedUrl("/WEB-INF/jsp/users.jsp")); - } - - @Test - void unAuth() throws Exception { - perform(get("/users")) - .andDo(print()) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); - } - - @Test - void getMeals() throws Exception { - perform(get("/meals") - .with(userAuth(user))) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(view().name("meals")) - .andExpect(forwardedUrl("/WEB-INF/jsp/meals.jsp")); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java b/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java deleted file mode 100644 index d1a7a5d6c..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/json/JsonUtilTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package ru.javawebinar.topjava.web.json; - -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.model.User; - -import java.util.List; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static ru.javawebinar.topjava.MealTestData.*; -import static ru.javawebinar.topjava.UserTestData.jsonWithPassword; -import static ru.javawebinar.topjava.UserTestData.user; - -class JsonUtilTest { - private static final Logger log = LoggerFactory.getLogger(JsonUtilTest.class); - - @Test - void readWriteValue() { - String json = JsonUtil.writeValue(adminMeal1); - log.info(json); - Meal meal = JsonUtil.readValue(json, Meal.class); - MEAL_MATCHER.assertMatch(meal, adminMeal1); - } - - @Test - void readWriteValues() { - String json = JsonUtil.writeValue(meals); - log.info(json); - List actual = JsonUtil.readValues(json, Meal.class); - MEAL_MATCHER.assertMatch(actual, meals); - } - - @Test - void writeOnlyAccess() { - String json = JsonUtil.writeValue(user); - System.out.println(json); - assertThat(json, not(containsString("password"))); - String jsonWithPass = jsonWithPassword(user, "newPass"); - System.out.println(jsonWithPass); - User user = JsonUtil.readValue(jsonWithPass, User.class); - assertEquals(user.getPassword(), "newPass"); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java deleted file mode 100644 index c45a1b358..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/meal/MealRestControllerTest.java +++ /dev/null @@ -1,198 +0,0 @@ -package ru.javawebinar.topjava.web.meal; - - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import ru.javawebinar.topjava.MealTestData; -import ru.javawebinar.topjava.model.Meal; -import ru.javawebinar.topjava.service.MealService; -import ru.javawebinar.topjava.util.exception.NotFoundException; -import ru.javawebinar.topjava.web.AbstractControllerTest; -import ru.javawebinar.topjava.web.json.JsonUtil; - -import java.time.LocalDateTime; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static ru.javawebinar.topjava.MealTestData.*; -import static ru.javawebinar.topjava.TestUtil.userHttpBasic; -import static ru.javawebinar.topjava.UserTestData.*; -import static ru.javawebinar.topjava.util.MealsUtil.createTo; -import static ru.javawebinar.topjava.util.MealsUtil.getTos; -import static ru.javawebinar.topjava.util.exception.ErrorType.VALIDATION_ERROR; -import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_DATETIME; - -class MealRestControllerTest extends AbstractControllerTest { - - private static final String REST_URL = MealRestController.REST_URL + '/'; - - @Autowired - private MealService mealService; - - @Test - void get() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + MEAL1_ID) - .with(userHttpBasic(user))) - .andExpect(status().isOk()) - .andDo(print()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(MEAL_MATCHER.contentJson(meal1)); - } - - @Test - void getUnauth() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + MEAL1_ID)) - .andExpect(status().isUnauthorized()); - } - - @Test - void getNotFound() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + ADMIN_MEAL_ID) - .with(userHttpBasic(user))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()); - } - - @Test - void delete() throws Exception { - perform(MockMvcRequestBuilders.delete(REST_URL + MEAL1_ID) - .with(userHttpBasic(user))) - .andExpect(status().isNoContent()); - assertThrows(NotFoundException.class, () -> mealService.get(MEAL1_ID, USER_ID)); - } - - @Test - void deleteNotFound() throws Exception { - perform(MockMvcRequestBuilders.delete(REST_URL + ADMIN_MEAL_ID) - .with(userHttpBasic(user))) - .andExpect(status().isUnprocessableEntity()); - } - - @Test - void update() throws Exception { - Meal updated = MealTestData.getUpdated(); - perform(MockMvcRequestBuilders.put(REST_URL + MEAL1_ID).contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(user)) - .content(JsonUtil.writeValue(updated))) - .andExpect(status().isNoContent()); - - MEAL_MATCHER.assertMatch(mealService.get(MEAL1_ID, USER_ID), updated); - } - - @Test - void createWithLocation() throws Exception { - Meal newMeal = MealTestData.getNew(); - ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(user)) - .content(JsonUtil.writeValue(newMeal))) - .andExpect(status().isCreated()); - - Meal created = MEAL_MATCHER.readFromJson(action); - int newId = created.id(); - newMeal.setId(newId); - MEAL_MATCHER.assertMatch(created, newMeal); - MEAL_MATCHER.assertMatch(mealService.get(newId, USER_ID), newMeal); - } - - @Test - void getAll() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL) - .with(userHttpBasic(user))) - .andExpect(status().isOk()) - .andDo(print()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(TO_MATCHER.contentJson(getTos(meals, user.getCaloriesPerDay()))); - } - - @Test - void getBetween() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + "filter") - .param("startDate", "2020-01-30").param("startTime", "07:00") - .param("endDate", "2020-01-31").param("endTime", "11:00") - .with(userHttpBasic(user))) - .andExpect(status().isOk()) - .andDo(print()) - .andExpect(TO_MATCHER.contentJson(createTo(meal5, true), createTo(meal1, false))); - } - - @Test - void getBetweenAll() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + "filter?startDate=&endTime=") - .with(userHttpBasic(user))) - .andExpect(status().isOk()) - .andExpect(TO_MATCHER.contentJson(getTos(meals, user.getCaloriesPerDay()))); - } - - @Test - void createInvalid() throws Exception { - Meal invalid = new Meal(null, null, "Dummy", 200); - perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(invalid)) - .with(userHttpBasic(admin))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - void updateInvalid() throws Exception { - Meal invalid = new Meal(MEAL1_ID, null, null, 6000); - perform(MockMvcRequestBuilders.put(REST_URL + MEAL1_ID) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(invalid)) - .with(userHttpBasic(user))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - void updateHtmlUnsafe() throws Exception { - Meal invalid = new Meal(MEAL1_ID, LocalDateTime.now(), "", 200); - perform(MockMvcRequestBuilders.put(REST_URL + MEAL1_ID) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(invalid)) - .with(userHttpBasic(user))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - @Transactional(propagation = Propagation.NEVER) - void updateDuplicate() throws Exception { - Meal invalid = new Meal(MEAL1_ID, meal2.getDateTime(), "Dummy", 200); - - perform(MockMvcRequestBuilders.put(REST_URL + MEAL1_ID) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(invalid)) - .with(userHttpBasic(user))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)) - .andExpect(detailMessage(EXCEPTION_DUPLICATE_DATETIME)); - } - - @Test - @Transactional(propagation = Propagation.NEVER) - void createDuplicate() throws Exception { - Meal invalid = new Meal(null, adminMeal1.getDateTime(), "Dummy", 200); - perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(invalid)) - .with(userHttpBasic(admin))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)) - .andExpect(detailMessage(EXCEPTION_DUPLICATE_DATETIME)); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java deleted file mode 100644 index 2abfaa933..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/user/AdminRestControllerTest.java +++ /dev/null @@ -1,219 +0,0 @@ -package ru.javawebinar.topjava.web.user; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import ru.javawebinar.topjava.model.Role; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.service.UserService; -import ru.javawebinar.topjava.util.exception.NotFoundException; -import ru.javawebinar.topjava.web.AbstractControllerTest; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static ru.javawebinar.topjava.TestUtil.userHttpBasic; -import static ru.javawebinar.topjava.UserTestData.*; -import static ru.javawebinar.topjava.util.exception.ErrorType.VALIDATION_ERROR; -import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL; - -class AdminRestControllerTest extends AbstractControllerTest { - - private static final String REST_URL = AdminRestController.REST_URL + '/'; - - @Autowired - private UserService userService; - - @Test - void get() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + ADMIN_ID) - .with(userHttpBasic(admin))) - .andExpect(status().isOk()) - .andDo(print()) - // https://jira.spring.io/browse/SPR-14472 - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(USER_MATCHER.contentJson(admin)); - } - - @Test - void getNotFound() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + NOT_FOUND) - .with(userHttpBasic(admin))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()); - } - - @Test - void getByEmail() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL + "by-email?email=" + user.getEmail()) - .with(userHttpBasic(admin))) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(USER_MATCHER.contentJson(user)); - } - - @Test - void delete() throws Exception { - perform(MockMvcRequestBuilders.delete(REST_URL + USER_ID) - .with(userHttpBasic(admin))) - .andDo(print()) - .andExpect(status().isNoContent()); - assertThrows(NotFoundException.class, () -> userService.get(USER_ID)); - } - - @Test - void deleteNotFound() throws Exception { - perform(MockMvcRequestBuilders.delete(REST_URL + NOT_FOUND) - .with(userHttpBasic(admin))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()); - } - - @Test - void getUnAuth() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL)) - .andExpect(status().isUnauthorized()); - } - - @Test - void getForbidden() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL) - .with(userHttpBasic(user))) - .andExpect(status().isForbidden()); - } - - @Test - void update() throws Exception { - User updated = getUpdated(); - updated.setId(null); - perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(jsonWithPassword(updated, updated.getPassword()))) - .andExpect(status().isNoContent()); - - USER_MATCHER.assertMatch(userService.get(USER_ID), getUpdated()); - } - - @Test - void createWithLocation() throws Exception { - User newUser = getNew(); - ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(jsonWithPassword(newUser, newUser.getPassword()))) - .andExpect(status().isCreated()); - - User created = USER_MATCHER.readFromJson(action); - int newId = created.id(); - newUser.setId(newId); - USER_MATCHER.assertMatch(created, newUser); - USER_MATCHER.assertMatch(userService.get(newId), newUser); - } - - @Test - void getAll() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL) - .with(userHttpBasic(admin))) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(USER_MATCHER.contentJson(admin, guest, user)); - } - - @Test - void getWithMeals() throws Exception { - assumeDataJpa(); - perform(MockMvcRequestBuilders.get(REST_URL + ADMIN_ID + "/with-meals") - .with(userHttpBasic(admin))) - .andExpect(status().isOk()) - .andDo(print()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(USER_WITH_MEALS_MATCHER.contentJson(admin)); - } - - @Test - void enable() throws Exception { - perform(MockMvcRequestBuilders.patch(REST_URL + USER_ID) - .param("enabled", "false") - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin))) - .andDo(print()) - .andExpect(status().isNoContent()); - - assertFalse(userService.get(USER_ID).isEnabled()); - } - - @Test - void createInvalid() throws Exception { - User invalid = new User(null, null, "", "newPass", 7300, Role.USER, Role.ADMIN); - perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(jsonWithPassword(invalid, "newPass"))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - void updateInvalid() throws Exception { - User invalid = new User(user); - invalid.setName(""); - perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(jsonWithPassword(invalid, "password"))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - void updateHtmlUnsafe() throws Exception { - User updated = new User(user); - updated.setName(""); - perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(jsonWithPassword(updated, "password"))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - @Transactional(propagation = Propagation.NEVER) - void updateDuplicate() throws Exception { - User updated = new User(user); - updated.setId(null); - updated.setEmail("admin@gmail.com"); - perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(jsonWithPassword(updated, "password"))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)) - .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); - } - - @Test - @Transactional(propagation = Propagation.NEVER) - void createDuplicate() throws Exception { - User expected = new User(null, "New", "user@yandex.ru", "newPass", 2300, Role.USER, Role.ADMIN); - perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(jsonWithPassword(expected, "newPass"))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)) - .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java index 7568d0f52..2386eeee4 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java @@ -1,18 +1,22 @@ package ru.javawebinar.topjava.web.user; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; import ru.javawebinar.topjava.repository.inmemory.InMemoryUserRepository; import ru.javawebinar.topjava.util.exception.NotFoundException; import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; import static ru.javawebinar.topjava.UserTestData.USER_ID; -@SpringJUnitConfig(locations = {"classpath:spring/inmemory.xml"}) -class InMemoryAdminRestControllerSpringTest { +@ContextConfiguration({"classpath:spring/spring-app.xml", "classpath:spring/inmemory.xml"}) +@RunWith(SpringRunner.class) +public class InMemoryAdminRestControllerSpringTest { @Autowired private AdminRestController controller; @@ -20,19 +24,19 @@ class InMemoryAdminRestControllerSpringTest { @Autowired private InMemoryUserRepository repository; - @BeforeEach - void setup() { + @Before + public void setUp() { repository.init(); } @Test - void delete() { + public void delete() { controller.delete(USER_ID); - Assertions.assertNull(repository.get(USER_ID)); + Assert.assertNull(repository.get(USER_ID)); } @Test - void deleteNotFound() { - Assertions.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); + public void deleteNotFound() { + Assert.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java index c41fa0e6b..7cc2a833c 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java @@ -1,6 +1,6 @@ package ru.javawebinar.topjava.web.user; -import org.junit.jupiter.api.*; +import org.junit.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ConfigurableApplicationContext; @@ -13,42 +13,40 @@ import static ru.javawebinar.topjava.UserTestData.NOT_FOUND; import static ru.javawebinar.topjava.UserTestData.USER_ID; -class InMemoryAdminRestControllerTest { +public class InMemoryAdminRestControllerTest { private static final Logger log = LoggerFactory.getLogger(InMemoryAdminRestControllerTest.class); private static ConfigurableApplicationContext appCtx; private static AdminRestController controller; private static InMemoryUserRepository repository; - @BeforeAll - static void beforeClass() { - appCtx = new ClassPathXmlApplicationContext("spring/inmemory.xml"); + @BeforeClass + public static void beforeClass() { + appCtx = new ClassPathXmlApplicationContext("spring/spring-app.xml", "spring/inmemory.xml"); log.info("\n{}\n", Arrays.toString(appCtx.getBeanDefinitionNames())); controller = appCtx.getBean(AdminRestController.class); repository = appCtx.getBean(InMemoryUserRepository.class); } - @AfterAll - static void afterClass() { - // May cause during JUnit "Cache is not alive (STATUS_SHUTDOWN)" as JUnit share Spring context for speed - // http://stackoverflow.com/questions/16281802/ehcache-shutdown-causing-an-exception-while-running-test-suite - // appCtx.close(); + @AfterClass + public static void afterClass() { + appCtx.close(); } - @BeforeEach - void setup() { + @Before + public void setUp() { // re-initialize repository.init(); } @Test - void delete() { + public void delete() { controller.delete(USER_ID); - Assertions.assertNull(repository.get(USER_ID)); + Assert.assertNull(repository.get(USER_ID)); } @Test - void deleteNotFound() { - Assertions.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); + public void deleteNotFound() { + Assert.assertThrows(NotFoundException.class, () -> controller.delete(NOT_FOUND)); } } \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java deleted file mode 100644 index bc71318c8..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ /dev/null @@ -1,131 +0,0 @@ -package ru.javawebinar.topjava.web.user; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import ru.javawebinar.topjava.model.User; -import ru.javawebinar.topjava.service.UserService; -import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.util.UsersUtil; -import ru.javawebinar.topjava.web.AbstractControllerTest; -import ru.javawebinar.topjava.web.json.JsonUtil; - -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static ru.javawebinar.topjava.TestUtil.userHttpBasic; -import static ru.javawebinar.topjava.UserTestData.*; -import static ru.javawebinar.topjava.util.exception.ErrorType.VALIDATION_ERROR; -import static ru.javawebinar.topjava.web.ExceptionInfoHandler.EXCEPTION_DUPLICATE_EMAIL; -import static ru.javawebinar.topjava.web.user.ProfileRestController.REST_URL; - -class ProfileRestControllerTest extends AbstractControllerTest { - - @Autowired - private UserService userService; - - @Test - void get() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL) - .with(userHttpBasic(user))) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(USER_MATCHER.contentJson(user)); - } - - @Test - void getUnAuth() throws Exception { - perform(MockMvcRequestBuilders.get(REST_URL)) - .andExpect(status().isUnauthorized()); - } - - @Test - void delete() throws Exception { - perform(MockMvcRequestBuilders.delete(REST_URL) - .with(userHttpBasic(user))) - .andExpect(status().isNoContent()); - USER_MATCHER.assertMatch(userService.getAll(), admin, guest); - } - - @Test - void register() throws Exception { - UserTo newTo = new UserTo(null, "newName", "newemail@ya.ru", "newPassword", 1500); - User newUser = UsersUtil.createNewFromTo(newTo); - ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(newTo))) - .andDo(print()) - .andExpect(status().isCreated()); - - User created = USER_MATCHER.readFromJson(action); - int newId = created.id(); - newUser.setId(newId); - USER_MATCHER.assertMatch(created, newUser); - USER_MATCHER.assertMatch(userService.get(newId), newUser); - } - - @Test - void update() throws Exception { - // ValidationUtil.assureIdConsistent called after validation, needs workaround - UserTo updatedTo = new UserTo(null, "newName", "user@yandex.ru", "newPassword", 1500); - perform(MockMvcRequestBuilders.put(REST_URL).contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(user)) - .content(JsonUtil.writeValue(updatedTo))) - .andDo(print()) - .andExpect(status().isNoContent()); - - USER_MATCHER.assertMatch(userService.get(USER_ID), UsersUtil.updateFromTo(new User(user), updatedTo)); - } - - @Test - void getWithMeals() throws Exception { - assumeDataJpa(); - perform(MockMvcRequestBuilders.get(REST_URL + "/with-meals") - .with(userHttpBasic(user))) - .andExpect(status().isOk()) - .andDo(print()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(USER_WITH_MEALS_MATCHER.contentJson(user)); - } - - @Test - void registerInvalid() throws Exception { - UserTo newTo = new UserTo(null, null, null, null, 1); - perform(MockMvcRequestBuilders.post(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.writeValue(newTo))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - void updateInvalid() throws Exception { - UserTo updatedTo = new UserTo(null, null, "password", null, 1500); - perform(MockMvcRequestBuilders.put(REST_URL) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(user)) - .content(JsonUtil.writeValue(updatedTo))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)); - } - - @Test - @Transactional(propagation = Propagation.NEVER) - void updateDuplicate() throws Exception { - UserTo updatedTo = new UserTo(null, "newName", "admin@gmail.com", "newPassword", 1500); - - perform(MockMvcRequestBuilders.put(REST_URL).contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(user)) - .content(JsonUtil.writeValue(updatedTo))) - .andDo(print()) - .andExpect(status().isUnprocessableEntity()) - .andExpect(errorType(VALIDATION_ERROR)) - .andExpect(detailMessage(EXCEPTION_DUPLICATE_EMAIL)); - } -} \ No newline at end of file diff --git a/src/test/java/ru/javawebinar/topjava/web/user/VdsRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/VdsRestControllerTest.java deleted file mode 100644 index fc05eb977..000000000 --- a/src/test/java/ru/javawebinar/topjava/web/user/VdsRestControllerTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.javawebinar.topjava.web.user; - -import org.junit.jupiter.api.Test; -import org.springframework.http.MediaType; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import ru.javawebinar.topjava.UserTestData; -import ru.javawebinar.topjava.util.exception.ErrorType; -import ru.javawebinar.topjava.web.AbstractControllerTest; - -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static ru.javawebinar.topjava.Profiles.VDS; -import static ru.javawebinar.topjava.TestUtil.userHttpBasic; -import static ru.javawebinar.topjava.UserTestData.*; -import static ru.javawebinar.topjava.util.exception.UpdateRestrictionException.EXCEPTION_UPDATE_RESTRICTION; - -@ActiveProfiles(VDS) -class VdsRestControllerTest extends AbstractControllerTest { - - private static final String REST_URL = AdminRestController.REST_URL + '/'; - - @Test - void delete() throws Exception { - perform(MockMvcRequestBuilders.delete(REST_URL + USER_ID) - .with(userHttpBasic(admin))) - .andDo(print()) - .andExpect(errorType(ErrorType.VALIDATION_ERROR)) - .andExpect(detailMessage(EXCEPTION_UPDATE_RESTRICTION)) - .andExpect(status().isUnprocessableEntity()); - } - - @Test - void update() throws Exception { - perform(MockMvcRequestBuilders.put(REST_URL + USER_ID) - .contentType(MediaType.APPLICATION_JSON) - .with(userHttpBasic(admin)) - .content(UserTestData.jsonWithPassword(user, "password"))) - .andExpect(errorType(ErrorType.VALIDATION_ERROR)) - .andExpect(detailMessage(EXCEPTION_UPDATE_RESTRICTION)) - .andExpect(status().isUnprocessableEntity()); - } -} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 803655475..da4eed7b9 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -7,22 +7,12 @@ UTF-8 - %d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%class{50}.%M:%L) - %msg%n + %d{HH:mm:ss.SSS} %-5level %class{50}.%M:%L - %msg%n - - - UTF-8 - %magenta(%msg%n) - - - - - - - - + + diff --git a/src/test/resources/spring/inmemory.xml b/src/test/resources/spring/inmemory.xml index 1207399dd..c6a2710cb 100644 --- a/src/test/resources/spring/inmemory.xml +++ b/src/test/resources/spring/inmemory.xml @@ -4,8 +4,4 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> - - - - \ No newline at end of file diff --git a/src/test/resources/spring/spring-cache.xml b/src/test/resources/spring/spring-cache.xml deleted file mode 100644 index 7c9dfda9a..000000000 --- a/src/test/resources/spring/spring-cache.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - false - - - - - - \ No newline at end of file