Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion config/curl.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
#### get Users 100001
`curl -s http://localhost:8080/topjava/rest/admin/users/100001 --user [email protected]:admin`

#### register User
`curl -s -i -X POST -d '{"name":"New User","email":"[email protected]","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 [email protected]:test-password`

#### get All Meals
`curl -s http://localhost:8080/topjava/rest/profile/meals --user [email protected]:password`

Expand All @@ -26,4 +32,7 @@
`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 [email protected]: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 [email protected]:password`
`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 [email protected]:password`

#### validate with Error
`curl -s -X POST -d '{}' -H 'Content-Type: application/json' http://localhost:8080/topjava/rest/admin/users --user [email protected]:admin`
5 changes: 5 additions & 0 deletions config/messages/app.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Meals could be filtered by date and time. Meal record color depends on daily cal
All REST interface covered with JUnit tests by Spring MVC Test and Spring Security Test.
app.footer=<a href="https://github.com/JavaOPs/topjava" target=_blank>Spring 5/JPA Enterprise (Topjava)</a> 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
Expand All @@ -16,6 +19,7 @@ user.roles=Roles
user.active=Active
user.registered=Registered
user.password=Password
user.caloriesPerDay=Daily calorie limit

meal.title=Meals
meal.edit=Edit meal
Expand All @@ -36,6 +40,7 @@ common.saved=Record saved
common.enabled=Record enabled
common.disabled=Record disabled
common.errorStatus=Error status
common.appError=Application error
common.confirm=Are you sure?
common.save=Save
common.cancel=Cancel
5 changes: 5 additions & 0 deletions config/messages/app_ru.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ app.description=<a href="https://github.com/JavaOPs/topjava">Java Enterprise п
Весь REST интерфейс покрывается JUnit тестами, используя Spring MVC Test и Spring Security Test.
app.footer=Приложение стажировки <a href="https://github.com/JavaOPs/topjava" target=_blank>Spring 5/JPA Enterprise (Topjava)</a>
app.login=Зайти как
app.profile=профиль
app.register=Регистрация
app.registered=Вы зарегистрированы. Введите ваш логин/пароль.

user.title=Пользователи
user.edit=Редактировать пользователя
Expand All @@ -16,6 +19,7 @@ user.roles=Роли
user.active=Активный
user.registered=Зарегистрирован
user.password=Пароль
user.caloriesPerDay=Норма калорий в день

meal.title=Моя еда
meal.edit=Редактировать еду
Expand All @@ -36,6 +40,7 @@ common.saved=Запись сохранена
common.enabled=Запись активирована
common.disabled=Запись деактивирована
common.errorStatus=Статус ошибки
common.appError=Ошибка приложения
common.confirm=Вы уверены?
common.save=Сохранить
common.cancel=Отменить
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.security.version}</version>
</dependency>

<!--- ORM -->
<dependency>
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/ru/javawebinar/topjava/AuthorizedUser.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ 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());
this.userTo = UsersUtil.asTo(user);
setTo(UsersUtil.asTo(user));
}

public int getId() {
return userTo.id();
}

public void update(UserTo newTo) {
public void setTo(UserTo newTo) {
newTo.setPassword(null);
userTo = newTo;
}

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/ru/javawebinar/topjava/View.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.javawebinar.topjava;

import javax.validation.groups.Default;

public class View {
public interface Persist extends Default {}
}
37 changes: 30 additions & 7 deletions src/main/java/ru/javawebinar/topjava/model/Meal.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
package ru.javawebinar.topjava.model;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonView;
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.View.ValidatedUI;
import ru.javawebinar.topjava.util.DateTimeUtil;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.groups.Default;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
Expand All @@ -31,23 +39,25 @@ public class Meal extends AbstractBaseEntity {
public static final String GET_BETWEEN = "Meal.getBetween";

@Column(name = "date_time", nullable = false)
@NotNull
@NotNull(groups = {ValidatedUI.class, Default.class})
@JsonView(View.JsonREST.class)
private LocalDateTime dateTime;

@Column(name = "description", nullable = false)
@NotBlank
@Size(min = 2, max = 120)
@NotBlank(groups = {ValidatedUI.class, Default.class})
@Size(min = 2, max = 120, groups = {ValidatedUI.class, Default.class})
private String description;

@Column(name = "calories", nullable = false)
@Range(min = 10, max = 5000)
private int calories;
@NotNull(groups = {ValidatedUI.class, Default.class})
@Range(min = 10, max = 5000, groups = {ValidatedUI.class, Default.class})
private Integer calories;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
@JsonBackReference
// @NotNull
@NotNull
private User user;

public Meal() {
Expand Down Expand Up @@ -88,7 +98,7 @@ public void setDescription(String description) {
this.description = description;
}

public void setCalories(int calories) {
public void setCalories(Integer calories) {
this.calories = calories;
}

Expand All @@ -100,6 +110,19 @@ public void setUser(User user) {
this.user = user;
}


@JsonGetter
@JsonView(View.JsonUI.class)
@JsonFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN)
public LocalDateTime getDateTimeUI() {
return dateTime;
}

@DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN)
public void setDateTimeUI(LocalDateTime dateTime) {
this.dateTime = dateTime;
}

@Override
public String toString() {
return "Meal{" +
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/ru/javawebinar/topjava/model/User.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ru.javawebinar.topjava.model;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.*;
import org.hibernate.validator.constraints.Range;
Expand Down Expand Up @@ -43,13 +44,16 @@ public class User extends AbstractNamedEntity {
@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")
private boolean enabled = true;

@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)
Expand Down Expand Up @@ -81,8 +85,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, Role... roles) {
this(id, name, email, password, DEFAULT_CALORIES_PER_DAY, true, new Date(), List.of(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, int caloriesPerDay, boolean enabled, Date registered, Collection<Role> roles) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import ru.javawebinar.topjava.View;
import ru.javawebinar.topjava.model.Meal;
import ru.javawebinar.topjava.repository.MealRepository;
import ru.javawebinar.topjava.util.ValidationUtil;
Expand Down Expand Up @@ -40,7 +41,7 @@ public JdbcMealRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate
@Override
@Transactional
public Meal save(Meal meal, int userId) {
ValidationUtil.validate(meal);
ValidationUtil.validate(meal, View.ValidatedUI.class);

MapSqlParameterSource map = new MapSqlParameterSource()
.addValue("id", meal.getId())
Expand Down
17 changes: 12 additions & 5 deletions src/main/java/ru/javawebinar/topjava/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.context.annotation.ScopedProxyMode;
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;
Expand All @@ -17,6 +18,7 @@

import java.util.List;

import static ru.javawebinar.topjava.util.UsersUtil.prepareToSave;
import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFound;
import static ru.javawebinar.topjava.util.ValidationUtil.checkNotFoundWithId;

Expand All @@ -25,15 +27,17 @@
public class UserService implements UserDetailsService {

private final UserRepository repository;
private final PasswordEncoder passwordEncoder;

public UserService(UserRepository repository) {
public UserService(UserRepository repository, PasswordEncoder passwordEncoder) {
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 repository.save(user);
return prepareAndSave(user);
}

@CacheEvict(value = "users", allEntries = true)
Expand All @@ -59,15 +63,14 @@ public List<User> getAll() {
public void update(User user) {
Assert.notNull(user, "user must not be null");
// checkNotFoundWithId : check works only for JDBC, disabled
repository.save(user);
prepareAndSave(user);
}

@CacheEvict(value = "users", allEntries = true)
@Transactional
public void update(UserTo userTo) {
User user = get(userTo.id());
User updatedUser = UsersUtil.updateFromTo(user, userTo);
repository.save(updatedUser); // !! need only for JDBC implementation
prepareAndSave(UsersUtil.updateFromTo(user, userTo));
}

@CacheEvict(value = "users", allEntries = true)
Expand All @@ -87,6 +90,10 @@ public AuthorizedUser loadUserByUsername(String email) throws UsernameNotFoundEx
return new AuthorizedUser(user);
}

private User prepareAndSave(User user) {
return repository.save(prepareToSave(user, passwordEncoder));
}

public User getWithMeals(int id) {
return checkNotFoundWithId(repository.getWithMeals(id), id);
}
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/ru/javawebinar/topjava/to/MealTo.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package ru.javawebinar.topjava.to;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonView;
import ru.javawebinar.topjava.View;
import ru.javawebinar.topjava.util.DateTimeUtil;

import java.beans.ConstructorProperties;
import java.time.LocalDateTime;
import java.util.Objects;

public class MealTo extends BaseTo {

@JsonView(View.JsonREST.class)
private final LocalDateTime dateTime;

private final String description;
Expand Down Expand Up @@ -56,6 +63,13 @@ public int hashCode() {
return Objects.hash(id, dateTime, description, calories, excess);
}

@JsonGetter
@JsonView(View.JsonUI.class)
@JsonFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN)
public LocalDateTime getDateTimeUI() {
return dateTime;
}

@Override
public String toString() {
return "MealTo{" +
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/ru/javawebinar/topjava/to/UserTo.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public void setEmail(String email) {
this.email = email;
}

public void setCaloriesPerDay(Integer caloriesPerDay) {
this.caloriesPerDay = caloriesPerDay;
}

public Integer getCaloriesPerDay() {
return caloriesPerDay;
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/ru/javawebinar/topjava/util/DateTimeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import java.time.format.DateTimeFormatter;

public class DateTimeUtil {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm";
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);

// DB doesn't support LocalDate.MIN/MAX
private static final LocalDateTime MIN_DATE = LocalDateTime.of(1, 1, 1, 0, 0);
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/ru/javawebinar/topjava/util/UsersUtil.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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;
Expand All @@ -9,7 +10,7 @@ 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(), Role.USER);
return new User(null, userTo.getName(), userTo.getEmail().toLowerCase(), userTo.getPassword(), userTo.getCaloriesPerDay(), Role.USER);
}

public static UserTo asTo(User user) {
Expand All @@ -23,4 +24,10 @@ public static User updateFromTo(User user, UserTo userTo) {
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;
}
}
Loading