Add staff form and bind it to model

Closes #51
This commit is contained in:
Denis Natusch 2023-11-28 09:48:06 +01:00
parent a46d2ad169
commit 0119b1cfa0
No known key found for this signature in database
GPG key ID: 5E57BD8EDACFA985
8 changed files with 169 additions and 38 deletions

View file

@ -300,6 +300,7 @@ image:models/design/staff.svg[class design diagram - Staff]
|StaffController |A Spring MVC Controller for handling staff-related operations such as adding, removing, and updating staff data. It interacts with StaffManagement.
|StaffManagement |A class that manages interactions and logic with StaffRepository. It provides methods to find, save/add, list, and delete staff members.
|StaffRepository |An extension of 'CrudRepository' that provides standard methods to perform CRUD operations on staff objects.
|StaffForm |A Form to cache a user input that was made during registration or updating an Employee.
|===
=== Link between analysis and design

View file

@ -6,6 +6,8 @@ skinparam groupInheritance 2
package Spring {
class CrudRepository
class Streamable
class Errors
class Model
}
package catering.staff {
@ -32,13 +34,18 @@ package catering.staff {
class StaffController {
+ StaffController(staffRepository: StaffRepository)
+ getStaff(model: Model): String
+ getStaff(model: Model, form: StaffForm): String
+ removeEmployee(employee: Employee, model: Model): String
+ addEmployee(name: String, job: JobType): String
+ addEmployee(form:StaffForm, result:Errors, model: Model): String
+ editEmployee(employee: Employee, model Model): String
+ updateEmployee(employee: Employee, name: String, job: JobType): String
+ editEmployee(model Model, form:StaffForm): String
+ updateEmployee(employee: Employee, form:StaffForm): String
}
StaffController --> StaffManagement : -staffManagement
StaffController ..> Employee
StaffController ..> StaffForm
StaffController ..> Model
StaffController ..> Errors
class StaffManagement {
+ StaffManagement(staffRepository: StaffRepository)
@ -56,6 +63,16 @@ package catering.staff {
StaffRepository ..|> Streamable
StaffRepository o-- Employee
class StaffForm {
- name: String
+ StaffForm(): void
+ getName(): String
+ getJob(): JobType
+ setName(name:String): void
+ setJob(job:JobType): void
+ validate(e:Errors): void
}
StaffForm ..> JobType : -job
}
@enduml

BIN
src/main/asciidoc/models/design/staff.svg (Stored with Git LFS)

Binary file not shown.

View file

@ -3,11 +3,15 @@ package catering.staff;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import jakarta.validation.Valid;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
@Controller
public class StaffController {
@ -21,10 +25,26 @@ public class StaffController {
@GetMapping("/staff")
@PreAuthorize("hasRole('ADMIN')")
public String getStaff(Model model) {
return getStaff(model, new StaffForm());
}
public String getStaff(Model model, @Valid StaffForm form) {
model.addAttribute("staff", staffManagement.findAll());
model.addAttribute("form", form);
return "staff";
}
@PostMapping("/staff/add")
@PreAuthorize("hasRole('ADMIN')")
public String addEmployee(@Valid @ModelAttribute("form") StaffForm form, Errors result, Model model) {
form.validate(result);
if (result.hasErrors()) {
return getStaff(model, form);
}
staffManagement.save(new Employee(form.getName(), form.getJob()));
return "redirect:/staff";
}
@PostMapping("/staff/remove")
@PreAuthorize("hasRole('ADMIN')")
public String removeEmployee(@RequestParam("id") Employee employee, Model model) {
@ -32,25 +52,30 @@ public class StaffController {
return "redirect:/staff";
}
@PostMapping("/staff/add")
@PreAuthorize("hasRole('ADMIN')")
public String addEmployee(@RequestParam String name, @RequestParam JobType job) {
staffManagement.save(new Employee(name, job));
return "redirect:/staff";
}
@GetMapping("/staff/edit/{id}")
@PreAuthorize("hasRole('ADMIN')")
public String editEmployee(@PathVariable("id") Employee employee, Model model) {
model.addAttribute("employee", employee);
StaffForm form = new StaffForm();
form.setJob(employee.getJob());
form.setName(employee.getName());
return editEmployee(model, form);
}
public String editEmployee(Model model, @Valid StaffForm form) {
model.addAttribute("form", form);
return "edit-staff";
}
@PostMapping("/staff/edit/{id}")
@PreAuthorize("hasRole('ADMIN')")
public String updateEmployee(@PathVariable("id") Employee employee, @RequestParam String name, @RequestParam JobType job) {
employee.setJob(job);
employee.setName(name);
public String updateEmployee(@PathVariable("id") Employee employee, @Valid @ModelAttribute("form") StaffForm form,
Errors result, Model model) {
form.validate(result);
if (result.hasErrors()) {
return editEmployee(model, form);
}
employee.setJob(form.getJob());
employee.setName(form.getName());
staffManagement.save(employee);
return "redirect:/staff";
}

View file

@ -0,0 +1,38 @@
package catering.staff;
import org.springframework.validation.Errors;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
public class StaffForm {
private @NotEmpty String name;
private @NotNull JobType job;
public StaffForm() {
}
public String getName() {
return name;
}
public JobType getJob() {
return job;
}
public void setName(String name) {
this.name = name;
}
public void setJob(JobType job) {
this.job = job;
}
public void validate(Errors e) {
if (job == null) {
e.rejectValue("job", "job is not a job type");
}
}
}

View file

@ -7,17 +7,18 @@
</head>
<body>
<div layout:fragment="content">
<form th:object="${employee}" method="post">
<input type="hidden" th:field="*{id}" />
<form th:object="${form}" method="post">
<div class="mb-3">
<label class="form-label" for="name">Name:</label>
<input class="form-control" type="text" th:field="*{name}" required />
<input class="form-control" type="text" th:field="*{name}" th:errorclass="is-invalid" required/>
<div th:if="${#fields.hasErrors('name')}" class="invalid-feedback">Ungültiger Name</div>
</div>
<div class="mb-3">
<label class="form-label" for="job">Beruf:</label>
<select name="job" class="form-select">
<option th:each="type : ${T(catering.staff.JobType).values()}" th:value="${type}" th:selected="${type.name() == employee.job.name()}" th:text="${type}" required></option>
<select th:field="*{job}" class="form-select" th:errorclass="is-invalid">
<option th:each="type : ${T(catering.staff.JobType).values()}" th:value="${type}" th:text="${type}" required></option>
</select>
<div th:if="${#fields.hasErrors('job')}" class="invalid-feedback">Ungültiger Job</div>
</div>
<button class="btn btn-primary" type="submit">Speichern</button>
<a th:href="@{/staff}"><button type="button" class="btn btn-danger">Abbrechen</button></a>

View file

@ -37,16 +37,18 @@
</div>
<div>
<h2>Personal Hinzufügen</h2>
<form action="/staff/add" method="post">
<form th:object="${form}" th:action="@{/staff/add}" method="post">
<div class="mb-3">
<label class="form-label" for="name">Name</label>
<input class="form-control" type="text" name="name" required />
<input class="form-control" type="text" th:field="*{name}" th:errorclass="is-invalid" required>
<div th:if="${#fields.hasErrors('name')}" class="invalid-feedback">Ungültiger Name</div>
</div>
<div class="mb-3">
<label class="form-label" for="job">Beruf:</label>
<select name="job" class="form-select">
<select th:field="*{job}" class="form-select" th:errorclass="is-invalid">
<option th:each="type : ${T(catering.staff.JobType).values()}" th:value="${type}" th:text="${type}" required></option>
</select>
<div th:if="${#fields.hasErrors('job')}" class="invalid-feedback">Ungültiger Job</div>
</div>
<button class="btn btn-primary" type="submit">Hinzufügen</button>
</form>

View file

@ -29,8 +29,8 @@ class StaffControllerIntegrationTests {
MockMvc mvc;
MockHttpServletRequestBuilder createStaff = post("/staff/add")
.param("name", "Karl Baum")
.param("job", "COOK");
.param("name", "Karl Baum")
.param("job", "COOK");
@Autowired
private StaffManagement staffManagement;
@ -55,16 +55,16 @@ class StaffControllerIntegrationTests {
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
void viewStaff() throws Exception {
mvc.perform(get("/staff"))
.andExpect(status().isOk())
.andExpect(content().string(containsString(defaultEmployee.getName())));
.andExpect(status().isOk())
.andExpect(content().string(containsString(defaultEmployee.getName())));
}
@Test
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
void viewStaffEditPage() throws Exception {
mvc.perform(get("/staff/edit/" + defaultEmployeeId.toString()))
.andExpect(status().isOk())
.andExpect(content().string(containsString(defaultEmployee.getName())));
.andExpect(status().isOk())
.andExpect(content().string(containsString(defaultEmployee.getName())));
}
@Test
@ -83,12 +83,12 @@ class StaffControllerIntegrationTests {
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
void modifyStaff() throws Exception {
mvc.perform(post("/staff/edit/" + defaultEmployeeId.toString())
.param("name", "Dieter Bäume")
.param("job", "SERVICE")).andExpect(redirectedUrl("/staff"))
.andExpect(redirectedUrl("/staff"));
.param("name", "Dieter Bäume")
.param("job", "SERVICE")).andExpect(redirectedUrl("/staff"))
.andExpect(redirectedUrl("/staff"));
assertThat(staffManagement.findById(defaultEmployeeId).get())
.extracting("name", "job")
.containsExactly("Dieter Bäume", JobType.SERVICE);
.extracting("name", "job")
.containsExactly("Dieter Bäume", JobType.SERVICE);
}
@Test
@ -103,6 +103,53 @@ class StaffControllerIntegrationTests {
.doesNotContain("Dieter Baum");
}
@Test
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
void addStaffWrong() throws Exception {
mvc.perform(post("/staff/add"))
.andExpect(content().string(containsString("Ungültiger Name")))
.andExpect(content().string(containsString("Ungültiger Job")));
}
@Test
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
void editStaffWrong() throws Exception {
mvc.perform(post("/staff/edit/" + defaultEmployeeId.toString()))
.andExpect(content().string(containsString("Ungültiger Name")))
.andExpect(content().string(containsString("Ungültiger Job")));
assertThat(staffManagement.findById(defaultEmployeeId).get())
.extracting("name", "job")
.containsExactly("Dieter Baum", JobType.COOK);
}
@Test
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
void addStaffWithWrongJobType() throws Exception {
assertThat(staffManagement.findAll().stream())
.extracting("name")
.doesNotContain("Paul Kunst");
mvc.perform(post("/staff/add")
.param("name", "Paul Kunst")
.param("job", "notAJob"))
.andExpect(content().string(containsString("Ungültiger Job")));
assertThat(staffManagement.findAll().stream())
.extracting("name")
.doesNotContain("Paul Kunst");
}
@Test
@WithMockUser(username = "admin", password = "admin", roles = "ADMIN")
void editStaffWithWrongJobType() throws Exception {
mvc.perform(
post("/staff/edit/" + defaultEmployeeId.toString())
.param("name", defaultEmployee.getName())
.param("job", "notAJob"))
.andExpect(content().string(containsString("Ungültiger Job")));
assertThat(staffManagement.findById(defaultEmployeeId).get())
.extracting("name", "job")
.containsExactly("Dieter Baum", JobType.COOK);
}
@Test
@WithMockUser(username = "dieter", password = "123", roles = "CUSTOMER")
void refuseCustomer() throws Exception {
@ -114,10 +161,10 @@ class StaffControllerIntegrationTests {
@WithAnonymousUser
void refuseAnonymous() throws Exception {
mvc.perform(get("/staff"))
.andExpect(status().is3xxRedirection())
.andExpect(header().string(HttpHeaders.LOCATION, endsWith("/login")));
.andExpect(status().is3xxRedirection())
.andExpect(header().string(HttpHeaders.LOCATION, endsWith("/login")));
mvc.perform(get("/staff/edit/" + defaultEmployeeId))
.andExpect(status().is3xxRedirection())
.andExpect(header().string(HttpHeaders.LOCATION, endsWith("/login")));
.andExpect(status().is3xxRedirection())
.andExpect(header().string(HttpHeaders.LOCATION, endsWith("/login")));
}
}