2024-07 spring-17-view updated

This commit is contained in:
stvort
2024-12-03 02:42:53 +04:00
parent e716a4f352
commit 81eb0a9101
12 changed files with 76 additions and 14 deletions
@@ -2,6 +2,7 @@ package ru.otus.spring.controller;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
@@ -9,12 +10,12 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.otus.spring.domain.Person;
import ru.otus.spring.dto.PersonDto;
import ru.otus.spring.repostory.PersonRepository;
import java.util.List;
@Slf4j
@Controller
@RequiredArgsConstructor
public class PersonController {
@@ -23,24 +24,32 @@ public class PersonController {
@GetMapping("/")
public String listPage(Model model) {
List<Person> persons = repository.findAll();
List<PersonDto> persons = repository.findAll().stream()
.map(PersonDto::fromDomainObject).toList();
model.addAttribute("persons", persons);
return "list";
}
@GetMapping("/edit")
public String editPage(@RequestParam("id") long id, Model model) {
Person person = repository.findById(id).orElseThrow(NotFoundException::new);
PersonDto person = repository.findById(id)
.map(PersonDto::fromDomainObject)
.orElseThrow(NotFoundException::new);
model.addAttribute("person", person);
return "edit";
}
@PostMapping("/edit")
public String savePerson(@Valid @ModelAttribute("person") PersonDto person,
BindingResult bindingResult, Model model) {
BindingResult bindingResult,
@RequestParam(value = "hobby", defaultValue = "") List<String> hobby) {
if (bindingResult.hasErrors()) {
return "edit";
}
log.debug("Hobby from plain RequestParam: {}", String.join(", ", hobby));
log.debug("Hobby from DTO: {}", person.hobbyAsString());
repository.save(person.toDomainObject());
return "redirect:/";
}
@@ -1,5 +1,7 @@
package ru.otus.spring.domain;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.FetchType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -9,6 +11,8 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@@ -19,4 +23,7 @@ public class Person {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@ElementCollection(fetch = FetchType.EAGER)
private List<String> hobby;
}
@@ -1,11 +1,14 @@
package ru.otus.spring.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import ru.otus.spring.domain.Person;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.util.List;
import static org.springframework.util.CollectionUtils.isEmpty;
@Data
@AllArgsConstructor
@@ -16,12 +19,20 @@ public class PersonDto {
@NotBlank(message = "{name-field-should-not-be-blank}")
@Size(min = 2, max = 10, message = "{name-field-should-has-expected-size}")
private String name;
private List<String> hobby;
public String hobbyAsString() {
if (isEmpty(hobby)){
return "";
}
return String.join(", ", hobby);
}
public Person toDomainObject(){
return new Person(id, name);
return new Person(id, name, hobby);
}
public static PersonDto fromDomainObject(Person person) {
return new PersonDto(person.getId(), person.getName());
return new PersonDto(person.getId(), person.getName(), person.getHobby());
}
}
@@ -19,4 +19,5 @@ spring:
logging:
level:
ROOT: ERROR
ROOT: ERROR
ru.otus.spring.controller: DEBUG
@@ -1,2 +1,10 @@
insert into person (id, name) values (1, 'Pushkin');
insert into person (id, name) values (2, 'Lermontov');
insert into person (id, name) values (2, 'Lermontov');
insert into person_hobby (person_id, hobby)
values
(1, 'Fishing'),
(1, 'Poetry'),
(2, 'Traveling'),
(2, 'Poetry')
;
@@ -5,6 +5,7 @@ persons-table-header=Persons:
persons-table-column-action=Action
person-field-id=ID
person-field-name=Name
person-field-hobby=Hobby
edit-button-caption=Edit
person-form-header=Person Info:
save-button-caption=Save
@@ -5,6 +5,7 @@ persons-table-header=Persons:
persons-table-column-action=Action
person-field-column-id=ID
person-field-name=Name
person-field-hobby=Hobby
edit-button-caption=Edit
person-form-header=Person Info:
save-button-caption=Save
@@ -5,6 +5,7 @@ persons-table-header=\u041F\u0451\u0440\u0441\u043E\u043D\u044B:
persons-table-column-action=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435
person-field-id=\u0410\u0439\u0414\u0438
person-field-name=\u0418\u043C\u044F
person-field-hobby=\u0425\u043E\u0431\u0431\u0438
edit-button-caption=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C
person-form-header=\u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E \u043F\u0451\u0440\u0441\u043E\u043D\u0435:
save-button-caption=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C
@@ -1,4 +1,11 @@
create table person (
id integer generated by default as identity,
name varchar(255), primary key (id)
name varchar(255),
hobby varchar(500),
primary key (id)
);
create table person_hobby (
person_id integer references person(id),
hobby varchar(255)
);
@@ -59,6 +59,15 @@
<div class="errors" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Wrong person name error</div>
</div>
<div class="row">
<label for="person-hobby-select" th:text="#{person-field-hobby} + ':'">Hobby:</label>
<select id="person-hobby-select" name="hobby" multiple>
<option value="Fishing" th:selected="*{hobby.contains('Fishing')}">Fishing</option>
<option value="Poetry" th:selected="*{hobby.contains('Poetry')}">Poetry</option>
<option value="Traveling" th:selected="*{hobby.contains('Traveling')}">Traveling</option>
</select>
</div>
<div class="row">
<button type="submit" th:text="#{save-button-caption}">Save</button>
<a href="list.html" th:href="@{/}"><button type="button" th:text="#{cancel-button-caption}">Cancel</button></a>
@@ -57,6 +57,7 @@
<tr>
<th th:text="#{person-field-id}">ID</th>
<th th:text="#{person-field-name}">Name</th>
<th th:text="#{person-field-hobby}">Hobby</th>
<th th:text="#{persons-table-column-action}">Action</th>
</tr>
</thead>
@@ -64,6 +65,7 @@
<tr th:each="person : ${persons}">
<td th:text="${person.id}">1</td>
<td th:text="${person.name}">John Doe</td>
<td th:text="${person.hobbyAsString()}">Hobby1, hobby2</td>
<td>
<a th:href="@{/edit(id=${person.id})}" href="edit.html" th:text="#{edit-button-caption}">Edit</a>
</td>
@@ -6,6 +6,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import ru.otus.spring.domain.Person;
import ru.otus.spring.dto.PersonDto;
import ru.otus.spring.repostory.PersonRepository;
import java.util.List;
@@ -29,22 +30,26 @@ class PersonControllerTest {
@MockBean
private PersonRepository personRepository;
private List<Person> persons = List.of(new Person(1L, "Vasya"), new Person(2L, "Dima"));
private List<Person> persons = List.of(new Person(1L, "Vasya", List.of()),
new Person(2L, "Dima", List.of()));
@Test
void shouldRenderListPageWithCorrectViewAndModelAttributes() throws Exception {
when(personRepository.findAll()).thenReturn(persons);
List<PersonDto> expectedPersons = persons.stream()
.map(PersonDto::fromDomainObject).toList();
mvc.perform(get("/"))
.andExpect(view().name("list"))
.andExpect(model().attribute("persons", persons));
.andExpect(model().attribute("persons", expectedPersons));
}
@Test
void shouldRenderEditPageWithCorrectViewAndModelAttributes() throws Exception {
when(personRepository.findById(1L)).thenReturn(Optional.of(persons.get(0)));
PersonDto expectedPerson = PersonDto.fromDomainObject(persons.get(0));
mvc.perform(get("/edit").param("id", "1"))
.andExpect(view().name("edit"))
.andExpect(model().attribute("person", persons.get(0)));
.andExpect(model().attribute("person", expectedPerson));
}
@Test