From 8230337839677c146bd53a6ce14d4378d53368e6 Mon Sep 17 00:00:00 2001 From: Jan Klattenhoff Date: Wed, 2 Oct 2024 10:09:07 +0000 Subject: [PATCH 1/8] refactor: remove unused imports and clean up code (!30) Reviewed-on: https://git.kjan.de/jank/LF8/pulls/30 Reviewed-by: Phan Huy Tran Co-authored-by: Jan Klattenhoff Co-committed-by: Jan Klattenhoff --- .../java/de/szut/lf8_starter/hello/HelloController.java | 3 --- .../de/szut/lf8_starter/project/dto/GetProjectDto.java | 2 -- .../szut/lf8_starter/security/KeycloakSecurityConfig.java | 7 ------- .../de/szut/lf8_starter/Lf8StarterApplicationTests.java | 1 - .../project/ProjectFindAllNotAuthenticated.java | 5 ----- ...ProjectActionTest.java => RemoveProjectActionTest.java} | 0 6 files changed, 18 deletions(-) rename src/test/java/de/szut/lf8_starter/integration/project/{DeleteProjectActionTest.java => RemoveProjectActionTest.java} (100%) diff --git a/src/main/java/de/szut/lf8_starter/hello/HelloController.java b/src/main/java/de/szut/lf8_starter/hello/HelloController.java index 3a4a993..f6baab4 100644 --- a/src/main/java/de/szut/lf8_starter/hello/HelloController.java +++ b/src/main/java/de/szut/lf8_starter/hello/HelloController.java @@ -14,9 +14,6 @@ import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import java.util.List; -import java.util.stream.Collectors; - @RestController @RequestMapping(value = "hello") @PreAuthorize("hasAnyAuthority('user')") diff --git a/src/main/java/de/szut/lf8_starter/project/dto/GetProjectDto.java b/src/main/java/de/szut/lf8_starter/project/dto/GetProjectDto.java index 614ece1..90ebe5e 100644 --- a/src/main/java/de/szut/lf8_starter/project/dto/GetProjectDto.java +++ b/src/main/java/de/szut/lf8_starter/project/dto/GetProjectDto.java @@ -3,8 +3,6 @@ package de.szut.lf8_starter.project.dto; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/de/szut/lf8_starter/security/KeycloakSecurityConfig.java b/src/main/java/de/szut/lf8_starter/security/KeycloakSecurityConfig.java index cd03823..99fbc5d 100644 --- a/src/main/java/de/szut/lf8_starter/security/KeycloakSecurityConfig.java +++ b/src/main/java/de/szut/lf8_starter/security/KeycloakSecurityConfig.java @@ -1,24 +1,17 @@ package de.szut.lf8_starter.security; import java.util.*; -import java.util.stream.Collectors; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.http.HttpMethod; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; -import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; -import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; diff --git a/src/test/java/de/szut/lf8_starter/Lf8StarterApplicationTests.java b/src/test/java/de/szut/lf8_starter/Lf8StarterApplicationTests.java index b5c7f7b..942f2cb 100644 --- a/src/test/java/de/szut/lf8_starter/Lf8StarterApplicationTests.java +++ b/src/test/java/de/szut/lf8_starter/Lf8StarterApplicationTests.java @@ -2,7 +2,6 @@ package de.szut.lf8_starter; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.TestPropertySource; @SpringBootTest class Lf8StarterApplicationTests { diff --git a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java index 8c62848..307ad4d 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java @@ -1,6 +1,5 @@ package de.szut.lf8_starter.integration.project; -import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -9,12 +8,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.test.web.servlet.MockMvc; -import java.time.LocalDate; -import java.util.List; -import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) diff --git a/src/test/java/de/szut/lf8_starter/integration/project/DeleteProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java similarity index 100% rename from src/test/java/de/szut/lf8_starter/integration/project/DeleteProjectActionTest.java rename to src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java From 1e38efaa63f8b640c0b7ee99b8d5d07ab0bc5866 Mon Sep 17 00:00:00 2001 From: Jan Klattenhoff Date: Wed, 2 Oct 2024 10:29:39 +0000 Subject: [PATCH 2/8] refactor(test): remove access modifiers from test classes (!31) Reviewed-on: https://git.kjan.de/jank/LF8/pulls/31 Reviewed-by: Phan Huy Tran Co-authored-by: Jan Klattenhoff Co-committed-by: Jan Klattenhoff --- .../integration/project/CreateProjectActionTest.java | 2 +- .../integration/project/ProjectFindAllNotAuthenticated.java | 2 +- .../integration/project/ProjectFindAllSuccessTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java index 36eee54..af10c64 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java @@ -20,7 +20,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) -public class CreateProjectActionTest { +class CreateProjectActionTest { @Autowired private MockMvc mockMvc; @Autowired diff --git a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java index 307ad4d..bda3c51 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java @@ -14,7 +14,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc(addFilters = true) -public class ProjectFindAllNotAuthenticated { +class ProjectFindAllNotAuthenticated { @Autowired private ProjectRepository projectRepository; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java index 8f1974a..3fa8bd8 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java @@ -18,7 +18,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc(addFilters = false) -public class ProjectFindAllSuccessTest { +class ProjectFindAllSuccessTest { @Autowired private ProjectRepository projectRepository; From 1008278fde124a5f4e345261f2b013be2414e96f Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 23 Oct 2024 08:39:25 +0000 Subject: [PATCH 3/8] feat: Implement add employee to project route (SCRUM-2) (!32) Co-authored-by: Phan Huy Tran Reviewed-on: https://git.kjan.de/jank/LF8/pulls/32 Reviewed-by: Jan Gleytenhoover Co-authored-by: Phan Huy Tran Co-committed-by: Phan Huy Tran --- requests/employee/createEmployee.http | 12 +++ requests/employee/deleteEmployee.http | 2 + requests/employee/getAllEmployees.http | 2 + requests/employee/getEmployee.http | 2 + requests/project/addEmployeeToProject.http | 2 + requests/{ => project}/createProject.http | 0 requests/{ => project}/getAllProjects.http | 0 requests/{ => project}/getProject.http | 0 requests/{ => project}/updateProject.http | 0 .../lf8_starter/employee/EmployeeService.java | 34 ++++++++ .../lf8_starter/project/ProjectEntity.java | 2 +- .../lf8_starter/project/ProjectService.java | 4 +- .../{ => crud}/CreateProjectAction.java | 2 +- .../{ => crud}/GetAllProjectsAction.java | 2 +- .../action/{ => crud}/GetProjectAction.java | 7 +- .../{ => crud}/UpdateProjectAction.java | 2 +- .../employee/AddEmployeeToProjectAction.java | 55 +++++++++++++ .../lf8_starter/project/dto/EmployeeDto.java | 22 ++++++ .../project/AddEmployeeToProjectTest.java | 77 +++++++++++++++++++ .../project/CreateProjectActionTest.java | 28 +++---- .../project/GetProjectActionTest.java | 4 +- .../project/ProjectFindAllSuccessTest.java | 4 +- .../project/RemoveProjectActionTest.java | 2 +- .../project/UpdateProjectActionTest.java | 15 ++-- 24 files changed, 246 insertions(+), 34 deletions(-) create mode 100644 requests/employee/createEmployee.http create mode 100644 requests/employee/deleteEmployee.http create mode 100644 requests/employee/getAllEmployees.http create mode 100644 requests/employee/getEmployee.http create mode 100644 requests/project/addEmployeeToProject.http rename requests/{ => project}/createProject.http (100%) rename requests/{ => project}/getAllProjects.http (100%) rename requests/{ => project}/getProject.http (100%) rename requests/{ => project}/updateProject.http (100%) create mode 100644 src/main/java/de/szut/lf8_starter/employee/EmployeeService.java rename src/main/java/de/szut/lf8_starter/project/action/{ => crud}/CreateProjectAction.java (97%) rename src/main/java/de/szut/lf8_starter/project/action/{ => crud}/GetAllProjectsAction.java (97%) rename src/main/java/de/szut/lf8_starter/project/action/{ => crud}/GetProjectAction.java (83%) rename src/main/java/de/szut/lf8_starter/project/action/{ => crud}/UpdateProjectAction.java (95%) create mode 100644 src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java create mode 100644 src/main/java/de/szut/lf8_starter/project/dto/EmployeeDto.java create mode 100644 src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java diff --git a/requests/employee/createEmployee.http b/requests/employee/createEmployee.http new file mode 100644 index 0000000..a170fe2 --- /dev/null +++ b/requests/employee/createEmployee.http @@ -0,0 +1,12 @@ +POST https://employee.szut.dev/employees +Authorization: Bearer {{auth_token}} +Content-Type: application/json + +{ + "firstName": "Jan", + "lastName": "Klattenhoff", + "street": "Pirolweg 17", + "postcode": "27777", + "city": "Gandakersee", + "phone": "017684984816" +} \ No newline at end of file diff --git a/requests/employee/deleteEmployee.http b/requests/employee/deleteEmployee.http new file mode 100644 index 0000000..cf1d827 --- /dev/null +++ b/requests/employee/deleteEmployee.http @@ -0,0 +1,2 @@ +DELETE https://employee.szut.dev/employees/310 +Authorization: Bearer {{auth_token}} \ No newline at end of file diff --git a/requests/employee/getAllEmployees.http b/requests/employee/getAllEmployees.http new file mode 100644 index 0000000..c3911be --- /dev/null +++ b/requests/employee/getAllEmployees.http @@ -0,0 +1,2 @@ +GET https://employee.szut.dev/employees +Authorization: Bearer {{auth_token}} \ No newline at end of file diff --git a/requests/employee/getEmployee.http b/requests/employee/getEmployee.http new file mode 100644 index 0000000..eddf4d9 --- /dev/null +++ b/requests/employee/getEmployee.http @@ -0,0 +1,2 @@ +GET https://employee.szut.dev/employees/312 +Authorization: Bearer {{auth_token}} \ No newline at end of file diff --git a/requests/project/addEmployeeToProject.http b/requests/project/addEmployeeToProject.http new file mode 100644 index 0000000..981839a --- /dev/null +++ b/requests/project/addEmployeeToProject.http @@ -0,0 +1,2 @@ +POST http://localhost:8080/projects/1/employees/312 +Authorization: Bearer {{auth_token}} \ No newline at end of file diff --git a/requests/createProject.http b/requests/project/createProject.http similarity index 100% rename from requests/createProject.http rename to requests/project/createProject.http diff --git a/requests/getAllProjects.http b/requests/project/getAllProjects.http similarity index 100% rename from requests/getAllProjects.http rename to requests/project/getAllProjects.http diff --git a/requests/getProject.http b/requests/project/getProject.http similarity index 100% rename from requests/getProject.http rename to requests/project/getProject.http diff --git a/requests/updateProject.http b/requests/project/updateProject.http similarity index 100% rename from requests/updateProject.http rename to requests/project/updateProject.http diff --git a/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java b/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java new file mode 100644 index 0000000..a78f76b --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java @@ -0,0 +1,34 @@ +package de.szut.lf8_starter.employee; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +@Service +public class EmployeeService { + private final RestTemplate restTemplate; + + public EmployeeService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public boolean employeeExists(String accessToken, Long employeeId) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(accessToken.replace("Bearer ", "")); + + HttpEntity requestEntity = new HttpEntity<>(headers); + + String url = "https://employee.szut.dev/employees/" + employeeId; + + try { + restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); + } catch (HttpClientErrorException.NotFound e) { + return false; + } + + return true; + } +} diff --git a/src/main/java/de/szut/lf8_starter/project/ProjectEntity.java b/src/main/java/de/szut/lf8_starter/project/ProjectEntity.java index d9c35a1..6aa7127 100644 --- a/src/main/java/de/szut/lf8_starter/project/ProjectEntity.java +++ b/src/main/java/de/szut/lf8_starter/project/ProjectEntity.java @@ -25,7 +25,7 @@ public class ProjectEntity { private long leadingEmployee; - @ElementCollection + @ElementCollection(fetch = FetchType.EAGER) private List employees; private long contractor; diff --git a/src/main/java/de/szut/lf8_starter/project/ProjectService.java b/src/main/java/de/szut/lf8_starter/project/ProjectService.java index 31efbec..17afcea 100644 --- a/src/main/java/de/szut/lf8_starter/project/ProjectService.java +++ b/src/main/java/de/szut/lf8_starter/project/ProjectService.java @@ -25,10 +25,8 @@ public class ProjectService { return projectRepository.findById(id); } - public ProjectEntity update(ProjectEntity project) { + public void update(ProjectEntity project) { this.projectRepository.save(project); - - return project; } public void delete(Long id) { diff --git a/src/main/java/de/szut/lf8_starter/project/action/CreateProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java similarity index 97% rename from src/main/java/de/szut/lf8_starter/project/action/CreateProjectAction.java rename to src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java index 3e636b5..f21646d 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/CreateProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.action; +package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectMapper; diff --git a/src/main/java/de/szut/lf8_starter/project/action/GetAllProjectsAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/GetAllProjectsAction.java similarity index 97% rename from src/main/java/de/szut/lf8_starter/project/action/GetAllProjectsAction.java rename to src/main/java/de/szut/lf8_starter/project/action/crud/GetAllProjectsAction.java index 039a104..5fb346a 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/GetAllProjectsAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/GetAllProjectsAction.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.action; +package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectMapper; import de.szut.lf8_starter.project.ProjectService; diff --git a/src/main/java/de/szut/lf8_starter/project/action/GetProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/GetProjectAction.java similarity index 83% rename from src/main/java/de/szut/lf8_starter/project/action/GetProjectAction.java rename to src/main/java/de/szut/lf8_starter/project/action/crud/GetProjectAction.java index d8e4207..eba31c1 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/GetProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/GetProjectAction.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.action; +package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectMapper; @@ -11,7 +11,10 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import java.util.Optional; diff --git a/src/main/java/de/szut/lf8_starter/project/action/UpdateProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/UpdateProjectAction.java similarity index 95% rename from src/main/java/de/szut/lf8_starter/project/action/UpdateProjectAction.java rename to src/main/java/de/szut/lf8_starter/project/action/crud/UpdateProjectAction.java index 9c174e3..29a4ad5 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/UpdateProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/UpdateProjectAction.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.action; +package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectMapper; diff --git a/src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java new file mode 100644 index 0000000..1e14ae0 --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java @@ -0,0 +1,55 @@ +package de.szut.lf8_starter.project.action.employee; + +import de.szut.lf8_starter.employee.EmployeeService; +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Optional; + +@RestController +public class AddEmployeeToProjectAction { + private final ProjectService projectService; + private final EmployeeService employeeService; + + public AddEmployeeToProjectAction(ProjectService projectService, EmployeeService employeeService) { + this.projectService = projectService; + this.employeeService = employeeService; + } + + @Operation(summary = "Add an employee to a project") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Employee added to project"), + @ApiResponse(responseCode = "404", description = "Project or employee not found", content = @Content) + }) + @PostMapping("/projects/{projectId}/employees/{employeeId}") + public ResponseEntity create( + @PathVariable Long projectId, + @PathVariable Long employeeId, + @RequestHeader("Authorization") String accessToken + ) { + Optional project = this.projectService.findById(projectId); + + if (project.isEmpty()) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + if (!this.employeeService.employeeExists(accessToken, employeeId)) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + project.get().getEmployees().add(employeeId); + this.projectService.update(project.get()); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/src/main/java/de/szut/lf8_starter/project/dto/EmployeeDto.java b/src/main/java/de/szut/lf8_starter/project/dto/EmployeeDto.java new file mode 100644 index 0000000..05909e1 --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/dto/EmployeeDto.java @@ -0,0 +1,22 @@ +package de.szut.lf8_starter.project.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class EmployeeDto { + private long id; + private String firstName; + private String lastName; + private String street; + private String postcode; + private String city; + private String phone; + private List skillSet; +} diff --git a/src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java b/src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java new file mode 100644 index 0000000..4193e77 --- /dev/null +++ b/src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java @@ -0,0 +1,77 @@ +package de.szut.lf8_starter.integration.project; + +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.*; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc(addFilters = false) +class AddEmployeeToProjectActionIntegrationTest { + @Autowired + private MockMvc mockMvc; + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private ProjectRepository projectRepository; + + @Test + void addEmployeeToProjectTest() throws Exception { + ProjectEntity project = new ProjectEntity(); + project.setComment("comment"); + project.setContractor(1); + project.setContractorName("contractorName"); + project.setEndDate(LocalDate.of(2024, 1, 1)); + project.setLeadingEmployee(1); + project.setName("name"); + project.setStartDate(LocalDate.of(2021, 1, 1)); + project.setEmployees(List.of(1L, 2L, 3L)); + this.projectRepository.save(project); + + + mockMvc.perform(post("/projects/{projectId}/employees/{employeeId}", 1, 312) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + ProjectEntity updatedProject = projectRepository.findById(1L).get(); + + assert updatedProject.getEmployees().contains(312L); + } + + private String getBearerToken() { + String url = "https://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("grant_type", "password"); + map.add("client_id", "employee-management-service"); + map.add("username", "user"); + map.add("password", "test"); + + HttpEntity> request = new HttpEntity<>(map, headers); + + ResponseEntity response = this.restTemplate.exchange(url, HttpMethod.POST, request, Map.class); + + return Objects.requireNonNull(response.getBody()).get("access_token").toString(); + } +} diff --git a/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java index af10c64..27fe186 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java @@ -1,5 +1,6 @@ package de.szut.lf8_starter.integration.project; +import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectRepository; import org.json.JSONObject; import org.junit.jupiter.api.Test; @@ -11,11 +12,12 @@ import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; import java.util.Arrays; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @@ -41,9 +43,9 @@ class CreateProjectActionTest { } """; - final var contentAsString = this.mockMvc.perform( - post("/projects").content(content).contentType(MediaType.APPLICATION_JSON) - ) + final String contentAsString = this.mockMvc.perform( + post("/projects").content(content).contentType(MediaType.APPLICATION_JSON) + ) .andExpect(status().isCreated()) .andExpect(jsonPath("name", is("name"))) .andExpect(jsonPath("leading_employee", is(1))) @@ -57,15 +59,15 @@ class CreateProjectActionTest { .getResponse() .getContentAsString(); - final var id = Long.parseLong(new JSONObject(contentAsString).get("id").toString()); + final long id = Long.parseLong(new JSONObject(contentAsString).get("id").toString()); - final var project = this.projectRepository.findById(id); - assertThat(project.get().getName()).isEqualTo("name"); - assertThat(project.get().getLeadingEmployee()).isEqualTo(1); - assertThat(project.get().getContractor()).isEqualTo(4); - assertThat(project.get().getContractorName()).isEqualTo("Peter File"); - assertThat(project.get().getComment()).isEqualTo("goal of project"); - assertThat(project.get().getStartDate()).isEqualTo(LocalDate.of(2000, 1, 1)); - assertThat(project.get().getPlannedEndDate()).isEqualTo(LocalDate.of(2001, 1, 1)); + final Optional project = this.projectRepository.findById(id); + assertThat(project.get().getName()).isEqualTo("name"); + assertThat(project.get().getLeadingEmployee()).isEqualTo(1); + assertThat(project.get().getContractor()).isEqualTo(4); + assertThat(project.get().getContractorName()).isEqualTo("Peter File"); + assertThat(project.get().getComment()).isEqualTo("goal of project"); + assertThat(project.get().getStartDate()).isEqualTo(LocalDate.of(2000, 1, 1)); + assertThat(project.get().getPlannedEndDate()).isEqualTo(LocalDate.of(2001, 1, 1)); } } diff --git a/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java index 751d002..1b6ed3f 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java @@ -26,7 +26,7 @@ class GetProjectActionTest { @Test void getProjectTest() throws Exception { - var project = new ProjectEntity(); + ProjectEntity project = new ProjectEntity(); project.setId(1); project.setComment("comment"); project.setContractor(1); @@ -54,6 +54,6 @@ class GetProjectActionTest { @Test void getProjectShouldReturnNotFoundResponseWhenProjectIsNotFound() throws Exception { - this.mockMvc.perform(get("/projects/2")).andExpect(status().isNotFound()); + this.mockMvc.perform(get("/projects/1111")).andExpect(status().isNotFound()); } } diff --git a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java index 3fa8bd8..ff3a642 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java @@ -27,7 +27,7 @@ class ProjectFindAllSuccessTest { @Test void findAllProjectsTest() throws Exception { - var project = new ProjectEntity(); + ProjectEntity project = new ProjectEntity(); project.setId(1); project.setComment("comment"); project.setContractor(1); @@ -36,7 +36,7 @@ class ProjectFindAllSuccessTest { project.setLeadingEmployee(1); project.setName("name"); project.setStartDate(LocalDate.of(2021, 1, 1)); - project.setEmployees(List.of(1L,2L,3L)); + project.setEmployees(List.of(1L, 2L, 3L)); this.projectRepository.save(project); this.mockMvc.perform(get("/projects")) diff --git a/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java index 93285ab..c2c47dd 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java @@ -25,7 +25,7 @@ class RemoveProjectActionTest { @Test void deleteProjectTest() throws Exception { - var project = new ProjectEntity(); + ProjectEntity project = new ProjectEntity(); project.setComment("comment"); project.setContractor(1); project.setContractorName("contractorName"); diff --git a/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java index 4e53c8c..e03a680 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java @@ -13,6 +13,7 @@ import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; import java.util.Arrays; import java.util.List; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; @@ -55,7 +56,7 @@ class UpdateProjectActionTest { } """; - final var contentAsString = this.mockMvc.perform( + final String contentAsString = this.mockMvc.perform( put("/projects/1").content(content).contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isOk()) @@ -71,9 +72,9 @@ class UpdateProjectActionTest { .getResponse() .getContentAsString(); - final var id = Long.parseLong(new JSONObject(contentAsString).get("id").toString()); + final long id = Long.parseLong(new JSONObject(contentAsString).get("id").toString()); - final var existingProject = this.projectRepository.findById(id); + final Optional existingProject = this.projectRepository.findById(id); assertThat(existingProject.get().getName()).isEqualTo("updatedName"); assertThat(existingProject.get().getLeadingEmployee()).isEqualTo(2); assertThat(existingProject.get().getContractor()).isEqualTo(6); @@ -103,13 +104,13 @@ class UpdateProjectActionTest { {} """; - final var contentAsString = this.mockMvc.perform( + final String contentAsString = this.mockMvc.perform( put("/projects/1").content(content).contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isOk()) .andExpect(jsonPath("name", is("name"))) .andExpect(jsonPath("leading_employee", is(1))) - .andExpect(jsonPath("employees", is(List.of(1,2,3)))) + .andExpect(jsonPath("employees", is(List.of(1, 2, 3)))) .andExpect(jsonPath("contractor", is(1))) .andExpect(jsonPath("contractor_name", is("contractorName"))) .andExpect(jsonPath("comment", is("comment"))) @@ -120,9 +121,9 @@ class UpdateProjectActionTest { .getResponse() .getContentAsString(); - final var id = Long.parseLong(new JSONObject(contentAsString).get("id").toString()); + final long id = Long.parseLong(new JSONObject(contentAsString).get("id").toString()); - final var existingProject = this.projectRepository.findById(id); + final Optional existingProject = this.projectRepository.findById(id); assertThat(existingProject.get().getName()).isEqualTo("name"); assertThat(existingProject.get().getLeadingEmployee()).isEqualTo(1); assertThat(existingProject.get().getContractor()).isEqualTo(1); From 76b0201cf2fec3d2b40feefe80a1f55339278fef Mon Sep 17 00:00:00 2001 From: Jan Klattenhoff Date: Wed, 23 Oct 2024 09:51:12 +0000 Subject: [PATCH 4/8] feat(employee): add action to remove employee from project (SCRUM-23) (!35) Reviewed-on: https://git.kjan.de/jank/LF8/pulls/35 Reviewed-by: Phan Huy Tran Co-authored-by: Jan Klattenhoff Co-committed-by: Jan Klattenhoff --- .../RemoveEmployeeFromProjectAction.java | 52 ++++++++++++ ...oveEmployeeFromProjectIntegrationTest.java | 85 +++++++++++++++++++ .../project/UpdateProjectActionTest.java | 2 +- 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/szut/lf8_starter/project/action/employee/RemoveEmployeeFromProjectAction.java create mode 100644 src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java diff --git a/src/main/java/de/szut/lf8_starter/project/action/employee/RemoveEmployeeFromProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/employee/RemoveEmployeeFromProjectAction.java new file mode 100644 index 0000000..4fec8e7 --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/action/employee/RemoveEmployeeFromProjectAction.java @@ -0,0 +1,52 @@ +package de.szut.lf8_starter.project.action.employee; + +import de.szut.lf8_starter.employee.EmployeeService; +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@RestController +public class RemoveEmployeeFromProjectAction { + private final ProjectService projectService; + private final EmployeeService employeeService; + + public RemoveEmployeeFromProjectAction(ProjectService projectService, EmployeeService employeeService) { + this.projectService = projectService; + this.employeeService = employeeService; + } + + @Operation(summary = "Remove an employee from a project") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Employee removed from project"), + @ApiResponse(responseCode = "404", description = "Project or employee not found", content = @Content) + }) + @DeleteMapping("/projects/{projectId}/employees/{employeeId}") + public ResponseEntity remove( + @PathVariable Long projectId, + @PathVariable Long employeeId, + @RequestHeader("Authorization") String accessToken + ) { + Optional project = this.projectService.findById(projectId); + + if (project.isEmpty()) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + if (!this.employeeService.employeeExists(accessToken, employeeId)) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + project.get().getEmployees().remove(employeeId); + this.projectService.update(project.get()); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java b/src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java new file mode 100644 index 0000000..7e55dd8 --- /dev/null +++ b/src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java @@ -0,0 +1,85 @@ +package de.szut.lf8_starter.integration.project; + +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.*; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc(addFilters = false) +class RemoveEmployeeFromProjectIntegrationTest { + @Autowired + private MockMvc mockMvc; + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private ProjectRepository projectRepository; + + @Test + void removeEmployeeFromProject() throws Exception { + ProjectEntity project = new ProjectEntity(); + project.setComment("comment"); + project.setContractor(1); + project.setContractorName("contractorName"); + project.setEndDate(LocalDate.of(2024, 1, 1)); + project.setLeadingEmployee(1); + project.setName("name"); + project.setStartDate(LocalDate.of(2021, 1, 1)); + project.setEmployees(List.of(1L, 2L, 3L, 312L)); + this.projectRepository.save(project); + + + mockMvc.perform(delete("/projects/{projectId}/employees/{employeeId}", 1, 312) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + ProjectEntity updatedProject = projectRepository.findById(1L).get(); + + assert !updatedProject.getEmployees().contains(312L); + } + + private String getBearerToken() { + String url = "https://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("grant_type", "password"); + map.add("client_id", "employee-management-service"); + map.add("username", "user"); + map.add("password", "test"); + + HttpEntity> request = new HttpEntity<>(map, headers); + + ResponseEntity response = this.restTemplate.exchange(url, HttpMethod.POST, request, Map.class); + + return Objects.requireNonNull(response.getBody()).get("access_token").toString(); + } + + @Test + void removeEmployeeShouldReturn404IfProjectNotFound() throws Exception { + mockMvc.perform(delete("/projects/{projectId}/employees/{employeeId}", 1987234987, 312) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()); + } +} diff --git a/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java index e03a680..a38e233 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java @@ -136,6 +136,6 @@ class UpdateProjectActionTest { @Test void updateProjectShouldReturnNotFoundResponseWhenProjectIsNotFound() throws Exception { - this.mockMvc.perform(put("/projects/2").content("{}").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isNotFound()); + this.mockMvc.perform(put("/projects/98723498798").content("{}").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isNotFound()); } } From 202805e3dd5d6bcb5536434cbcc2a489553fbdd0 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 23 Oct 2024 10:19:33 +0000 Subject: [PATCH 5/8] feat: Implement feature to list all employees from a project (SCRUM-46) (!36) Co-authored-by: Phan Huy Tran Reviewed-on: https://git.kjan.de/jank/LF8/pulls/36 --- requests/employee/createEmployee.http | 13 +-- requests/employee/getEmployee.http | 2 +- requests/project/createProject.http | 2 +- requests/project/getEmployeesFromProject.http | 2 + .../lf8_starter/employee/EmployeeService.java | 17 ++++ .../GlobalExceptionHandler.java | 2 +- .../project/action/RemoveProjectAction.java | 7 +- .../action/crud/CreateProjectAction.java | 6 +- .../action/crud/GetAllProjectsAction.java | 4 +- .../project/action/crud/GetProjectAction.java | 4 +- .../action/crud/UpdateProjectAction.java | 6 +- .../GetEmployeeFromProjectAction.java | 51 ++++++++++++ .../dto/{ => employee}/EmployeeDto.java | 2 +- .../project/dto/employee/GetEmployeeDto.java | 11 +++ .../employee/GetEmployeesFromProjectDto.java | 17 ++++ .../project/dto/employee/GetSkillDto.java | 13 +++ .../dto/{ => project}/CreateProjectDto.java | 2 +- .../dto/{ => project}/GetProjectDto.java | 2 +- .../dto/{ => project}/UpdateProjectDto.java | 2 +- .../mapper/EmployeeFromProjectMapper.java | 35 ++++++++ .../project/{ => mapper}/ProjectMapper.java | 9 +- .../project/GetEmployeesFromProjectTest.java | 82 +++++++++++++++++++ 22 files changed, 263 insertions(+), 28 deletions(-) create mode 100644 requests/project/getEmployeesFromProject.http create mode 100644 src/main/java/de/szut/lf8_starter/project/action/employee/GetEmployeeFromProjectAction.java rename src/main/java/de/szut/lf8_starter/project/dto/{ => employee}/EmployeeDto.java (91%) create mode 100644 src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeeDto.java create mode 100644 src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeesFromProjectDto.java create mode 100644 src/main/java/de/szut/lf8_starter/project/dto/employee/GetSkillDto.java rename src/main/java/de/szut/lf8_starter/project/dto/{ => project}/CreateProjectDto.java (95%) rename src/main/java/de/szut/lf8_starter/project/dto/{ => project}/GetProjectDto.java (94%) rename src/main/java/de/szut/lf8_starter/project/dto/{ => project}/UpdateProjectDto.java (90%) create mode 100644 src/main/java/de/szut/lf8_starter/project/mapper/EmployeeFromProjectMapper.java rename src/main/java/de/szut/lf8_starter/project/{ => mapper}/ProjectMapper.java (90%) create mode 100644 src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java diff --git a/requests/employee/createEmployee.http b/requests/employee/createEmployee.http index a170fe2..b3f4591 100644 --- a/requests/employee/createEmployee.http +++ b/requests/employee/createEmployee.http @@ -3,10 +3,13 @@ Authorization: Bearer {{auth_token}} Content-Type: application/json { - "firstName": "Jan", - "lastName": "Klattenhoff", - "street": "Pirolweg 17", + "firstName": "Jan1", + "lastName": "Klattenhoff1", + "street": "Pirolweg 171", "postcode": "27777", - "city": "Gandakersee", - "phone": "017684984816" + "city": "Gandakersee1", + "phone": "0176849848161", + "skillSet": [ + "1" + ] } \ No newline at end of file diff --git a/requests/employee/getEmployee.http b/requests/employee/getEmployee.http index eddf4d9..350a7fc 100644 --- a/requests/employee/getEmployee.http +++ b/requests/employee/getEmployee.http @@ -1,2 +1,2 @@ -GET https://employee.szut.dev/employees/312 +GET https://employee.szut.dev/employees/315 Authorization: Bearer {{auth_token}} \ No newline at end of file diff --git a/requests/project/createProject.http b/requests/project/createProject.http index 7b08e28..3338a2c 100644 --- a/requests/project/createProject.http +++ b/requests/project/createProject.http @@ -6,7 +6,7 @@ Content-Type: application/json { "name": "name", "leading_employee": 1, - "employees": [2, 3], + "employees": [315, 312], "contractor": 4, "contractor_name": "Peter File", "comment": "goal of project", diff --git a/requests/project/getEmployeesFromProject.http b/requests/project/getEmployeesFromProject.http new file mode 100644 index 0000000..d12dd96 --- /dev/null +++ b/requests/project/getEmployeesFromProject.http @@ -0,0 +1,2 @@ +GET http://localhost:8080/projects/2/employees +Authorization: Bearer {{auth_token}} \ No newline at end of file diff --git a/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java b/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java index a78f76b..907de80 100644 --- a/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java +++ b/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java @@ -1,5 +1,7 @@ package de.szut.lf8_starter.employee; +import de.szut.lf8_starter.exceptionHandling.ResourceNotFoundException; +import de.szut.lf8_starter.project.dto.employee.GetEmployeeDto; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -31,4 +33,19 @@ public class EmployeeService { return true; } + + public GetEmployeeDto getEmployee(String accessToken, Long employeeId) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(accessToken.replace("Bearer ", "")); + + HttpEntity requestEntity = new HttpEntity<>(headers); + + String url = "https://employee.szut.dev/employees/" + employeeId; + + try { + return restTemplate.exchange(url, HttpMethod.GET, requestEntity, GetEmployeeDto.class).getBody(); + } catch (HttpClientErrorException.NotFound e) { + throw new ResourceNotFoundException("Employee not found"); + } + } } diff --git a/src/main/java/de/szut/lf8_starter/exceptionHandling/GlobalExceptionHandler.java b/src/main/java/de/szut/lf8_starter/exceptionHandling/GlobalExceptionHandler.java index f6415b5..5327d4f 100644 --- a/src/main/java/de/szut/lf8_starter/exceptionHandling/GlobalExceptionHandler.java +++ b/src/main/java/de/szut/lf8_starter/exceptionHandling/GlobalExceptionHandler.java @@ -30,7 +30,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity handleAllOtherExceptions(Exception ex, WebRequest request) { ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getClass() + " " + ex.getMessage(), request.getDescription(false)); - + return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR); } diff --git a/src/main/java/de/szut/lf8_starter/project/action/RemoveProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/RemoveProjectAction.java index d14dbee..efbd48d 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/RemoveProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/RemoveProjectAction.java @@ -2,14 +2,17 @@ package de.szut.lf8_starter.project.action; import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectService; -import de.szut.lf8_starter.project.dto.GetProjectDto; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import java.util.Optional; diff --git a/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java index f21646d..1725ba8 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java @@ -1,10 +1,10 @@ package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectEntity; -import de.szut.lf8_starter.project.ProjectMapper; import de.szut.lf8_starter.project.ProjectService; -import de.szut.lf8_starter.project.dto.CreateProjectDto; -import de.szut.lf8_starter.project.dto.GetProjectDto; +import de.szut.lf8_starter.project.dto.project.CreateProjectDto; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; +import de.szut.lf8_starter.project.mapper.ProjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/de/szut/lf8_starter/project/action/crud/GetAllProjectsAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/GetAllProjectsAction.java index 5fb346a..97f5b9a 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/crud/GetAllProjectsAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/GetAllProjectsAction.java @@ -1,8 +1,8 @@ package de.szut.lf8_starter.project.action.crud; -import de.szut.lf8_starter.project.ProjectMapper; import de.szut.lf8_starter.project.ProjectService; -import de.szut.lf8_starter.project.dto.GetProjectDto; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; +import de.szut.lf8_starter.project.mapper.ProjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/de/szut/lf8_starter/project/action/crud/GetProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/GetProjectAction.java index eba31c1..041a26d 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/crud/GetProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/GetProjectAction.java @@ -1,9 +1,9 @@ package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectEntity; -import de.szut.lf8_starter.project.ProjectMapper; import de.szut.lf8_starter.project.ProjectService; -import de.szut.lf8_starter.project.dto.GetProjectDto; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; +import de.szut.lf8_starter.project.mapper.ProjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/de/szut/lf8_starter/project/action/crud/UpdateProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/UpdateProjectAction.java index 29a4ad5..3a169cf 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/crud/UpdateProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/UpdateProjectAction.java @@ -1,10 +1,10 @@ package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectEntity; -import de.szut.lf8_starter.project.ProjectMapper; import de.szut.lf8_starter.project.ProjectService; -import de.szut.lf8_starter.project.dto.GetProjectDto; -import de.szut.lf8_starter.project.dto.UpdateProjectDto; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; +import de.szut.lf8_starter.project.dto.project.UpdateProjectDto; +import de.szut.lf8_starter.project.mapper.ProjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/src/main/java/de/szut/lf8_starter/project/action/employee/GetEmployeeFromProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/employee/GetEmployeeFromProjectAction.java new file mode 100644 index 0000000..f9d339c --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/action/employee/GetEmployeeFromProjectAction.java @@ -0,0 +1,51 @@ +package de.szut.lf8_starter.project.action.employee; + +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectService; +import de.szut.lf8_starter.project.dto.employee.GetEmployeesFromProjectDto; +import de.szut.lf8_starter.project.mapper.EmployeeFromProjectMapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Optional; + +@RestController +public class GetEmployeeFromProjectAction { + private final ProjectService projectService; + private final EmployeeFromProjectMapper employeeFromProjectMapper; + + public GetEmployeeFromProjectAction(ProjectService projectService, EmployeeFromProjectMapper employeeFromProjectMapper) { + this.projectService = projectService; + this.employeeFromProjectMapper = employeeFromProjectMapper; + } + + @Operation(summary = "Get all employees from project") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Outputs projects with employees"), + @ApiResponse(responseCode = "404", description = "Project not found", content = @Content) + }) + @GetMapping("/projects/{projectId}/employees") + public ResponseEntity create( + @PathVariable Long projectId, + @RequestHeader("Authorization") String accessToken + ) { + Optional project = projectService.findById(projectId); + + if (project.isEmpty()) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + GetEmployeesFromProjectDto getEmployeesFromProjectDto = this.employeeFromProjectMapper + .mapToGetEmployeesFromProjectDto(project.get(), accessToken); + + return new ResponseEntity<>(getEmployeesFromProjectDto, HttpStatus.OK); + } +} diff --git a/src/main/java/de/szut/lf8_starter/project/dto/EmployeeDto.java b/src/main/java/de/szut/lf8_starter/project/dto/employee/EmployeeDto.java similarity index 91% rename from src/main/java/de/szut/lf8_starter/project/dto/EmployeeDto.java rename to src/main/java/de/szut/lf8_starter/project/dto/employee/EmployeeDto.java index 05909e1..cc4e872 100644 --- a/src/main/java/de/szut/lf8_starter/project/dto/EmployeeDto.java +++ b/src/main/java/de/szut/lf8_starter/project/dto/employee/EmployeeDto.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.dto; +package de.szut.lf8_starter.project.dto.employee; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; diff --git a/src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeeDto.java b/src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeeDto.java new file mode 100644 index 0000000..fdb13a1 --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeeDto.java @@ -0,0 +1,11 @@ +package de.szut.lf8_starter.project.dto.employee; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class GetEmployeeDto { + private long id; + private GetSkillDto[] skillSet; +} diff --git a/src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeesFromProjectDto.java b/src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeesFromProjectDto.java new file mode 100644 index 0000000..df24a76 --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/dto/employee/GetEmployeesFromProjectDto.java @@ -0,0 +1,17 @@ +package de.szut.lf8_starter.project.dto.employee; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; + +@Getter +@Setter +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GetEmployeesFromProjectDto { + private long projectId; + private String projectDescription; + private ArrayList employees; +} diff --git a/src/main/java/de/szut/lf8_starter/project/dto/employee/GetSkillDto.java b/src/main/java/de/szut/lf8_starter/project/dto/employee/GetSkillDto.java new file mode 100644 index 0000000..ba91c03 --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/dto/employee/GetSkillDto.java @@ -0,0 +1,13 @@ +package de.szut.lf8_starter.project.dto.employee; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GetSkillDto { + private String skill; +} diff --git a/src/main/java/de/szut/lf8_starter/project/dto/CreateProjectDto.java b/src/main/java/de/szut/lf8_starter/project/dto/project/CreateProjectDto.java similarity index 95% rename from src/main/java/de/szut/lf8_starter/project/dto/CreateProjectDto.java rename to src/main/java/de/szut/lf8_starter/project/dto/project/CreateProjectDto.java index ebf4393..8e2a2aa 100644 --- a/src/main/java/de/szut/lf8_starter/project/dto/CreateProjectDto.java +++ b/src/main/java/de/szut/lf8_starter/project/dto/project/CreateProjectDto.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.dto; +package de.szut.lf8_starter.project.dto.project; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.PropertyNamingStrategies; diff --git a/src/main/java/de/szut/lf8_starter/project/dto/GetProjectDto.java b/src/main/java/de/szut/lf8_starter/project/dto/project/GetProjectDto.java similarity index 94% rename from src/main/java/de/szut/lf8_starter/project/dto/GetProjectDto.java rename to src/main/java/de/szut/lf8_starter/project/dto/project/GetProjectDto.java index 90ebe5e..89f043b 100644 --- a/src/main/java/de/szut/lf8_starter/project/dto/GetProjectDto.java +++ b/src/main/java/de/szut/lf8_starter/project/dto/project/GetProjectDto.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.dto; +package de.szut.lf8_starter.project.dto.project; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.PropertyNamingStrategies; diff --git a/src/main/java/de/szut/lf8_starter/project/dto/UpdateProjectDto.java b/src/main/java/de/szut/lf8_starter/project/dto/project/UpdateProjectDto.java similarity index 90% rename from src/main/java/de/szut/lf8_starter/project/dto/UpdateProjectDto.java rename to src/main/java/de/szut/lf8_starter/project/dto/project/UpdateProjectDto.java index 6d94232..943a62b 100644 --- a/src/main/java/de/szut/lf8_starter/project/dto/UpdateProjectDto.java +++ b/src/main/java/de/szut/lf8_starter/project/dto/project/UpdateProjectDto.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.dto; +package de.szut.lf8_starter.project.dto.project; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.PropertyNamingStrategies; diff --git a/src/main/java/de/szut/lf8_starter/project/mapper/EmployeeFromProjectMapper.java b/src/main/java/de/szut/lf8_starter/project/mapper/EmployeeFromProjectMapper.java new file mode 100644 index 0000000..039f23c --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/mapper/EmployeeFromProjectMapper.java @@ -0,0 +1,35 @@ +package de.szut.lf8_starter.project.mapper; + +import de.szut.lf8_starter.employee.EmployeeService; +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.dto.employee.GetEmployeeDto; +import de.szut.lf8_starter.project.dto.employee.GetEmployeesFromProjectDto; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; + +@Service +public class EmployeeFromProjectMapper { + private final EmployeeService employeeService; + + public EmployeeFromProjectMapper(EmployeeService employeeService) { + this.employeeService = employeeService; + } + + public GetEmployeesFromProjectDto mapToGetEmployeesFromProjectDto(ProjectEntity projectEntity, String accessToken) { + GetEmployeesFromProjectDto getEmployeesFromProjectDto = new GetEmployeesFromProjectDto(); + getEmployeesFromProjectDto.setProjectId(projectEntity.getId()); + getEmployeesFromProjectDto.setProjectDescription(projectEntity.getComment()); + + ArrayList employeeDtos = new ArrayList<>(); + for (long id : projectEntity.getEmployees()) { + GetEmployeeDto getEmployeeDto = this.employeeService.getEmployee(accessToken, id); + + employeeDtos.add(getEmployeeDto); + } + + getEmployeesFromProjectDto.setEmployees(employeeDtos); + + return getEmployeesFromProjectDto; + } +} diff --git a/src/main/java/de/szut/lf8_starter/project/ProjectMapper.java b/src/main/java/de/szut/lf8_starter/project/mapper/ProjectMapper.java similarity index 90% rename from src/main/java/de/szut/lf8_starter/project/ProjectMapper.java rename to src/main/java/de/szut/lf8_starter/project/mapper/ProjectMapper.java index cb43b84..0d3b0af 100644 --- a/src/main/java/de/szut/lf8_starter/project/ProjectMapper.java +++ b/src/main/java/de/szut/lf8_starter/project/mapper/ProjectMapper.java @@ -1,8 +1,9 @@ -package de.szut.lf8_starter.project; +package de.szut.lf8_starter.project.mapper; -import de.szut.lf8_starter.project.dto.CreateProjectDto; -import de.szut.lf8_starter.project.dto.GetProjectDto; -import de.szut.lf8_starter.project.dto.UpdateProjectDto; +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.dto.project.CreateProjectDto; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; +import de.szut.lf8_starter.project.dto.project.UpdateProjectDto; import org.springframework.stereotype.Service; @Service diff --git a/src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java b/src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java new file mode 100644 index 0000000..985fd2f --- /dev/null +++ b/src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java @@ -0,0 +1,82 @@ +package de.szut.lf8_starter.integration.project; + +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.*; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc(addFilters = false) +class GetEmployeesFromProjectTest { + @Autowired + private MockMvc mockMvc; + @Autowired + private ProjectRepository projectRepository; + @Autowired + private RestTemplate restTemplate; + + @Test + void getEmployeesFromProjectTest() throws Exception { + ProjectEntity project = new ProjectEntity(); + project.setId(1); + project.setComment("comment"); + project.setContractor(1); + project.setContractorName("contractorName"); + project.setEndDate(LocalDate.of(2024, 1, 1)); + project.setLeadingEmployee(1); + project.setName("name"); + project.setStartDate(LocalDate.of(2021, 1, 1)); + project.setEmployees(List.of(315L)); + this.projectRepository.save(project); + + this.mockMvc.perform(get("/projects/1/employees") + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ).andExpect(status().isOk()) + .andExpect(jsonPath("project_id").value(1)) + .andExpect(jsonPath("project_description").value("comment")) + .andExpect(jsonPath("employees[0].id").value(315)) + .andExpect(jsonPath("employees[0].skillSet[0].skill").value("Product Owner")); + } + + @Test + void getEmployeesFromProjectShouldReturnNotFoundResponseWhenProjectIsNotFound() throws Exception { + this.mockMvc.perform(get("/projects/1111/employees") + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ).andExpect(status().isNotFound()); + } + + private String getBearerToken() { + String url = "https://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("grant_type", "password"); + map.add("client_id", "employee-management-service"); + map.add("username", "user"); + map.add("password", "test"); + + HttpEntity> request = new HttpEntity<>(map, headers); + + ResponseEntity response = this.restTemplate.exchange(url, HttpMethod.POST, request, Map.class); + + return Objects.requireNonNull(response.getBody()).get("access_token").toString(); + } +} From 97e211946faf2027b1fffdf6b03bfc7fce57d413 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 23 Oct 2024 10:50:49 +0000 Subject: [PATCH 6/8] feat: Implement feature to get all projects from an employee (SCRUM-31) (!37) Co-authored-by: Phan Huy Tran Reviewed-on: https://git.kjan.de/jank/LF8/pulls/37 Reviewed-by: Jan Gleytenhoover --- requests/employee/getProjects.http | 3 + .../lf8_starter/employee/EmployeeService.java | 24 +++++- .../project/ProjectRepository.java | 6 ++ .../employee/GetProjectsFromEmployee.java | 39 +++++++++ .../project/GetProjectsFromEmployeeTest.java | 86 +++++++++++++++++++ 5 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 requests/employee/getProjects.http create mode 100644 src/main/java/de/szut/lf8_starter/project/action/employee/GetProjectsFromEmployee.java create mode 100644 src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java diff --git a/requests/employee/getProjects.http b/requests/employee/getProjects.http new file mode 100644 index 0000000..8689798 --- /dev/null +++ b/requests/employee/getProjects.http @@ -0,0 +1,3 @@ +GET http://localhost:8080/employees/315/projects +Authorization: Bearer {{auth_token}} +Content-Type: application/json \ No newline at end of file diff --git a/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java b/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java index 907de80..d126288 100644 --- a/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java +++ b/src/main/java/de/szut/lf8_starter/employee/EmployeeService.java @@ -1,7 +1,11 @@ package de.szut.lf8_starter.employee; import de.szut.lf8_starter.exceptionHandling.ResourceNotFoundException; +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectRepository; import de.szut.lf8_starter.project.dto.employee.GetEmployeeDto; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; +import de.szut.lf8_starter.project.mapper.ProjectMapper; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -9,12 +13,19 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import java.util.ArrayList; +import java.util.List; + @Service public class EmployeeService { private final RestTemplate restTemplate; + private final ProjectRepository projectRepository; + private final ProjectMapper projectMapper; - public EmployeeService(RestTemplate restTemplate) { + public EmployeeService(RestTemplate restTemplate, ProjectRepository projectRepository, ProjectMapper projectMapper) { this.restTemplate = restTemplate; + this.projectRepository = projectRepository; + this.projectMapper = projectMapper; } public boolean employeeExists(String accessToken, Long employeeId) { @@ -48,4 +59,15 @@ public class EmployeeService { throw new ResourceNotFoundException("Employee not found"); } } + + public List getProjects(Long employeeId) { + ArrayList projects = this.projectRepository.findAllByEmployeeId(employeeId); + ArrayList projectDtos = new ArrayList<>(); + + for (ProjectEntity project : projects) { + projectDtos.add(this.projectMapper.mapToGetDto(project)); + } + + return projectDtos; + } } diff --git a/src/main/java/de/szut/lf8_starter/project/ProjectRepository.java b/src/main/java/de/szut/lf8_starter/project/ProjectRepository.java index 6f7e33f..3b9dfb0 100644 --- a/src/main/java/de/szut/lf8_starter/project/ProjectRepository.java +++ b/src/main/java/de/szut/lf8_starter/project/ProjectRepository.java @@ -1,8 +1,14 @@ package de.szut.lf8_starter.project; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.ArrayList; + @Repository public interface ProjectRepository extends JpaRepository { + @Query("SELECT p FROM ProjectEntity p WHERE :employeeId MEMBER OF p.employees") + ArrayList findAllByEmployeeId(@Param("employeeId") Long employeeId); } diff --git a/src/main/java/de/szut/lf8_starter/project/action/employee/GetProjectsFromEmployee.java b/src/main/java/de/szut/lf8_starter/project/action/employee/GetProjectsFromEmployee.java new file mode 100644 index 0000000..baa4dea --- /dev/null +++ b/src/main/java/de/szut/lf8_starter/project/action/employee/GetProjectsFromEmployee.java @@ -0,0 +1,39 @@ +package de.szut.lf8_starter.project.action.employee; + +import de.szut.lf8_starter.employee.EmployeeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class GetProjectsFromEmployee { + private final EmployeeService employeeService; + + public GetProjectsFromEmployee(EmployeeService employeeService) { + this.employeeService = employeeService; + } + + @Operation(summary = "Get all projects from employee") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Get all projects from employee", content = @Content), + @ApiResponse(responseCode = "404", description = "Employee not found", content = @Content) + }) + @GetMapping("/employees/{employeeId}/projects") + public ResponseEntity create( + @PathVariable Long employeeId, + @RequestHeader("Authorization") String accessToken + ) { + if (!this.employeeService.employeeExists(accessToken, employeeId)) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + return new ResponseEntity<>(this.employeeService.getProjects(employeeId), HttpStatus.OK); + } +} diff --git a/src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java new file mode 100644 index 0000000..769bd69 --- /dev/null +++ b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java @@ -0,0 +1,86 @@ +package de.szut.lf8_starter.integration.project; + +import de.szut.lf8_starter.project.ProjectEntity; +import de.szut.lf8_starter.project.ProjectRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.*; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc(addFilters = false) +class GetProjectsFromEmployeeTest { + @Autowired + private MockMvc mockMvc; + @Autowired + private ProjectRepository projectRepository; + @Autowired + private RestTemplate restTemplate; + + @Test + void getProjectsFromEmployeeTest() throws Exception { + ProjectEntity project = new ProjectEntity(); + project.setId(1); + project.setComment("comment"); + project.setContractor(1); + project.setContractorName("contractorName"); + project.setEndDate(LocalDate.of(2024, 1, 1)); + project.setLeadingEmployee(1); + project.setName("name"); + project.setStartDate(LocalDate.of(2021, 1, 1)); + project.setEmployees(List.of(315L)); + this.projectRepository.save(project); + + this.mockMvc.perform(get("/employees/315/projects") + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ).andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(1)) + .andExpect(jsonPath("$[0].name").value("name")) + .andExpect(jsonPath("$[0].leading_employee").value(1)) + .andExpect(jsonPath("$[0].employees[0]").value(315)) + .andExpect(jsonPath("$[0].contractor").value(1)) + .andExpect(jsonPath("$[0].contractor_name").value("contractorName")) + .andExpect(jsonPath("$[0].comment").value("comment")) + .andExpect(jsonPath("$[0].start_date").value("01.01.2021")); + } + + @Test + void GetProjectsFromEmployeeShouldReturnNotFoundResponseWhenEmployeeIsNotFound() throws Exception { + this.mockMvc.perform(get("/employees/1111/projects") + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ).andExpect(status().isNotFound()); + } + + private String getBearerToken() { + String url = "https://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("grant_type", "password"); + map.add("client_id", "employee-management-service"); + map.add("username", "user"); + map.add("password", "test"); + + HttpEntity> request = new HttpEntity<>(map, headers); + + ResponseEntity response = this.restTemplate.exchange(url, HttpMethod.POST, request, Map.class); + + return Objects.requireNonNull(response.getBody()).get("access_token").toString(); + } +} From 5f6caa7fc23819451ca670ca938d2c30e7bb6c19 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Fri, 25 Oct 2024 05:01:10 +0000 Subject: [PATCH 7/8] feat: Implement feature to acknowledge project time frames when assigning employees to projects (SCRUM-7 SCRUM-2) (!38) Co-authored-by: Phan Huy Tran Reviewed-on: https://git.kjan.de/jank/LF8/pulls/38 Co-authored-by: Phan Huy Tran Co-committed-by: Phan Huy Tran --- requests/project/createProject.http | 6 +- .../lf8_starter/project/ProjectService.java | 15 ++ .../action/crud/CreateProjectAction.java | 33 +++- .../{ => crud}/RemoveProjectAction.java | 2 +- .../employee/AddEmployeeToProjectAction.java | 20 +- .../project/AddEmployeeToProjectTest.java | 85 ++++++++- .../project/CreateProjectActionTest.java | 173 +++++++++++++++++- .../project/GetEmployeesFromProjectTest.java | 2 + .../project/GetProjectActionTest.java | 2 + .../project/GetProjectsFromEmployeeTest.java | 2 + .../ProjectFindAllNotAuthenticated.java | 32 ---- .../project/ProjectFindAllSuccessTest.java | 2 + ...oveEmployeeFromProjectIntegrationTest.java | 4 +- .../project/RemoveProjectActionTest.java | 2 + .../project/UpdateProjectActionTest.java | 2 + .../resources/application-test.properties | 6 + 16 files changed, 334 insertions(+), 54 deletions(-) rename src/main/java/de/szut/lf8_starter/project/action/{ => crud}/RemoveProjectAction.java (97%) delete mode 100644 src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java create mode 100644 src/test/resources/application-test.properties diff --git a/requests/project/createProject.http b/requests/project/createProject.http index 3338a2c..1e044b5 100644 --- a/requests/project/createProject.http +++ b/requests/project/createProject.http @@ -6,10 +6,10 @@ Content-Type: application/json { "name": "name", "leading_employee": 1, - "employees": [315, 312], + "employees": [312], "contractor": 4, "contractor_name": "Peter File", "comment": "goal of project", - "start_date": "01.01.2000", - "planned_end_date": "01.01.2001" + "start_date": "02.01.2020", + "planned_end_date": "01.01.2023" } \ No newline at end of file diff --git a/src/main/java/de/szut/lf8_starter/project/ProjectService.java b/src/main/java/de/szut/lf8_starter/project/ProjectService.java index 17afcea..5a50454 100644 --- a/src/main/java/de/szut/lf8_starter/project/ProjectService.java +++ b/src/main/java/de/szut/lf8_starter/project/ProjectService.java @@ -1,5 +1,6 @@ package de.szut.lf8_starter.project; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; import org.springframework.stereotype.Service; import java.util.List; @@ -32,4 +33,18 @@ public class ProjectService { public void delete(Long id) { this.projectRepository.deleteById(id); } + + public boolean isOverlapping(GetProjectDto getProjectDto, ProjectEntity existingProjectEntity) { + return isDateRangeOverlapping(getProjectDto, existingProjectEntity) || isDateEqual(getProjectDto, existingProjectEntity); + } + + private boolean isDateRangeOverlapping(GetProjectDto getProjectDto, ProjectEntity existingProjectEntity) { + return getProjectDto.getStartDate().isBefore(existingProjectEntity.getPlannedEndDate()) + && getProjectDto.getPlannedEndDate().isAfter(existingProjectEntity.getStartDate()); + } + + private boolean isDateEqual(GetProjectDto getProjectDto, ProjectEntity existingProjectEntity) { + return getProjectDto.getStartDate().isEqual(existingProjectEntity.getStartDate()) + || getProjectDto.getPlannedEndDate().isEqual(existingProjectEntity.getPlannedEndDate()); + } } diff --git a/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java index 1725ba8..8319011 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/CreateProjectAction.java @@ -1,5 +1,6 @@ package de.szut.lf8_starter.project.action.crud; +import de.szut.lf8_starter.employee.EmployeeService; import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectService; import de.szut.lf8_starter.project.dto.project.CreateProjectDto; @@ -12,15 +13,18 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping(value = "/projects") public class CreateProjectAction { + private final EmployeeService employeeService; private final ProjectService projectService; private final ProjectMapper projectMapper; - public CreateProjectAction(ProjectService projectService, ProjectMapper mappingService) { + public CreateProjectAction(EmployeeService employeeService, ProjectService projectService, ProjectMapper mappingService) { + this.employeeService = employeeService; this.projectService = projectService; this.projectMapper = mappingService; } @@ -29,14 +33,31 @@ public class CreateProjectAction { @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "created project", content = {@Content(mediaType = "application/json", schema = @Schema(implementation = GetProjectDto.class))}), @ApiResponse(responseCode = "400", description = "invalid JSON posted", content = @Content), - @ApiResponse(responseCode = "401", description = "not authorized", content = @Content)}) + @ApiResponse(responseCode = "401", description = "not authorized", content = @Content), + @ApiResponse(responseCode = "409", description = "Project dates conflict", content = @Content) + }) @PostMapping @ResponseStatus(code = HttpStatus.CREATED) - public GetProjectDto create(@RequestBody @Valid CreateProjectDto createProjectDto) { - ProjectEntity projectEntity = this.projectMapper.mapCreateDtoToEntity(createProjectDto); + public ResponseEntity create( + @RequestBody @Valid CreateProjectDto createProjectDto, + @RequestHeader("Authorization") String accessToken + ) { + ProjectEntity project = this.projectMapper.mapCreateDtoToEntity(createProjectDto); - projectEntity = this.projectService.create(projectEntity); + for (Long employeeId : createProjectDto.getEmployees()) { + if (!this.employeeService.employeeExists(accessToken, employeeId)) { + return new ResponseEntity<>("Employee with ID: " + employeeId + " not found", HttpStatus.NOT_FOUND); + } - return this.projectMapper.mapToGetDto(projectEntity); + for (GetProjectDto getProjectDto : this.employeeService.getProjects(employeeId)) { + if (projectService.isOverlapping(getProjectDto, project)) { + return new ResponseEntity<>("Project dates conflict with an existing project for Employee with ID: " + employeeId, HttpStatus.CONFLICT); + } + } + } + + this.projectService.create(project); + + return new ResponseEntity<>(this.projectMapper.mapToGetDto(project), HttpStatus.CREATED); } } diff --git a/src/main/java/de/szut/lf8_starter/project/action/RemoveProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/crud/RemoveProjectAction.java similarity index 97% rename from src/main/java/de/szut/lf8_starter/project/action/RemoveProjectAction.java rename to src/main/java/de/szut/lf8_starter/project/action/crud/RemoveProjectAction.java index efbd48d..49aebab 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/RemoveProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/crud/RemoveProjectAction.java @@ -1,4 +1,4 @@ -package de.szut.lf8_starter.project.action; +package de.szut.lf8_starter.project.action.crud; import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectService; diff --git a/src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java index 1e14ae0..d9862e8 100644 --- a/src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java +++ b/src/main/java/de/szut/lf8_starter/project/action/employee/AddEmployeeToProjectAction.java @@ -3,6 +3,7 @@ package de.szut.lf8_starter.project.action.employee; import de.szut.lf8_starter.employee.EmployeeService; import de.szut.lf8_starter.project.ProjectEntity; import de.szut.lf8_starter.project.ProjectService; +import de.szut.lf8_starter.project.dto.project.GetProjectDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -29,7 +30,8 @@ public class AddEmployeeToProjectAction { @Operation(summary = "Add an employee to a project") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Employee added to project"), - @ApiResponse(responseCode = "404", description = "Project or employee not found", content = @Content) + @ApiResponse(responseCode = "404", description = "Project or employee not found", content = @Content), + @ApiResponse(responseCode = "409", description = "Project dates conflict", content = @Content) }) @PostMapping("/projects/{projectId}/employees/{employeeId}") public ResponseEntity create( @@ -37,18 +39,26 @@ public class AddEmployeeToProjectAction { @PathVariable Long employeeId, @RequestHeader("Authorization") String accessToken ) { - Optional project = this.projectService.findById(projectId); + Optional optionalProject = this.projectService.findById(projectId); - if (project.isEmpty()) { + if (optionalProject.isEmpty()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } + ProjectEntity projectEntity = optionalProject.get(); + if (!this.employeeService.employeeExists(accessToken, employeeId)) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - project.get().getEmployees().add(employeeId); - this.projectService.update(project.get()); + for (GetProjectDto getProjectDto : this.employeeService.getProjects(employeeId)) { + if (this.projectService.isOverlapping(getProjectDto, projectEntity)) { + return new ResponseEntity<>("Project dates conflict with an existing project for Employee with ID: " + employeeId, HttpStatus.CONFLICT); + } + } + + projectEntity.getEmployees().add(employeeId); + this.projectService.update(projectEntity); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } diff --git a/src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java b/src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java index 4193e77..3efc563 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/AddEmployeeToProjectTest.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.*; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -22,7 +23,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) -class AddEmployeeToProjectActionIntegrationTest { +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class AddEmployeeToProjectTest { @Autowired private MockMvc mockMvc; @@ -56,6 +58,87 @@ class AddEmployeeToProjectActionIntegrationTest { assert updatedProject.getEmployees().contains(312L); } + @Test + void addEmployeeToPastProjectTest() throws Exception { + ProjectEntity project = new ProjectEntity(); + project.setComment("past project"); + project.setContractor(1); + project.setContractorName("contractorName"); + project.setPlannedEndDate(LocalDate.of(1991, 1, 1)); + project.setLeadingEmployee(1); + project.setName("past project"); + project.setStartDate(LocalDate.of(1990, 1, 1)); + project.setEmployees(List.of(1L, 2L, 3L)); + this.projectRepository.save(project); + + mockMvc.perform(post("/projects/{projectId}/employees/{employeeId}", project.getId(), 312) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + ProjectEntity updatedProject = projectRepository.findById(project.getId()).get(); + + assert updatedProject.getEmployees().contains(312L); + } + + @Test + void addEmployeeToFutureProjectTest() throws Exception { + ProjectEntity project = new ProjectEntity(); + project.setComment("future project"); + project.setContractor(1); + project.setContractorName("contractorName"); + project.setPlannedEndDate(LocalDate.of(2101, 1, 1)); + project.setLeadingEmployee(1); + project.setName("future project"); + project.setStartDate(LocalDate.of(2100, 1, 1)); + project.setEmployees(List.of(1L, 2L, 3L)); + this.projectRepository.save(project); + + mockMvc.perform(post("/projects/{projectId}/employees/{employeeId}", project.getId(), 312) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + ProjectEntity updatedProject = projectRepository.findById(project.getId()).get(); + + assert updatedProject.getEmployees().contains(312L); + } + + @Test + void addEmployeeToProjectWithOverlappingDatesTest() throws Exception { + ProjectEntity project1 = new ProjectEntity(); + project1.setComment("project 1"); + project1.setContractor(1); + project1.setContractorName("contractorName"); + project1.setPlannedEndDate(LocalDate.of(2023, 1, 1)); + project1.setLeadingEmployee(1); + project1.setName("project 1"); + project1.setStartDate(LocalDate.of(2022, 1, 1)); + project1.setEmployees(List.of(1L, 2L, 3L)); + this.projectRepository.save(project1); + + ProjectEntity project2 = new ProjectEntity(); + project2.setComment("project 2"); + project2.setContractor(1); + project2.setContractorName("contractorName"); + project2.setPlannedEndDate(LocalDate.of(2023, 6, 1)); + project2.setLeadingEmployee(1); + project2.setName("project 2"); + project2.setStartDate(LocalDate.of(2022, 6, 1)); + project2.setEmployees(List.of(1L, 2L, 3L)); + this.projectRepository.save(project2); + + mockMvc.perform(post("/projects/{projectId}/employees/{employeeId}", project1.getId(), 312) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + mockMvc.perform(post("/projects/{projectId}/employees/{employeeId}", project2.getId(), 312) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isConflict()); + } + private String getBearerToken() { String url = "https://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token"; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java index 27fe186..881b71a 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/CreateProjectActionTest.java @@ -7,11 +7,17 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; +import org.springframework.http.*; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; import java.time.LocalDate; -import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -22,11 +28,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class CreateProjectActionTest { @Autowired private MockMvc mockMvc; @Autowired private ProjectRepository projectRepository; + @Autowired + private RestTemplate restTemplate; @Test void createProjectTest() throws Exception { @@ -34,7 +43,7 @@ class CreateProjectActionTest { { "name": "name", "leading_employee": 1, - "employees": [2, 3], + "employees": [312], "contractor": 4, "contractor_name": "Peter File", "comment": "goal of project", @@ -44,12 +53,15 @@ class CreateProjectActionTest { """; final String contentAsString = this.mockMvc.perform( - post("/projects").content(content).contentType(MediaType.APPLICATION_JSON) + post("/projects") + .content(content) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) ) .andExpect(status().isCreated()) .andExpect(jsonPath("name", is("name"))) .andExpect(jsonPath("leading_employee", is(1))) - .andExpect(jsonPath("employees", is(Arrays.asList(2, 3)))) + .andExpect(jsonPath("employees", is(List.of(312)))) .andExpect(jsonPath("contractor", is(4))) .andExpect(jsonPath("contractor_name", is("Peter File"))) .andExpect(jsonPath("comment", is("goal of project"))) @@ -70,4 +82,155 @@ class CreateProjectActionTest { assertThat(project.get().getStartDate()).isEqualTo(LocalDate.of(2000, 1, 1)); assertThat(project.get().getPlannedEndDate()).isEqualTo(LocalDate.of(2001, 1, 1)); } + + @Test + void createProjectShouldReturnConflictResponseOnConflictingProjects() throws Exception { + String content = """ + { + "name": "name", + "leading_employee": 1, + "employees": [312], + "contractor": 4, + "contractor_name": "Peter File", + "comment": "goal of project", + "start_date": "01.01.2000", + "planned_end_date": "01.01.2001" + } + """; + + this.mockMvc.perform( + post("/projects") + .content(content) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ) + .andExpect(status().isCreated()); + + this.mockMvc.perform( + post("/projects") + .content(content) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ) + .andExpect(status().isConflict()); + } + + @Test + void createProjectWithPastStartDate() throws Exception { + String content = """ + { + "name": "past project", + "leading_employee": 1, + "employees": [312], + "contractor": 4, + "contractor_name": "Past Contractor", + "comment": "past project", + "start_date": "01.01.1990", + "planned_end_date": "01.01.1991" + } + """; + + this.mockMvc.perform( + post("/projects") + .content(content) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ) + .andExpect(status().isCreated()) + .andExpect(jsonPath("name", is("past project"))) + .andExpect(jsonPath("start_date", is("01.01.1990"))) + .andExpect(jsonPath("planned_end_date", is("01.01.1991"))); + } + + @Test + void createProjectWithFutureStartDate() throws Exception { + String content = """ + { + "name": "future project", + "leading_employee": 1, + "employees": [312], + "contractor": 4, + "contractor_name": "Future Contractor", + "comment": "future project", + "start_date": "01.01.2100", + "planned_end_date": "01.01.2101" + } + """; + + this.mockMvc.perform( + post("/projects") + .content(content) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ) + .andExpect(status().isCreated()) + .andExpect(jsonPath("name", is("future project"))) + .andExpect(jsonPath("start_date", is("01.01.2100"))) + .andExpect(jsonPath("planned_end_date", is("01.01.2101"))); + } + + @Test + void createProjectWithOverlappingDates() throws Exception { + String content1 = """ + { + "name": "project 1", + "leading_employee": 1, + "employees": [312], + "contractor": 4, + "contractor_name": "Contractor 1", + "comment": "project 1", + "start_date": "01.01.2022", + "planned_end_date": "01.01.2023" + } + """; + + String content2 = """ + { + "name": "project 2", + "leading_employee": 1, + "employees": [312], + "contractor": 4, + "contractor_name": "Contractor 2", + "comment": "project 2", + "start_date": "01.06.2022", + "planned_end_date": "01.06.2023" + } + """; + + this.mockMvc.perform( + post("/projects") + .content(content1) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ) + .andExpect(status().isCreated()); + + this.mockMvc.perform( + post("/projects") + .content(content2) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getBearerToken()) + ) + .andExpect(status().isConflict()); + } + + private String getBearerToken() { + String url = "https://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("grant_type", "password"); + map.add("client_id", "employee-management-service"); + map.add("username", "user"); + map.add("password", "test"); + + HttpEntity> request = new HttpEntity<>(map, headers); + + ResponseEntity response = this.restTemplate.exchange(url, HttpMethod.POST, request, Map.class); + + return Objects.requireNonNull(response.getBody()).get("access_token").toString(); + } } + diff --git a/src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java b/src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java index 985fd2f..8b73e06 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/GetEmployeesFromProjectTest.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.*; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -23,6 +24,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class GetEmployeesFromProjectTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java index 1b6ed3f..c3d2235 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectActionTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; @@ -18,6 +19,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class GetProjectActionTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java index 769bd69..f7282ee 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/GetProjectsFromEmployeeTest.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.*; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -23,6 +24,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class GetProjectsFromEmployeeTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java deleted file mode 100644 index bda3c51..0000000 --- a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllNotAuthenticated.java +++ /dev/null @@ -1,32 +0,0 @@ -package de.szut.lf8_starter.integration.project; - -import de.szut.lf8_starter.project.ProjectRepository; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.test.web.servlet.MockMvc; - - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@AutoConfigureMockMvc(addFilters = true) -class ProjectFindAllNotAuthenticated { - @Autowired - private ProjectRepository projectRepository; - - @Autowired - private MockMvc mockMvc; - - @Autowired - private TestRestTemplate restTemplate; - - @Test - void findAllProjects() throws Exception { - this.mockMvc.perform(get("/projects")) - .andExpect(status().isUnauthorized()); - } -} diff --git a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java index ff3a642..e424a92 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/ProjectFindAllSuccessTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; @@ -18,6 +19,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class ProjectFindAllSuccessTest { @Autowired private ProjectRepository projectRepository; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java b/src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java index 7e55dd8..4008ddb 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/RemoveEmployeeFromProjectIntegrationTest.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.*; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -17,11 +18,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class RemoveEmployeeFromProjectIntegrationTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java index c2c47dd..0dd94fe 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/RemoveProjectActionTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; @@ -17,6 +18,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class RemoveProjectActionTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java b/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java index a38e233..74d889b 100644 --- a/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java +++ b/src/test/java/de/szut/lf8_starter/integration/project/UpdateProjectActionTest.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; @@ -23,6 +24,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest @AutoConfigureMockMvc(addFilters = false) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class UpdateProjectActionTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties new file mode 100644 index 0000000..f45162f --- /dev/null +++ b/src/test/resources/application-test.properties @@ -0,0 +1,6 @@ +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=create-drop \ No newline at end of file From 8c6f6301d374f5751b8c292470a707872dbbf860 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 20 Dec 2024 11:50:43 +0000 Subject: [PATCH 8/8] chore(deps): update plugin org.sonarqube to v6 (!40) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | org.sonarqube | `5.1.0.4882` -> `6.0.1.5171` | [![age](https://developer.mend.io/api/mc/badges/age/maven/org.sonarqube:org.sonarqube.gradle.plugin/6.0.1.5171?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/org.sonarqube:org.sonarqube.gradle.plugin/6.0.1.5171?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/org.sonarqube:org.sonarqube.gradle.plugin/5.1.0.4882/6.0.1.5171?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.sonarqube:org.sonarqube.gradle.plugin/5.1.0.4882/6.0.1.5171?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://git.kjan.de/jank/LF8/pulls/40 Co-authored-by: Renovate Co-committed-by: Renovate --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0488a79..80769b3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { id("org.springframework.boot") version "3.3.4" id("io.spring.dependency-management") version "1.1.6" id("checkstyle") - id("org.sonarqube") version "5.1.0.4882" + id("org.sonarqube") version "6.0.1.5171" id("jacoco") }