diff --git a/.run/Test.run.xml b/.run/Test.run.xml
index e33b35f..667c096 100644
--- a/.run/Test.run.xml
+++ b/.run/Test.run.xml
@@ -1,24 +1,12 @@
-
-
-
-
-
-
-
-
-
-
- true
- true
- false
- false
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api/pmt.yml b/api/pmt.yml
index 4640111..ba7ebd1 100644
--- a/api/pmt.yml
+++ b/api/pmt.yml
@@ -30,16 +30,16 @@ components:
CreateProjectDTO:
type: object
properties:
+ name:
+ type: string
+ goal:
+ type: string
customerId:
type: integer
format: int64
administratorId:
type: integer
format: int64
- title:
- type: string
- goal:
- type: string
start:
type: string
format: date-time
@@ -52,16 +52,16 @@ components:
id:
type: integer
format: int64
+ name:
+ type: string
+ goal:
+ type: string
customerId:
type: integer
format: int64
administratorId:
type: integer
format: int64
- title:
- type: string
- goal:
- type: string
start:
type: string
format: date-time
@@ -69,14 +69,8 @@ components:
type: string
format: date-time
responses:
- UnAuthorized:
- description: "Un Authorized"
- InternalError:
- description: "Internal Server Error"
- content:
- text/plain:
- schema:
- type: string
+ Unauthorized:
+ description: "Unauthorized"
NotFound:
description: "Not Found"
content:
@@ -89,6 +83,24 @@ components:
text/plain:
schema:
type: string
+ UnprocessableContent:
+ description: "Unprocessable Content"
+ content:
+ text/plain:
+ schema:
+ type: string
+ InternalError:
+ description: "Internal Server Error"
+ content:
+ text/plain:
+ schema:
+ type: string
+ ServiceUnavailable:
+ description: "Service Unavailable"
+ content:
+ text/plain:
+ schema:
+ type: string
paths:
/project:
get:
@@ -102,7 +114,7 @@ paths:
schema:
$ref: "#/components/schemas/GetAllProjectsDTO"
401:
- $ref: "#/components/responses/UnAuthorized"
+ $ref: "#/components/responses/Unauthorized"
500:
$ref: "#/components/responses/InternalError"
post:
@@ -121,10 +133,39 @@ paths:
schema:
$ref: "#/components/schemas/CreatedProjectDTO"
401:
- $ref: "#/components/responses/UnAuthorized"
+ $ref: "#/components/responses/Unauthorized"
404:
$ref: "#/components/responses/NotFound"
409:
- $ref: "#/components/responses/NotFound"
+ $ref: "#/components/responses/Conflict"
+ 422:
+ $ref: "#/components/responses/UnprocessableContent"
500:
- $ref: "#/components/responses/InternalError"
\ No newline at end of file
+ $ref: "#/components/responses/InternalError"
+ 503:
+ $ref: "#/components/responses/ServiceUnavailable"
+ /project/{id}:
+ delete:
+ operationId: "deleteProject"
+ description: "Delete a specific Project"
+ parameters:
+ - in: path
+ name: id
+ schema:
+ type: integer
+ format: int64
+ required: true
+ responses:
+ 204:
+ description: "Deletes a specific Project"
+ 401:
+ $ref: "#/components/responses/Unauthorized"
+ 404:
+ description: "Project not found"
+ content:
+ text/plain:
+ schema:
+ type: string
+ 500:
+ $ref: "#/components/responses/InternalError"
+
diff --git a/gen/config-pmt.json b/gen/config-pmt.json
index c2d6f24..80ab789 100644
--- a/gen/config-pmt.json
+++ b/gen/config-pmt.json
@@ -4,7 +4,7 @@
"invokerPackage": "de.hmmh.pmt",
"java8": false,
"java11": true,
- "dateLibrary": "java11",
+ "dateLibrary": "java8-localdatetime",
"library": "spring-boot3",
"defaultInterfaces": false,
"serializableModel": true
diff --git a/src/main/java/de/hmmh/pmt/ApiController.java b/src/main/java/de/hmmh/pmt/ApiController.java
index b28479e..51799b5 100644
--- a/src/main/java/de/hmmh/pmt/ApiController.java
+++ b/src/main/java/de/hmmh/pmt/ApiController.java
@@ -1,20 +1,22 @@
package de.hmmh.pmt;
import com.fasterxml.jackson.databind.ObjectMapper;
-import de.hmmh.pmt.dtos.CreateProjectDTO;
-import de.hmmh.pmt.dtos.CreatedProjectDTO;
-import de.hmmh.pmt.employee.ApiClientFactory;
import de.hmmh.pmt.db.Project;
import de.hmmh.pmt.db.ProjectRepository;
-import de.hmmh.pmt.oas.DefaultApi;
+import de.hmmh.pmt.dtos.CreateProjectDTO;
+import de.hmmh.pmt.dtos.CreatedProjectDTO;
import de.hmmh.pmt.dtos.GetAllProjectsDTO;
import de.hmmh.pmt.dtos.ProjectInfo;
+import de.hmmh.pmt.employee.ApiClientFactory;
+import de.hmmh.pmt.oas.DefaultApi;
+import de.hmmh.pmt.util.Mapper;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import java.util.Optional;
@@ -22,6 +24,8 @@ import java.util.Optional;
@Controller
@RequestMapping("${openapi.projectManagement.base-path:/api/v1}")
public class ApiController implements DefaultApi {
+ @Autowired
+ private Mapper mapper;
@Autowired
private ApiClientFactory apiClientFactory;
@Autowired
@@ -37,11 +41,21 @@ public class ApiController implements DefaultApi {
return Optional.empty();
}
+ @Override
+ public ResponseEntity deleteProject(Long id) {
+ if (!projectRepository.existsById(id)) {
+ return ResponseEntity.notFound().build();
+ }
+
+ projectRepository.deleteById(id);
+ return ResponseEntity.noContent().build();
+ }
+
@Override
public ResponseEntity getAllProjects() {
GetAllProjectsDTO response = new GetAllProjectsDTO();
- for (Project project : this.projectRepository.findAll()){
+ for (Project project : this.projectRepository.findAll()) {
ProjectInfo projectInfo = new ProjectInfo();
projectInfo.setId(project.getId());
projectInfo.setName(project.getName());
@@ -53,17 +67,29 @@ public class ApiController implements DefaultApi {
@Override
public ResponseEntity createProject(CreateProjectDTO body) {
- if (projectRepository.existsByName(body.getTitle())){
+ if (projectRepository.existsByName(body.getName())) {
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
- try {
- if (apiClientFactory.getEmployeeApi().findById(body.getAdministratorId()).getId() == body.getAdministratorId()){
- }
- } catch (RestClientException exception){
- return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
- }
+ try {
+ apiClientFactory.getEmployeeApi().findById(body.getAdministratorId());
+ } catch (HttpClientErrorException exception) {
+ return new ResponseEntity<>(
+ exception.getStatusCode().equals(HttpStatus.NOT_FOUND)
+ ? HttpStatus.NOT_FOUND
+ : HttpStatus.SERVICE_UNAVAILABLE
+ );
+ } catch (RestClientException exception) {
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
- return null;
+ Project project = mapper.map(body);
+ if (!project.isValid()) {
+ return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY);
+ }
+ projectRepository.save(project);
+
+ CreatedProjectDTO response = mapper.map(project);
+ return new ResponseEntity<>(response, HttpStatus.CREATED);
}
}
diff --git a/src/main/java/de/hmmh/pmt/db/Project.java b/src/main/java/de/hmmh/pmt/db/Project.java
index 9c9891f..0a70189 100644
--- a/src/main/java/de/hmmh/pmt/db/Project.java
+++ b/src/main/java/de/hmmh/pmt/db/Project.java
@@ -1,6 +1,9 @@
package de.hmmh.pmt.db;
import jakarta.persistence.*;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@@ -10,6 +13,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
+import java.util.Set;
@NoArgsConstructor
@AllArgsConstructor
@@ -19,7 +23,7 @@ import java.time.LocalDateTime;
@Table(name = "project")
public class Project {
@Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@@ -43,5 +47,16 @@ public class Project {
private LocalDateTime plannedEnd;
private LocalDateTime realEnd; // Cant be named just "end" because it's and SQL Keyword
+
+
+ public boolean isValid() {
+ Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+ Set> violations = validator.validate(this);
+
+ return violations.isEmpty() &&
+ plannedEnd.isAfter(start) &&
+ (realEnd == null || realEnd.isAfter(start));
+ }
+
}
diff --git a/src/main/java/de/hmmh/pmt/util/Mapper.java b/src/main/java/de/hmmh/pmt/util/Mapper.java
new file mode 100644
index 0000000..d5d7591
--- /dev/null
+++ b/src/main/java/de/hmmh/pmt/util/Mapper.java
@@ -0,0 +1,32 @@
+package de.hmmh.pmt.util;
+
+import de.hmmh.pmt.db.Project;
+import de.hmmh.pmt.dtos.CreateProjectDTO;
+import de.hmmh.pmt.dtos.CreatedProjectDTO;
+import org.springframework.stereotype.Component;
+
+@Component
+public class Mapper {
+ public Project map(CreateProjectDTO dto) {
+ Project project = new Project();
+ project.setName(dto.getName());
+ project.setGoal(dto.getGoal());
+ project.setCustomerId(dto.getCustomerId());
+ project.setAdministratorId(dto.getAdministratorId());
+ project.setStart(dto.getStart());
+ project.setPlannedEnd(dto.getPlannedEnd());
+ return project;
+ }
+
+ public CreatedProjectDTO map(Project project) {
+ CreatedProjectDTO dto = new CreatedProjectDTO();
+ dto.setId(project.getId());
+ dto.setName(project.getName());
+ dto.setGoal(project.getGoal());
+ dto.setCustomerId(project.getCustomerId());
+ dto.setAdministratorId(project.getAdministratorId());
+ dto.setStart(project.getStart());
+ dto.setPlannedEnd(project.getPlannedEnd());
+ return dto;
+ }
+}
diff --git a/src/test/java/de/hmmh/pmt/IntegrationTest.java b/src/test/java/de/hmmh/pmt/IntegrationTest.java
index 1bc1a0c..eb59826 100644
--- a/src/test/java/de/hmmh/pmt/IntegrationTest.java
+++ b/src/test/java/de/hmmh/pmt/IntegrationTest.java
@@ -1,17 +1,22 @@
package de.hmmh.pmt;
+import com.fasterxml.jackson.databind.ObjectMapper;
import de.hmmh.pmt.db.Project;
import de.hmmh.pmt.db.ProjectRepository;
+import de.hmmh.pmt.employee.api.EmployeeControllerApi;
+import de.hmmh.pmt.employee.api.QualificationControllerApi;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDateTime;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@@ -23,8 +28,15 @@ public abstract class IntegrationTest {
@Autowired
protected MockMvc mvc;
@Autowired
+ protected ObjectMapper objectMapper;
+ @Autowired
protected ProjectRepository projectRepository;
+ @MockBean
+ protected EmployeeControllerApi mockEmployeeApi;
+ @MockBean
+ protected QualificationControllerApi mockQualificationApi;
+
@BeforeEach
void setUp() {
projectRepository.deleteAll();
@@ -35,70 +47,65 @@ public abstract class IntegrationTest {
projectRepository.deleteAll();
}
- protected List createTestProjectData() {
- Project project1 = new Project(
- 1L,
- "Build a Dream Space Station",
- "Launch a self-sustaining space habitat!",
- 42L,
- 101L,
- LocalDateTime.of(2024, 3, 1, 10, 0),
- LocalDateTime.of(2028, 6, 30, 18, 0),
- LocalDateTime.of(2029, 12, 15, 16, 30)
- );
- Project project2 = new Project(
- 2L,
- "Underwater Research Lab",
- "Discover new marine species!",
- 73L,
- 202L,
- LocalDateTime.of(2025, 5, 22, 8, 45),
- LocalDateTime.of(2027, 11, 5, 17, 0),
- LocalDateTime.of(2027, 10, 20, 14, 0)
- );
- Project project3 = new Project(
- 3L,
- "AI-Powered Smart City",
- "Create the world's most advanced smart city!",
- 89L,
- 303L,
- LocalDateTime.of(2026, 9, 14, 12, 0),
- LocalDateTime.of(2030, 4, 1, 9, 30),
- LocalDateTime.of(2030, 5, 2, 15, 0)
- );
- Project project4 = new Project(
- 4L,
- "Renewable Energy Revolution",
- "Replace all fossil fuels with renewables!",
- 56L,
- 404L,
- LocalDateTime.of(2023, 7, 19, 11, 30),
- LocalDateTime.of(2029, 12, 31, 20, 0),
- LocalDateTime.of(2029, 10, 5, 18, 45)
- );
- Project project5 = new Project(
- 5L,
- "Virtual Reality Theme Park",
- "Build a fully immersive VR theme park!",
- 99L,
- 505L,
- LocalDateTime.of(2024, 2, 28, 9, 15),
- LocalDateTime.of(2026, 9, 30, 17, 0),
- LocalDateTime.of(2026, 8, 15, 13, 45)
- );
+ protected Map createTestProjectData() {
+ Map projects = new HashMap<>();
+
+ Project researchLabProject = new Project();
+ researchLabProject.setName("Underwater Research Lab");
+ researchLabProject.setGoal( "Discover new marine species!");
+ researchLabProject.setCustomerId(73L);
+ researchLabProject.setAdministratorId(202L);
+ researchLabProject.setStart(LocalDateTime.of(2025, 5, 22, 8, 45));
+ researchLabProject.setPlannedEnd(LocalDateTime.of(2026, 5, 22, 8, 45));
+ projects.put("research-lab", researchLabProject);
+
+ Project spaceStationProject = new Project();
+ spaceStationProject.setName("International Space Station Expansion");
+ spaceStationProject.setGoal("Build new modules for extended research.");
+ spaceStationProject.setCustomerId(85L);
+ spaceStationProject.setAdministratorId(150L);
+ spaceStationProject.setStart(LocalDateTime.of(2024, 10, 15, 10, 0));
+ spaceStationProject.setPlannedEnd(LocalDateTime.of(2025, 12, 15, 10, 0));
+ projects.put("space-station", spaceStationProject);
+
+ Project aiResearchProject = new Project();
+ aiResearchProject.setName("AI Human Interaction Study");
+ aiResearchProject.setGoal("Study AI interactions in healthcare.");
+ aiResearchProject.setCustomerId(63L);
+ aiResearchProject.setAdministratorId(180L);
+ aiResearchProject.setStart(LocalDateTime.of(2023, 11, 3, 9, 30));
+ aiResearchProject.setPlannedEnd(LocalDateTime.of(2024, 6, 3, 9, 30));
+ projects.put("ai-research", aiResearchProject);
+
+ Project renewableEnergyProject = new Project();
+ renewableEnergyProject.setName("Renewable Energy Storage System");
+ renewableEnergyProject.setGoal("Develop new battery tech for solar power.");
+ renewableEnergyProject.setCustomerId(90L);
+ renewableEnergyProject.setAdministratorId(175L);
+ renewableEnergyProject.setStart(LocalDateTime.of(2024, 1, 10, 11, 0));
+ renewableEnergyProject.setPlannedEnd(LocalDateTime.of(2025, 1, 10, 11, 0));
+ projects.put("renewable-energy", renewableEnergyProject);
+
+ Project climateChangeProject = new Project();
+ climateChangeProject.setName("Climate Change Impact Analysis");
+ climateChangeProject.setGoal("Study global warming effects on ecosystems.");
+ climateChangeProject.setCustomerId(72L);
+ climateChangeProject.setAdministratorId(190L);
+ climateChangeProject.setStart(LocalDateTime.of(2025, 3, 12, 8, 0));
+ climateChangeProject.setPlannedEnd(LocalDateTime.of(2026, 3, 12, 8, 0));
+ projects.put("climate change", climateChangeProject);
+
+
+ Project medicalResearchProject = new Project();
+ medicalResearchProject.setName("Cancer Treatment Innovation");
+ medicalResearchProject.setGoal("Develop new gene therapy techniques.");
+ medicalResearchProject.setCustomerId(95L);
+ medicalResearchProject.setAdministratorId(155L);
+ medicalResearchProject.setStart(LocalDateTime.of(2024, 8, 20, 9, 15));
+ medicalResearchProject.setPlannedEnd(LocalDateTime.of(2026, 8, 20, 9, 15));
+ projects.put("medical-research", medicalResearchProject);
- projectRepository.save(project1);
- projectRepository.save(project2);
- projectRepository.save(project3);
- projectRepository.save(project4);
- projectRepository.save(project5);
-
- return List.of(
- project1,
- project2,
- project3,
- project4,
- project5
- );
+ projectRepository.saveAllAndFlush(projects.values());
+ return projects;
}
}
diff --git a/src/test/java/de/hmmh/pmt/project/CreateTest.java b/src/test/java/de/hmmh/pmt/project/CreateTest.java
new file mode 100644
index 0000000..2f62f0b
--- /dev/null
+++ b/src/test/java/de/hmmh/pmt/project/CreateTest.java
@@ -0,0 +1,44 @@
+package de.hmmh.pmt.project;
+
+import de.hmmh.pmt.IntegrationTest;
+import de.hmmh.pmt.dtos.CreateProjectDTO;
+import de.hmmh.pmt.employee.dtos.EmployeeResponseDTO;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.RequestBuilder;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import java.time.LocalDateTime;
+
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class CreateTest extends IntegrationTest {
+
+ @Test
+ void successfullyCreate() throws Exception {
+ Mockito
+ .when(this.mockEmployeeApi.findById(Mockito.anyLong()))
+ .thenReturn(new EmployeeResponseDTO());
+
+ CreateProjectDTO createDTO = new CreateProjectDTO();
+ createDTO.setName("Test");
+ createDTO.setGoal("A Test Goal");
+ createDTO.setCustomerId(10L);
+ createDTO.setAdministratorId(10L);
+ createDTO.setStart(LocalDateTime.of(2000, 1, 13, 12, 51));
+ createDTO.setPlannedEnd(LocalDateTime.of(2002, 3, 21, 11, 42));
+
+ RequestBuilder requestBuilder = MockMvcRequestBuilders
+ .post(baseUri + "/project")
+ .accept(MediaType.APPLICATION_JSON)
+ .content(this.objectMapper.writeValueAsString(createDTO))
+ .contentType(MediaType.APPLICATION_JSON);
+
+ this.mvc
+ .perform(requestBuilder)
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.id").exists());
+ }
+}
diff --git a/src/test/java/de/hmmh/pmt/project/DeleteTest.java b/src/test/java/de/hmmh/pmt/project/DeleteTest.java
new file mode 100644
index 0000000..25a95ba
--- /dev/null
+++ b/src/test/java/de/hmmh/pmt/project/DeleteTest.java
@@ -0,0 +1,31 @@
+package de.hmmh.pmt.project;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+import de.hmmh.pmt.IntegrationTest;
+import de.hmmh.pmt.db.Project;
+
+public class DeleteTest extends IntegrationTest {
+ @Test
+ void projectNotFound() throws Exception {
+ mvc
+ .perform(delete(baseUri + "/project/1"))
+ .andExpect(status().isNotFound())
+ ;
+ }
+
+ @Test
+ void deletedSuccessfully() throws Exception {
+ Map allProjects = createTestProjectData();
+ mvc
+ .perform(delete(baseUri + "/project/" + allProjects.get("space-station").getId()))
+ .andExpect(status().isNoContent())
+ ;
+ }
+}
diff --git a/src/test/java/de/hmmh/pmt/project/GetAllTest.java b/src/test/java/de/hmmh/pmt/project/GetAllTest.java
index ea8fb84..b71ecf0 100644
--- a/src/test/java/de/hmmh/pmt/project/GetAllTest.java
+++ b/src/test/java/de/hmmh/pmt/project/GetAllTest.java
@@ -5,7 +5,7 @@ import de.hmmh.pmt.IntegrationTest;
import de.hmmh.pmt.db.Project;
import org.junit.jupiter.api.Test;
-import java.util.List;
+import java.util.Map;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
@@ -25,7 +25,7 @@ public class GetAllTest extends IntegrationTest {
@Test
void multipleProjects() throws Exception {
- List allProjects = createTestProjectData();
+ Map allProjects = createTestProjectData();
mvc
.perform(get(baseUri + "/project"))