diff --git a/README.md b/README.md index 10f296158..7fd61b43a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![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) Java Enterprise Online Project =============================== @@ -11,13 +11,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/) -### 29.01: Старт проекта -- Начало проверки [вступительного задания HW0](https://github.com/JavaOPs/topjava#-Домашнее-задание-hw0) +### 26.05: Старт проекта +- Начало проверки [вступительного задания](https://github.com/JavaOPs/topjava#-Домашнее-задание-hw0) -#### 02.02 Дедлайн на сдачу HW0 -### 05.02: 1-е занятие +#### 31.05 Дедлайн на сдачу HW0 +### 02.06: 1-е занятие +#### 03.06 Дедлайн подачи заявки на [дипломную программу](https://javaops.ru/view/register/diploma) - Разбор домашнего задания вступительного занятия (вместе с Optional) - Обзор используемых в проекте технологий. Интеграция ПО - Maven @@ -26,7 +27,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Уровни и зависимости логгирования. JMX - Домашнее задание 1-го занятия (HW1 + Optional) -### 12.02: 2-е занятие +### 09.06: 2-е занятие - Разбор домашнего задания HW1 + Optional - Библиотека vs Фреймворк. Стандартные библиотеки Apache Commons, Guava - Слои приложения. Создание каркаса приложения @@ -34,7 +35,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Пояснения к HW2. Обработка Autowired - Домашнее задание (HW2 + Optional) -### 19.02: 3-е занятие +### 16.06: 3-е занятие - Разбор домашнего задания HW2 + Optional - Жизненный цикл Spring контекста - Тестирование через JUnit @@ -47,7 +48,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Логирование тестов - Домашнее задание (HW3 + Optional) -### 26.02: 4-е занятие +### 23.06: 4-е занятие - Разбор домашнего задания HW3 + Optional - Методы улучшения качества кода - Spring: инициализация и популирование DB @@ -57,7 +58,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Домашнее задание (HW4 + Optional) #### Начало выполнения [выпускного проекта](https://github.com/JavaOPs/topjava/blob/master/graduation.md) -### 05.03: 5-е занятие +### 30.06: 5-е занятие - Обзор JDK 9/17. Миграция Topjava с 1.8 на 17 - Разбор вопросов - Разбор домашнего задания HW4 + Optional @@ -68,7 +69,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Spring кэш - Домашнее задание (HW5 + Optional) -### 12.03: 6-е занятие +### 07.07: 6-е занятие - Разбор домашнего задания HW5 + Optional - Кэш Hibernate - Spring Web @@ -81,7 +82,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + #### Большое ДЗ + выпускной проект + начинаем [курс BootJava](https://javaops.ru/view/bootjava) + подтягиваем "хвосты". -### 26.03: 7-е занятие +### 21.07: 7-е занятие - Разбор домашнего задания HW6 + Optional - Автогенерация DDL по модели - Тестирование Spring MVC @@ -92,7 +93,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Тестирование через SoapUi. UTF-8 - Домашнее задание (HW7 + Optional) -### 02.04: 8-е занятие +### 28.07: 8-е занятие - Разбор домашнего задания HW7 + Optional - WebJars. jQuery и JavaScript frameworks - Bootstrap @@ -101,7 +102,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Добавление Spring Security - Домашнее задание (HW8 + Optional) -### 09.04: 9-е занятие +### 04.08: 9-е занятие - Разбор домашнего задания HW8 + Optional - Spring Binding - Spring Validation @@ -113,7 +114,7 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Cookie. Session - Домашнее задание (HW9 + Optional) -### 16.04: 10-е занятие +### 11.08: 10-е занятие - Разбор домашнего задания HW10 + Optional - Кастомизация JSON (@JsonView) и валидации (groups) - Рефакторинг: jQuery конверторы и группы валидации по умолчанию @@ -126,22 +127,23 @@ Maven/ Spring/ Security/ JPA(Hibernate)/ REST(Jackson)/ Bootstrap(CSS)/ jQuery + - Защита от межсайтовой подделки запросов (CSRF) - Домашнее задание (HW10) -### 23.04: 11-е занятие +### 18.08: 11-е занятие - Разбор домашнего задания HW10 + Optional - Локализация datatables, ошибок валидации - Защита от XSS (Cross Site Scripting) - Обработка ошибок 404 (NotFound) - Доступ к AuthorizedUser - Ограничение модификации пользователей -- Деплой приложения [на собственный выделенный сервер](https://github.com/JavaOPs/startup) -- Домашнее задание (HW11): сокрытия полей в Swagger -- Составление резюме. Собеседование. Разработка ПО. Возможные доработки приложения - -### 27.04: Миграция на 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 -### 11.05: Дедлайн на сдачу [выпускного проекта](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/messages/app.properties b/config/messages/app.properties index 979407b48..8dec4e48f 100644 --- a/config/messages/app.properties +++ b/config/messages/app.properties @@ -2,8 +2,8 @@ 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. +Meals could be filtered by date and time. Meal record color depends on daily calories sum exceeding "Daily calorie limit" (editable user's profile parameter). \ +All REST interface covered with JUnit tests by Spring MVC Test и Spring Security Test. app.footer=Spring 5/JPA Enterprise (Topjava) internship application app.login=Login as app.profile=profile diff --git a/pom.xml b/pom.xml index d47b60047..1a9225385 100644 --- a/pom.xml +++ b/pom.xml @@ -9,68 +9,71 @@ 1.0-SNAPSHOT Calories Management - https://javaops-demo.ru/topjava + http://topjava.herokuapp.com/ - 21 + 17 UTF-8 UTF-8 - 5.3.39 - 5.8.16 - 2.7.18 - 2.21.2 - 9.0.113 + + 5.3.20 + 2.7.1 + 5.7.2 + + 2.13.3 + 9.0.64 + + + 1.2.11 + 1.7.36 + + + 42.4.0 - 5.6.15.Final - 6.2.5.Final + 5.6.9.Final + 6.2.3.Final 3.0.1-b12 - 3.10.8 + 3.10.0 + + + 5.8.2 + 3.23.1 + 2.2 - 4.6.2 - 3.7.1 + 4.6.1 + 3.6.0 2.5.20-1 3.1.4 - 1.13.5 - - - 1.5.20 - 2.0.17 - - - 42.7.8 - - 5.14.1 - 3.27.6 - 3.0 + 1.11.4 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-war-plugin + 3.3.2 + org.apache.maven.plugins maven-surefire-plugin - 3.5.4 + 2.22.2 -Dfile.encoding=UTF-8 @@ -81,7 +84,7 @@ org.codehaus.cargo cargo-maven3-plugin - 1.10.26 + 1.9.13 tomcat9x @@ -126,6 +129,7 @@ org.slf4j slf4j-api ${slf4j.version} + compile @@ -135,14 +139,6 @@ runtime - - - com.google.code.findbugs - annotations - 3.0.1 - compile - - javax.annotation javax.annotation-api @@ -312,7 +308,7 @@ org.junit.jupiter junit-jupiter-engine - ${junit.version} + ${junit.jupiter.version} test @@ -345,7 +341,7 @@ org.junit.platform junit-platform-launcher - 1.14.3 + 1.8.2 test @@ -357,7 +353,7 @@ org.hsqldb hsqldb - 2.7.4 + 2.6.1 diff --git a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java index b51930e87..2126d687b 100644 --- a/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java +++ b/src/main/java/ru/javawebinar/topjava/AuthorizedUser.java @@ -2,7 +2,7 @@ import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; -import ru.javawebinar.topjava.util.UsersUtil; +import ru.javawebinar.topjava.util.UserUtil; import java.io.Serial; @@ -14,7 +14,7 @@ public class AuthorizedUser extends org.springframework.security.core.userdetail public AuthorizedUser(User user) { super(user.getEmail(), user.getPassword(), user.isEnabled(), true, true, true, user.getRoles()); - setTo(UsersUtil.asTo(user)); + setTo(UserUtil.asTo(user)); } public int getId() { diff --git a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java index 16ae37b0f..536c5c986 100644 --- a/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java +++ b/src/main/java/ru/javawebinar/topjava/model/AbstractBaseEntity.java @@ -1,11 +1,11 @@ package ru.javawebinar.topjava.model; +import org.hibernate.Hibernate; +import org.springframework.util.Assert; import ru.javawebinar.topjava.HasId; 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) @@ -40,19 +40,23 @@ public Integer getId() { @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().equals(Hibernate.getClass(o))) { + 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/Meal.java b/src/main/java/ru/javawebinar/topjava/model/Meal.java index 00e7e6372..ed5063121 100644 --- a/src/main/java/ru/javawebinar/topjava/model/Meal.java +++ b/src/main/java/ru/javawebinar/topjava/model/Meal.java @@ -27,7 +27,7 @@ // "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")}) +@Table(name = "meals", uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "date_time"}, name = "meals_unique_user_datetime_idx")}) public class Meal extends AbstractBaseEntity { public static final String ALL_SORTED = "Meal.getAll"; public static final String DELETE = "Meal.delete"; diff --git a/src/main/java/ru/javawebinar/topjava/model/User.java b/src/main/java/ru/javawebinar/topjava/model/User.java index 12c608d0c..00e9d5265 100644 --- a/src/main/java/ru/javawebinar/topjava/model/User.java +++ b/src/main/java/ru/javawebinar/topjava/model/User.java @@ -19,7 +19,7 @@ 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.UserUtil.DEFAULT_CALORIES_PER_DAY; @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @NamedQueries({ @@ -58,11 +58,11 @@ public class User extends AbstractNamedEntity { @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) +// @Fetch(FetchMode.SUBSELECT) @BatchSize(size = 200) @JoinColumn @OnDelete(action = OnDeleteAction.CASCADE) @@ -86,7 +86,7 @@ public User(User u) { } 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)); + this(id, name, email, password, caloriesPerDay, true, new Date(), Arrays.asList((roles))); } public User(Integer id, String name, String email, String password, int caloriesPerDay, boolean enabled, Date registered, Collection roles) { 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 775d314ed..82c80dd7b 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcMealRepository.java @@ -30,7 +30,7 @@ public class JdbcMealRepository implements MealRepository { public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.insertMeal = new SimpleJdbcInsert(jdbcTemplate) - .withTableName("meal") + .withTableName("meals") .usingGeneratedKeyColumns("id"); this.jdbcTemplate = jdbcTemplate; @@ -54,7 +54,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; @@ -66,26 +66,26 @@ 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 ca5dcc185..dda61a5f4 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jdbc/JdbcUserRepository.java @@ -90,7 +90,7 @@ 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 -> { + jdbcTemplate.query("SELECT * FROM user_roles", rs -> { map.computeIfAbsent(rs.getInt("user_id"), userId -> EnumSet.noneOf(Role.class)) .add(Role.valueOf(rs.getString("role"))); }); @@ -101,7 +101,7 @@ public List getAll() { 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(), + jdbcTemplate.batchUpdate("INSERT INTO user_roles (user_id, role) VALUES (?, ?)", roles, roles.size(), (ps, role) -> { ps.setInt(1, u.id()); ps.setString(2, role.name()); @@ -110,12 +110,12 @@ private void insertRoles(User u) { } private void deleteRoles(User u) { - jdbcTemplate.update("DELETE FROM user_role WHERE user_id=?", u.getId()); + jdbcTemplate.update("DELETE FROM user_roles 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()); + List roles = jdbcTemplate.queryForList("SELECT role FROM user_roles WHERE user_id=?", Role.class, u.getId()); u.setRoles(roles); } return u; 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..300a920ae 100644 --- a/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java +++ b/src/main/java/ru/javawebinar/topjava/repository/jpa/JpaMealRepository.java @@ -25,8 +25,10 @@ public Meal save(Meal meal, int userId) { if (meal.isNew()) { em.persist(meal); return meal; + } else if (get(meal.id(), userId) == null) { + return null; } - return get(meal.id(), userId) == null ? null : em.merge(meal); + return em.merge(meal); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/service/MealService.java b/src/main/java/ru/javawebinar/topjava/service/MealService.java index 5f759a11a..5e08c9e5a 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.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,7 +40,7 @@ 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) { @@ -49,6 +49,6 @@ public Meal create(Meal meal, int userId) { } public Meal getWithUser(int id, int userId) { - return checkNotFound(repository.getWithUser(id, userId), id); + return checkNotFoundWithId(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 13ae1c5fa..0946273f6 100644 --- a/src/main/java/ru/javawebinar/topjava/service/UserService.java +++ b/src/main/java/ru/javawebinar/topjava/service/UserService.java @@ -14,12 +14,13 @@ 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.UserUtil; import java.util.List; -import static ru.javawebinar.topjava.util.UsersUtil.prepareToSave; +import static ru.javawebinar.topjava.util.UserUtil.prepareToSave; import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFound; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId; @Service("userService") @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) @@ -41,11 +42,11 @@ public User create(User user) { @CacheEvict(value = "users", allEntries = true) public void delete(int 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) { @@ -61,16 +62,15 @@ public List 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 +// checkNotFoundWithId : check works only for JDBC, disabled prepareAndSave(user); } - @CacheEvict(value = "users", allEntries = true) @Transactional public void update(UserTo userTo) { User user = get(userTo.id()); - prepareAndSave(UsersUtil.updateFromTo(user, userTo)); + prepareAndSave(UserUtil.updateFromTo(user, userTo)); } @CacheEvict(value = "users", allEntries = true) @@ -95,6 +95,6 @@ private User prepareAndSave(User user) { } public User getWithMeals(int id) { - return checkNotFound(repository.getWithMeals(id), id); + return checkNotFoundWithId(repository.getWithMeals(id), id); } } \ No newline at end of file diff --git a/src/main/java/ru/javawebinar/topjava/to/UserTo.java b/src/main/java/ru/javawebinar/topjava/to/UserTo.java index 217d7390a..6f63cfa59 100644 --- a/src/main/java/ru/javawebinar/topjava/to/UserTo.java +++ b/src/main/java/ru/javawebinar/topjava/to/UserTo.java @@ -1,7 +1,7 @@ package ru.javawebinar.topjava.to; import org.hibernate.validator.constraints.Range; -import ru.javawebinar.topjava.util.UsersUtil; +import ru.javawebinar.topjava.util.UserUtil; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; @@ -29,7 +29,7 @@ public class UserTo extends BaseTo implements Serializable { @Range(min = 10, max = 10000) @NotNull - private Integer caloriesPerDay = UsersUtil.DEFAULT_CALORIES_PER_DAY; + private Integer caloriesPerDay = UserUtil.DEFAULT_CALORIES_PER_DAY; public UserTo() { } diff --git a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java index 09052faa5..1fb662b11 100644 --- a/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java @@ -7,6 +7,7 @@ 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"; @@ -24,7 +25,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/UsersUtil.java b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java similarity index 97% rename from src/main/java/ru/javawebinar/topjava/util/UsersUtil.java rename to src/main/java/ru/javawebinar/topjava/util/UserUtil.java index bdb4291f7..a0700dd49 100644 --- a/src/main/java/ru/javawebinar/topjava/util/UsersUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/UserUtil.java @@ -5,7 +5,7 @@ import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.UserTo; -public class UsersUtil { +public class UserUtil { public static final int DEFAULT_CALORIES_PER_DAY = 2000; diff --git a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java index e300f00c3..6c91f95fd 100644 --- a/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java +++ b/src/main/java/ru/javawebinar/topjava/util/ValidationUtil.java @@ -1,5 +1,6 @@ package ru.javawebinar.topjava.util; + import org.springframework.core.NestedExceptionUtils; import org.springframework.http.ResponseEntity; import org.springframework.lang.NonNull; @@ -34,12 +35,12 @@ public static void validate(T bean) { } } - public static T checkNotFound(T object, int id) { - checkNotFound(object != null, id); + public static T checkNotFoundWithId(T object, int id) { + checkNotFoundWithId(object != null, id); return object; } - public static void checkNotFound(boolean found, int id) { + public static void checkNotFoundWithId(boolean found, int id) { checkNotFound(found, "id=" + id); } @@ -54,7 +55,7 @@ public static void checkNotFound(boolean found, String msg) { } } - public static void checkIsNew(HasId bean) { + public static void checkNew(HasId bean) { if (!bean.isNew()) { throw new IllegalRequestDataException(bean + " must be new (id=null)"); } diff --git a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java index 5b7e95758..aa841a06f 100644 --- a/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java +++ b/src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java @@ -25,12 +25,12 @@ @RestControllerAdvice(annotations = RestController.class) @Order(Ordered.HIGHEST_PRECEDENCE + 5) public class ExceptionInfoHandler { - private static final Logger log = LoggerFactory.getLogger(ExceptionInfoHandler.class); + private static Logger log = LoggerFactory.getLogger(ExceptionInfoHandler.class); // http://stackoverflow.com/a/22358422/548473 @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) @ExceptionHandler(NotFoundException.class) - public ErrorInfo notFoundError(HttpServletRequest req, NotFoundException e) { + public ErrorInfo handleError(HttpServletRequest req, NotFoundException e) { return logAndGetErrorInfo(req, e, false, DATA_NOT_FOUND); } @@ -42,13 +42,13 @@ public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationExceptio @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) // 422 @ExceptionHandler({IllegalRequestDataException.class, MethodArgumentTypeMismatchException.class, HttpMessageNotReadableException.class}) - public ErrorInfo validationError(HttpServletRequest req, Exception e) { + public ErrorInfo illegalRequestDataError(HttpServletRequest req, Exception e) { return logAndGetErrorInfo(req, e, false, VALIDATION_ERROR); } @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) - public ErrorInfo internalError(HttpServletRequest req, Exception e) { + public ErrorInfo handleError(HttpServletRequest req, Exception e) { return logAndGetErrorInfo(req, e, true, APP_ERROR); } diff --git a/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java b/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java index 15448ce8c..bc4409869 100644 --- a/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java +++ b/src/main/java/ru/javawebinar/topjava/web/converter/DateTimeFormatters.java @@ -7,12 +7,15 @@ import java.time.format.DateTimeFormatter; import java.util.Locale; +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalDate; +import static ru.javawebinar.topjava.util.DateTimeUtil.parseLocalTime; + public class DateTimeFormatters { public static class LocalDateFormatter implements Formatter { @Override public LocalDate parse(String text, Locale locale) { - return LocalDate.parse(text); + return parseLocalDate(text); } @Override @@ -25,7 +28,7 @@ public static class LocalTimeFormatter implements Formatter { @Override public LocalTime parse(String text, Locale locale) { - return LocalTime.parse(text); + return parseLocalTime(text); } @Override diff --git a/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java b/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java index 1931f998f..ec601c187 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/AbstractMealController.java @@ -15,7 +15,7 @@ import java.util.List; import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; -import static ru.javawebinar.topjava.util.ValidationUtil.checkIsNew; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; public abstract class AbstractMealController { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -44,7 +44,7 @@ public List getAll() { public Meal create(Meal meal) { int userId = SecurityUtil.authUserId(); log.info("create {} for user {}", meal, userId); - checkIsNew(meal); + checkNew(meal); return service.create(meal, userId); } 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 54d827d28..af1da8138 100644 --- a/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java +++ b/src/main/java/ru/javawebinar/topjava/web/meal/MealRestController.java @@ -56,7 +56,6 @@ public ResponseEntity createWithLocation(@RequestBody Meal meal) { return ResponseEntity.created(uriOfNewResource).body(created); } - @Override @GetMapping("/filter") public List getBetween( @RequestParam @Nullable LocalDate startDate, @@ -65,4 +64,4 @@ public List getBetween( @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 7317c3eb2..532e17816 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AbstractUserController.java @@ -6,12 +6,12 @@ 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.util.UserUtil; import java.util.List; import static ru.javawebinar.topjava.util.ValidationUtil.assureIdConsistent; -import static ru.javawebinar.topjava.util.ValidationUtil.checkIsNew; +import static ru.javawebinar.topjava.util.ValidationUtil.checkNew; public abstract class AbstractUserController { protected final Logger log = LoggerFactory.getLogger(getClass()); @@ -31,13 +31,13 @@ public User get(int id) { public User create(UserTo userTo) { log.info("create {}", userTo); - checkIsNew(userTo); - return service.create(UsersUtil.createNewFromTo(userTo)); + checkNew(userTo); + return service.create(UserUtil.createNewFromTo(userTo)); } public User create(User user) { log.info("create {}", user); - checkIsNew(user); + checkNew(user); return service.create(user); } diff --git a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java index ec66952e5..0f5218560 100644 --- a/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java +++ b/src/main/java/ru/javawebinar/topjava/web/user/AdminUIController.java @@ -36,6 +36,7 @@ public void delete(@PathVariable int id) { } @PostMapping + @ResponseStatus(HttpStatus.NO_CONTENT) public ResponseEntity createOrUpdate(@Valid UserTo userTo, BindingResult result) { if (result.hasErrors()) { // TODO change to exception handler diff --git a/src/main/resources/db/initDB.sql b/src/main/resources/db/initDB.sql index 4bf3d8446..7644dc610 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; @@ -17,7 +17,7 @@ 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 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..f2bb54b1e 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,7 +18,7 @@ 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, @@ -26,7 +26,7 @@ CREATE TABLE user_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..8265d3655 100644 --- a/src/main/resources/db/populateDB.sql +++ b/src/main/resources/db/populateDB.sql @@ -1,5 +1,5 @@ -DELETE FROM user_role; -DELETE FROM meal; +DELETE FROM user_roles; +DELETE FROM meals; DELETE FROM users; ALTER SEQUENCE global_seq RESTART WITH 100000; @@ -8,12 +8,12 @@ VALUES ('User', 'user@yandex.ru', '{noop}password', 2005), ('Admin', 'admin@gmail.com', '{noop}admin', 1900), ('Guest', 'guest@gmail.com', '{noop}guest', 2000); -INSERT INTO user_role (role, user_id) +INSERT INTO user_roles (role, user_id) VALUES ('USER', 100000), ('ADMIN', 100001), ('USER', 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 +22,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..ba40447d4 100644 --- a/src/main/resources/db/postgres.properties +++ b/src/main/resources/db/postgres.properties @@ -1,3 +1,7 @@ +#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:5432/topjava database.username=user database.password=password diff --git a/src/main/resources/db/tomcat.properties b/src/main/resources/db/tomcat.properties index e11f0725f..2e073681a 100644 --- a/src/main/resources/db/tomcat.properties +++ b/src/main/resources/db/tomcat.properties @@ -1,5 +1,5 @@ database.init=false -jdbc.initLocation=classpath:db/initDB.sql +jdbc.initLocation=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..ab4cfe51e 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -30,4 +30,4 @@ - \ 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 index cc302e868..8c0600763 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp @@ -12,18 +12,18 @@ - + - + <%--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 index 98bd60fa9..3e2ad09be 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/i18n.jsp @@ -3,12 +3,12 @@ <%@ 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/web.xml b/src/main/webapp/WEB-INF/web.xml index 31dcf2676..9816e2a1b 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -4,7 +4,7 @@ http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> - TopJava + Topjava spring.profiles.default diff --git a/src/main/webapp/resources/images/icon-meal.png b/src/main/webapp/resources/images/icon-meal.png index bb9dc2d73..b4fc54ad0 100644 Binary files a/src/main/webapp/resources/images/icon-meal.png and b/src/main/webapp/resources/images/icon-meal.png differ diff --git a/src/main/webapp/resources/js/topjava.common.js b/src/main/webapp/resources/js/topjava.common.js index 07258e129..261659f9f 100644 --- a/src/main/webapp/resources/js/topjava.common.js +++ b/src/main/webapp/resources/js/topjava.common.js @@ -2,16 +2,17 @@ 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 - } - ); + // https://api.jquery.com/jquery.extend/#jQuery-extend-deep-target-object1-objectN + $.extend(true, datatableOpts, + { + "ajax": { + "url": ctx.ajaxUrl, + "dataSrc": "" + }, + "paging": false, + "info": true + } + )); form = $('#detailsForm'); $(document).ajaxError(function (event, jqXHR, options, jsExc) { @@ -85,7 +86,7 @@ function closeNoty() { function successNoty(key) { closeNoty(); new Noty({ - text: `  ${i18n[key]}`, + text: "  " + i18n[key], type: 'success', layout: "bottomRight", timeout: 1000 @@ -94,13 +95,13 @@ function successNoty(key) { function renderEditBtn(data, type, row) { if (type === "display") { - return ``; + return ""; } } function renderDeleteBtn(data, type, row) { if (type === "display") { - return ``; + return ""; } } @@ -108,7 +109,8 @@ function failNoty(jqXHR) { closeNoty(); var errorInfo = jqXHR.responseJSON; failedNote = new Noty({ - text: `  ${i18n['common.errorStatus']}: ${jqXHR.status}
${errorInfo.type}
${errorInfo.detail}`, + text: "  " + i18n["common.errorStatus"] + ": " + jqXHR.status + + "
" + errorInfo.type + "
" + errorInfo.detail, type: "error", layout: "bottomRight" }); diff --git a/src/main/webapp/resources/js/topjava.meals.js b/src/main/webapp/resources/js/topjava.meals.js index 73ab323b4..0b3e8e7e8 100644 --- a/src/main/webapp/resources/js/topjava.meals.js +++ b/src/main/webapp/resources/js/topjava.meals.js @@ -10,7 +10,7 @@ const ctx = { data: $("#filter").serialize() }).done(updateTableByData); } -}; +} function clearFilter() { $("#filter")[0].reset(); @@ -21,11 +21,15 @@ function clearFilter() { $.ajaxSetup({ converters: { "text json": function (stringData) { - return JSON.parse(stringData, - function (key, value) { - return (key === 'dateTime') ? value.substring(0, 16).replace('T', ' ') : value; - } - ); + var json = JSON.parse(stringData); + if (typeof json === 'object') { + $(json).each(function () { + if (this.hasOwnProperty('dateTime')) { + this.dateTime = this.dateTime.substr(0, 16).replace('T', ' '); + } + }); + } + return json; } } }); @@ -67,13 +71,10 @@ $(function () { // http://xdsoft.net/jqplugins/datetimepicker/ var startDate = $('#startDate'); var endDate = $('#endDate'); - const dateOptions = { + startDate.datetimepicker({ timepicker: false, format: 'Y-m-d', formatDate: 'Y-m-d', - }; - startDate.datetimepicker({ - ...dateOptions, onShow: function (ct) { this.setOptions({ maxDate: endDate.val() ? endDate.val() : false @@ -81,7 +82,9 @@ $(function () { } }); endDate.datetimepicker({ - ...dateOptions, + timepicker: false, + format: 'Y-m-d', + formatDate: 'Y-m-d', onShow: function (ct) { this.setOptions({ minDate: startDate.val() ? startDate.val() : false diff --git a/src/main/webapp/resources/js/topjava.users.js b/src/main/webapp/resources/js/topjava.users.js index fb4db9efd..bb8863a8f 100644 --- a/src/main/webapp/resources/js/topjava.users.js +++ b/src/main/webapp/resources/js/topjava.users.js @@ -9,12 +9,12 @@ const ctx = { } function enable(chkbox, id) { - const enabled = chkbox.is(":checked"); + var enabled = chkbox.is(":checked"); // https://stackoverflow.com/a/22213543/548473 $.ajax({ url: userAjaxUrl + id, type: "POST", - data: `enabled=${enabled}` + data: "enabled=" + enabled }).done(function () { chkbox.closest("tr").attr("data-user-enabled", enabled); successNoty(enabled ? "common.enabled" : "common.disabled"); @@ -34,7 +34,7 @@ $(function () { "data": "email", "render": function (data, type, row) { if (type === "display") { - return `${data}`; + return "" + data + ""; } return data; } @@ -46,7 +46,7 @@ $(function () { "data": "enabled", "render": function (data, type, row) { if (type === "display") { - return ``; + return ""; } return data; } diff --git a/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java index 43f143cc7..da3bec700 100644 --- a/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java +++ b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java @@ -3,17 +3,17 @@ import org.springframework.lang.NonNull; import org.springframework.test.context.support.DefaultActiveProfilesResolver; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; //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; + List profiles = new ArrayList<>(Arrays.asList(super.resolve(aClass))); + profiles.add(Profiles.getActiveDbProfile()); + return profiles.toArray(String[]::new); } } diff --git a/src/test/java/ru/javawebinar/topjava/SpringMain.java b/src/test/java/ru/javawebinar/topjava/SpringMain.java index a9e46b208..1aa3df136 100644 --- a/src/test/java/ru/javawebinar/topjava/SpringMain.java +++ b/src/test/java/ru/javawebinar/topjava/SpringMain.java @@ -1,7 +1,6 @@ package ru.javawebinar.topjava; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.context.support.GenericXmlApplicationContext; import ru.javawebinar.topjava.model.Role; import ru.javawebinar.topjava.model.User; import ru.javawebinar.topjava.to.MealTo; @@ -20,7 +19,11 @@ public class SpringMain { public static void main(String[] args) { // java 7 automatic resource management (ARM) - try (ConfigurableApplicationContext appCtx = new ClassPathXmlApplicationContext("spring/inmemory.xml")) { + try (GenericXmlApplicationContext appCtx = new GenericXmlApplicationContext()) { + appCtx.getEnvironment().setActiveProfiles(Profiles.getActiveDbProfile(), Profiles.REPOSITORY_IMPLEMENTATION); + appCtx.load("spring/inmemory.xml"); + appCtx.refresh(); + System.out.println("Bean definition names: " + Arrays.toString(appCtx.getBeanDefinitionNames())); AdminRestController adminUserController = appCtx.getBean(AdminRestController.class); adminUserController.create(new User(null, "userName", "email@mail.ru", "password", 2000, Role.ADMIN)); diff --git a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java index b8713e6b9..fb1626c4c 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractServiceTest.java @@ -8,8 +8,8 @@ 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; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static ru.javawebinar.topjava.util.ValidationUtil.getRootCause; @SpringJUnitConfig(locations = { "classpath:spring/spring-app.xml", @@ -21,13 +21,14 @@ @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)); + assertThrows(rootExceptionClass, () -> { + try { + runnable.run(); + } catch (Exception e) { + throw getRootCause(e); + } + }); } } \ 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 index 100d5637e..2566c1c76 100644 --- a/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java +++ b/src/test/java/ru/javawebinar/topjava/service/AbstractUserServiceTest.java @@ -21,7 +21,7 @@ public abstract class AbstractUserServiceTest extends AbstractServiceTest { protected UserService service; @Test - void create() { + public void create() { User created = service.create(getNew()); int newId = created.id(); User newUser = getNew(); diff --git a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java index bdddcc56a..c12f7c53e 100644 --- a/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/AbstractControllerTest.java @@ -33,6 +33,9 @@ public abstract class AbstractControllerTest { private static final CharacterEncodingFilter CHARACTER_ENCODING_FILTER = new CharacterEncodingFilter(); + @Autowired + public Environment env; + static { CHARACTER_ENCODING_FILTER.setEncoding("UTF-8"); CHARACTER_ENCODING_FILTER.setForceEncoding(true); @@ -40,13 +43,10 @@ public abstract class AbstractControllerTest { private MockMvc mockMvc; - @Autowired - private Environment env; - @Autowired private WebApplicationContext webApplicationContext; - protected void assumeDataJpa() { + public void assumeDataJpa() { Assumptions.assumeTrue(env.acceptsProfiles(org.springframework.core.env.Profiles.of(Profiles.DATAJPA)), "DATA-JPA only"); } 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..40af802e0 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerSpringTest.java @@ -21,7 +21,7 @@ class InMemoryAdminRestControllerSpringTest { private InMemoryUserRepository repository; @BeforeEach - void setup() { + public void setUp() { repository.init(); } 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..75b8a5a82 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/InMemoryAdminRestControllerTest.java @@ -30,13 +30,13 @@ static void beforeClass() { @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(); +// 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(); } @BeforeEach - void setup() { + public void setup() { // re-initialize repository.init(); } diff --git a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java index 9547ccff4..ae91e2da9 100644 --- a/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java +++ b/src/test/java/ru/javawebinar/topjava/web/user/ProfileRestControllerTest.java @@ -8,7 +8,7 @@ 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.util.UserUtil; import ru.javawebinar.topjava.web.AbstractControllerTest; import ru.javawebinar.topjava.web.json.JsonUtil; @@ -50,7 +50,7 @@ void delete() throws Exception { @Test void register() throws Exception { UserTo newTo = new UserTo(null, "newName", "newemail@ya.ru", "newPassword", 1500); - User newUser = UsersUtil.createNewFromTo(newTo); + User newUser = UserUtil.createNewFromTo(newTo); ResultActions action = perform(MockMvcRequestBuilders.post(REST_URL) .contentType(MediaType.APPLICATION_JSON) .content(JsonUtil.writeValue(newTo))) @@ -73,7 +73,7 @@ void update() throws Exception { .andDo(print()) .andExpect(status().isNoContent()); - USER_MATCHER.assertMatch(userService.get(USER_ID), UsersUtil.updateFromTo(new User(user), updatedTo)); + USER_MATCHER.assertMatch(userService.get(USER_ID), UserUtil.updateFromTo(new User(user), updatedTo)); } @Test diff --git a/src/test/resources/spring/spring-cache.xml b/src/test/resources/spring/spring-cache.xml index 7c9dfda9a..ea51df903 100644 --- a/src/test/resources/spring/spring-cache.xml +++ b/src/test/resources/spring/spring-cache.xml @@ -10,10 +10,10 @@ http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> - --->