From 1a5a44162c0194c89acf2d55358877244b9cd54d Mon Sep 17 00:00:00 2001
From: Phan Huy Tran
Date: Wed, 2 Oct 2024 10:47:30 +0200
Subject: [PATCH] Rebase
---
build.gradle.kts | 168 +++++------
requests/getProject.http | 6 +-
requests/updateProject.http | 28 +-
.../config/OpenAPIConfiguration.java | 154 +++++-----
.../GlobalExceptionHandler.java | 118 ++++----
.../lf8_starter/hello/HelloController.java | 130 ++++----
.../lf8_starter/project/ProjectMapper.java | 124 ++++----
.../lf8_starter/project/ProjectService.java | 66 ++---
.../project/action/GetProjectAction.java | 92 +++---
.../project/action/UpdateProjectAction.java | 100 +++----
.../project/dto/UpdateProjectDto.java | 72 ++---
.../security/KeycloakLogoutHandler.java | 98 +++---
.../security/KeycloakSecurityConfig.java | 188 ++++++------
.../welcome/WelcomeController.java | 50 ++--
.../project/GetProjectActionTest.java | 118 ++++----
.../project/UpdateProjectActionTest.java | 280 +++++++++---------
16 files changed, 896 insertions(+), 896 deletions(-)
diff --git a/build.gradle.kts b/build.gradle.kts
index d15cf48..0488a79 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,85 +1,85 @@
-plugins {
- java
- 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("jacoco")
-}
-
-tasks.jacocoTestReport {
- dependsOn(tasks.test) // Ensure tests are run before generating the report
- reports {
- xml.required = true
- csv.required = true
- }
-}
-
-sonar {
- properties {
- property("sonar.projectKey", "LF8")
- property("sonar.projectName", "LF8")
- }
-}
-
-tasks.withType {
- reports {
- // Disable HTML report
- html.required.set(false)
-
- // Disable XML report
- xml.required.set(false)
- }
-}
-
-group = "de.szut"
-version = "0.0.1-SNAPSHOT"
-
-tasks.test {
- useJUnitPlatform()
-
- // Activate the 'test' profile for Spring during tests
- systemProperty("spring.profiles.active", "test")
-}
-
-java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(21)
- }
-}
-
-configurations {
- compileOnly {
- extendsFrom(configurations.annotationProcessor.get())
- }
-}
-
-repositories {
- mavenCentral()
-}
-
-val springDocVersion = "2.6.0"
-val oauth2Version = "3.3.4"
-
-dependencies {
- implementation("org.springframework.boot:spring-boot-starter-data-jpa")
- implementation("org.springframework.boot:spring-boot-starter-web")
- implementation("org.springframework.boot:spring-boot-starter-validation")
- implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$springDocVersion")
- implementation("org.springframework.boot:spring-boot-starter-security")
- implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server:$oauth2Version")
- implementation("org.springframework.boot:spring-boot-starter-oauth2-client:$oauth2Version")
-
- testImplementation("com.h2database:h2")
- testImplementation("org.springframework.boot:spring-boot-starter-test")
-
- compileOnly("org.projectlombok:lombok")
- annotationProcessor("org.projectlombok:lombok")
- testRuntimeOnly("org.junit.platform:junit-platform-launcher")
- runtimeOnly("org.postgresql:postgresql")
-}
-
-tasks.withType {
- useJUnitPlatform()
- finalizedBy(tasks.jacocoTestReport) // Run JaCoCo report after tests
+plugins {
+ java
+ 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("jacoco")
+}
+
+tasks.jacocoTestReport {
+ dependsOn(tasks.test) // Ensure tests are run before generating the report
+ reports {
+ xml.required = true
+ csv.required = true
+ }
+}
+
+sonar {
+ properties {
+ property("sonar.projectKey", "LF8")
+ property("sonar.projectName", "LF8")
+ }
+}
+
+tasks.withType {
+ reports {
+ // Disable HTML report
+ html.required.set(false)
+
+ // Disable XML report
+ xml.required.set(false)
+ }
+}
+
+group = "de.szut"
+version = "0.0.1-SNAPSHOT"
+
+tasks.test {
+ useJUnitPlatform()
+
+ // Activate the 'test' profile for Spring during tests
+ systemProperty("spring.profiles.active", "test")
+}
+
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+}
+
+configurations {
+ compileOnly {
+ extendsFrom(configurations.annotationProcessor.get())
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+val springDocVersion = "2.6.0"
+val oauth2Version = "3.3.4"
+
+dependencies {
+ implementation("org.springframework.boot:spring-boot-starter-data-jpa")
+ implementation("org.springframework.boot:spring-boot-starter-web")
+ implementation("org.springframework.boot:spring-boot-starter-validation")
+ implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$springDocVersion")
+ implementation("org.springframework.boot:spring-boot-starter-security")
+ implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server:$oauth2Version")
+ implementation("org.springframework.boot:spring-boot-starter-oauth2-client:$oauth2Version")
+
+ testImplementation("com.h2database:h2")
+ testImplementation("org.springframework.boot:spring-boot-starter-test")
+
+ compileOnly("org.projectlombok:lombok")
+ annotationProcessor("org.projectlombok:lombok")
+ testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+ runtimeOnly("org.postgresql:postgresql")
+}
+
+tasks.withType {
+ useJUnitPlatform()
+ finalizedBy(tasks.jacocoTestReport) // Run JaCoCo report after tests
}
\ No newline at end of file
diff --git a/requests/getProject.http b/requests/getProject.http
index 0bf862f..1074d4a 100644
--- a/requests/getProject.http
+++ b/requests/getProject.http
@@ -1,3 +1,3 @@
-### GET request to example server
-GET http://localhost:8080/projects/1
-Authorization: Bearer {{auth_token}}
+### GET request to example server
+GET http://localhost:8080/projects/1
+Authorization: Bearer {{auth_token}}
diff --git a/requests/updateProject.http b/requests/updateProject.http
index dfa5f0b..13bf006 100644
--- a/requests/updateProject.http
+++ b/requests/updateProject.http
@@ -1,15 +1,15 @@
-### GET request to example server
-PUT http://localhost:8080/projects/1
-Authorization: Bearer {{auth_token}}
-Content-Type: application/json
-
-{
- "name": "newName",
- "leading_employee": 2,
- "employees": [],
- "contractor": 9,
- "contractor_name": "New Contractor name",
- "comment": "new goal of project",
- "start_date": "01.01.2010",
- "planned_end_date": "01.01.2021"
+### GET request to example server
+PUT http://localhost:8080/projects/1
+Authorization: Bearer {{auth_token}}
+Content-Type: application/json
+
+{
+ "name": "newName",
+ "leading_employee": 2,
+ "employees": [],
+ "contractor": 9,
+ "contractor_name": "New Contractor name",
+ "comment": "new goal of project",
+ "start_date": "01.01.2010",
+ "planned_end_date": "01.01.2021"
}
\ No newline at end of file
diff --git a/src/main/java/de/szut/lf8_starter/config/OpenAPIConfiguration.java b/src/main/java/de/szut/lf8_starter/config/OpenAPIConfiguration.java
index 3c782d3..17f6bda 100644
--- a/src/main/java/de/szut/lf8_starter/config/OpenAPIConfiguration.java
+++ b/src/main/java/de/szut/lf8_starter/config/OpenAPIConfiguration.java
@@ -1,77 +1,77 @@
-package de.szut.lf8_starter.config;
-
-
-import io.swagger.v3.oas.models.Components;
-import io.swagger.v3.oas.models.OpenAPI;
-import io.swagger.v3.oas.models.info.Info;
-import io.swagger.v3.oas.models.security.SecurityRequirement;
-import io.swagger.v3.oas.models.security.SecurityScheme;
-import io.swagger.v3.oas.models.servers.Server;
-import jakarta.servlet.ServletContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-
-@Configuration
-public class OpenAPIConfiguration {
-
- private ServletContext context;
-
- public OpenAPIConfiguration(ServletContext context) {
- this.context = context;
- }
-
-
- @Bean
- public OpenAPI springShopOpenAPI(
- // @Value("${info.app.version}") String appVersion,
- ) {
- final String securitySchemeName = "bearerAuth";
-
- return new OpenAPI()
- .addServersItem(new Server().url(this.context.getContextPath()))
- .info(new Info()
- .title("LF8 project starter")
- .description("""
- ## Auth
-
- ## Authentication
-
- This Hello service uses JWTs to authenticate requests. You will receive a bearer token by making a POST-Request in IntelliJ on:
-
- ```
- POST http://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token
- Content-Type: application/x-www-form-urlencoded
- grant_type=password&client_id=employee-management-service&username=user&password=test
- ```
-
- or by CURL:
-
- ```
- curl -X POST 'http://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token'
- --header 'Content-Type: application/x-www-form-urlencoded'
- --data-urlencode 'grant_type=password'
- --data-urlencode 'client_id=employee-management-service'
- --data-urlencode 'username=user'
- --data-urlencode 'password=test'
- ```
-
- To get a bearer-token in Postman, you have to follow the instructions in
- [Postman-Documentation](https://documenter.getpostman.com/view/7294517/SzmfZHnd).
- """)
- .version("0.1"))
- .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
- .components(
- new Components()
- .addSecuritySchemes(securitySchemeName,
- new SecurityScheme()
- .name(securitySchemeName)
- .type(SecurityScheme.Type.HTTP)
- .scheme("bearer")
- .bearerFormat("JWT")
- )
- );
- }
-
-
-}
+package de.szut.lf8_starter.config;
+
+
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.servers.Server;
+import jakarta.servlet.ServletContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+@Configuration
+public class OpenAPIConfiguration {
+
+ private ServletContext context;
+
+ public OpenAPIConfiguration(ServletContext context) {
+ this.context = context;
+ }
+
+
+ @Bean
+ public OpenAPI springShopOpenAPI(
+ // @Value("${info.app.version}") String appVersion,
+ ) {
+ final String securitySchemeName = "bearerAuth";
+
+ return new OpenAPI()
+ .addServersItem(new Server().url(this.context.getContextPath()))
+ .info(new Info()
+ .title("LF8 project starter")
+ .description("""
+ ## Auth
+
+ ## Authentication
+
+ This Hello service uses JWTs to authenticate requests. You will receive a bearer token by making a POST-Request in IntelliJ on:
+
+ ```
+ POST http://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token
+ Content-Type: application/x-www-form-urlencoded
+ grant_type=password&client_id=employee-management-service&username=user&password=test
+ ```
+
+ or by CURL:
+
+ ```
+ curl -X POST 'http://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token'
+ --header 'Content-Type: application/x-www-form-urlencoded'
+ --data-urlencode 'grant_type=password'
+ --data-urlencode 'client_id=employee-management-service'
+ --data-urlencode 'username=user'
+ --data-urlencode 'password=test'
+ ```
+
+ To get a bearer-token in Postman, you have to follow the instructions in
+ [Postman-Documentation](https://documenter.getpostman.com/view/7294517/SzmfZHnd).
+ """)
+ .version("0.1"))
+ .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
+ .components(
+ new Components()
+ .addSecuritySchemes(securitySchemeName,
+ new SecurityScheme()
+ .name(securitySchemeName)
+ .type(SecurityScheme.Type.HTTP)
+ .scheme("bearer")
+ .bearerFormat("JWT")
+ )
+ );
+ }
+
+
+}
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 4c93a21..f6415b5 100644
--- a/src/main/java/de/szut/lf8_starter/exceptionHandling/GlobalExceptionHandler.java
+++ b/src/main/java/de/szut/lf8_starter/exceptionHandling/GlobalExceptionHandler.java
@@ -1,59 +1,59 @@
-package de.szut.lf8_starter.exceptionHandling;
-
-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 jakarta.validation.ConstraintViolationException;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.http.converter.HttpMessageNotReadableException;
-import org.springframework.web.bind.MethodArgumentNotValidException;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.context.request.WebRequest;
-
-import java.util.Date;
-
-@ControllerAdvice
-@ApiResponses(value = {
- @ApiResponse(responseCode = "500", description = "invalid JSON posted",
- content = @Content)
-})
-public class GlobalExceptionHandler {
-
- @ExceptionHandler(ResourceNotFoundException.class)
- public ResponseEntity handleHelloEntityNotFoundException(ResourceNotFoundException ex, WebRequest request) {
- ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
- return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
- }
-
- @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);
- }
-
- @ExceptionHandler(MethodArgumentNotValidException.class)
- public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, WebRequest request) {
- ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
-
- return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
- }
-
- @ExceptionHandler(HttpMessageNotReadableException.class)
- public ResponseEntity handleHttpMessageNotReadableException(HttpMessageNotReadableException ex, WebRequest request) {
- ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
-
- return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
- }
-
- @ExceptionHandler(ConstraintViolationException.class)
- public ResponseEntity handleConstraintViolationException(ConstraintViolationException ex, WebRequest request) {
- String errorMessage = ex.getConstraintViolations().stream().findFirst().get().getMessage();
-
- ErrorDetails errorDetails = new ErrorDetails(new Date(), errorMessage, request.getDescription(false));
-
- return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
- }
-}
+package de.szut.lf8_starter.exceptionHandling;
+
+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 jakarta.validation.ConstraintViolationException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.context.request.WebRequest;
+
+import java.util.Date;
+
+@ControllerAdvice
+@ApiResponses(value = {
+ @ApiResponse(responseCode = "500", description = "invalid JSON posted",
+ content = @Content)
+})
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(ResourceNotFoundException.class)
+ public ResponseEntity handleHelloEntityNotFoundException(ResourceNotFoundException ex, WebRequest request) {
+ ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
+ return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
+ }
+
+ @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);
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, WebRequest request) {
+ ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
+
+ return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler(HttpMessageNotReadableException.class)
+ public ResponseEntity handleHttpMessageNotReadableException(HttpMessageNotReadableException ex, WebRequest request) {
+ ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
+
+ return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler(ConstraintViolationException.class)
+ public ResponseEntity handleConstraintViolationException(ConstraintViolationException ex, WebRequest request) {
+ String errorMessage = ex.getConstraintViolations().stream().findFirst().get().getMessage();
+
+ ErrorDetails errorDetails = new ErrorDetails(new Date(), errorMessage, request.getDescription(false));
+
+ return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
+ }
+}
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 2ca47da..3a4a993 100644
--- a/src/main/java/de/szut/lf8_starter/hello/HelloController.java
+++ b/src/main/java/de/szut/lf8_starter/hello/HelloController.java
@@ -1,65 +1,65 @@
-package de.szut.lf8_starter.hello;
-
-
-import de.szut.lf8_starter.exceptionHandling.ResourceNotFoundException;
-import de.szut.lf8_starter.hello.dto.HelloCreateDto;
-import de.szut.lf8_starter.hello.dto.HelloGetDto;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Content;
-import io.swagger.v3.oas.annotations.media.Schema;
-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.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')")
-public class HelloController {
- private final HelloService service;
- private final HelloMapper helloMapper;
-
- public HelloController(HelloService service, HelloMapper mappingService) {
- this.service = service;
- this.helloMapper = mappingService;
- }
-
- @Operation(summary = "creates a new hello with its id and message")
- @ApiResponses(value = {
- @ApiResponse(responseCode = "201", description = "created hello",
- content = {@Content(mediaType = "application/json",
- schema = @Schema(implementation = HelloGetDto.class))}),
- @ApiResponse(responseCode = "400", description = "invalid JSON posted",
- content = @Content),
- @ApiResponse(responseCode = "401", description = "not authorized",
- content = @Content)})
- @PostMapping
- public HelloGetDto create(@RequestBody @Valid HelloCreateDto helloCreateDto) {
- HelloEntity helloEntity = this.helloMapper.mapCreateDtoToEntity(helloCreateDto);
- helloEntity = this.service.create(helloEntity);
- return this.helloMapper.mapToGetDto(helloEntity);
- }
-
- @Operation(summary = "deletes a Hello by id")
- @ApiResponses(value = {
- @ApiResponse(responseCode = "204", description = "delete successful"),
- @ApiResponse(responseCode = "401", description = "not authorized",
- content = @Content),
- @ApiResponse(responseCode = "404", description = "resource not found",
- content = @Content)})
- @DeleteMapping("/{id}")
- @ResponseStatus(code = HttpStatus.NO_CONTENT)
- public void deleteHelloById(@PathVariable long id) {
- var entity = this.service.readById(id);
- if (entity == null) {
- throw new ResourceNotFoundException("HelloEntity not found on id = " + id);
- } else {
- this.service.delete(entity);
- }
- }
-}
+package de.szut.lf8_starter.hello;
+
+
+import de.szut.lf8_starter.exceptionHandling.ResourceNotFoundException;
+import de.szut.lf8_starter.hello.dto.HelloCreateDto;
+import de.szut.lf8_starter.hello.dto.HelloGetDto;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+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.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')")
+public class HelloController {
+ private final HelloService service;
+ private final HelloMapper helloMapper;
+
+ public HelloController(HelloService service, HelloMapper mappingService) {
+ this.service = service;
+ this.helloMapper = mappingService;
+ }
+
+ @Operation(summary = "creates a new hello with its id and message")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "201", description = "created hello",
+ content = {@Content(mediaType = "application/json",
+ schema = @Schema(implementation = HelloGetDto.class))}),
+ @ApiResponse(responseCode = "400", description = "invalid JSON posted",
+ content = @Content),
+ @ApiResponse(responseCode = "401", description = "not authorized",
+ content = @Content)})
+ @PostMapping
+ public HelloGetDto create(@RequestBody @Valid HelloCreateDto helloCreateDto) {
+ HelloEntity helloEntity = this.helloMapper.mapCreateDtoToEntity(helloCreateDto);
+ helloEntity = this.service.create(helloEntity);
+ return this.helloMapper.mapToGetDto(helloEntity);
+ }
+
+ @Operation(summary = "deletes a Hello by id")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "204", description = "delete successful"),
+ @ApiResponse(responseCode = "401", description = "not authorized",
+ content = @Content),
+ @ApiResponse(responseCode = "404", description = "resource not found",
+ content = @Content)})
+ @DeleteMapping("/{id}")
+ @ResponseStatus(code = HttpStatus.NO_CONTENT)
+ public void deleteHelloById(@PathVariable long id) {
+ var entity = this.service.readById(id);
+ if (entity == null) {
+ throw new ResourceNotFoundException("HelloEntity not found on id = " + id);
+ } else {
+ this.service.delete(entity);
+ }
+ }
+}
diff --git a/src/main/java/de/szut/lf8_starter/project/ProjectMapper.java b/src/main/java/de/szut/lf8_starter/project/ProjectMapper.java
index 608233c..35b448b 100644
--- a/src/main/java/de/szut/lf8_starter/project/ProjectMapper.java
+++ b/src/main/java/de/szut/lf8_starter/project/ProjectMapper.java
@@ -1,62 +1,62 @@
-package de.szut.lf8_starter.project;
-
-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 jakarta.validation.Valid;
-import org.springframework.stereotype.Service;
-
-@Service
-public class ProjectMapper {
- public ProjectEntity mapCreateDtoToEntity(CreateProjectDto createProjectDto) {
- ProjectEntity projectEntity = new ProjectEntity();
-
- projectEntity.setName(createProjectDto.getName());
- projectEntity.setComment(createProjectDto.getComment());
- projectEntity.setLeadingEmployee(createProjectDto.getLeadingEmployee());
- projectEntity.setEmployees(createProjectDto.getEmployees());
- projectEntity.setContractor(createProjectDto.getContractor());
- projectEntity.setContractorName(createProjectDto.getContractorName());
- projectEntity.setStartDate(createProjectDto.getStartDate());
- projectEntity.setPlannedEndDate(createProjectDto.getPlannedEndDate());
- projectEntity.setEndDate(createProjectDto.getEndDate());
-
- return projectEntity;
- }
-
- public GetProjectDto mapToGetDto(ProjectEntity projectEntity) {
- GetProjectDto getProjectDto = new GetProjectDto();
-
- getProjectDto.setId(projectEntity.getId());
- getProjectDto.setName(projectEntity.getName());
- getProjectDto.setComment(projectEntity.getComment());
- getProjectDto.setLeadingEmployee(projectEntity.getLeadingEmployee());
- getProjectDto.setEmployees(projectEntity.getEmployees());
- getProjectDto.setContractor(projectEntity.getContractor());
- getProjectDto.setContractorName(projectEntity.getContractorName());
- getProjectDto.setStartDate(projectEntity.getStartDate());
- getProjectDto.setPlannedEndDate(projectEntity.getPlannedEndDate());
- getProjectDto.setEndDate(projectEntity.getEndDate());
-
- return getProjectDto;
- }
-
- public ProjectEntity mapUpdateDtoToEntity(UpdateProjectDto updateProjectDto, ProjectEntity projectEntity) {
- projectEntity.setName(updateProjectDto.getName() != null ? updateProjectDto.getName() : projectEntity.getName());
- projectEntity.setLeadingEmployee(updateProjectDto.getLeadingEmployee() != null ? updateProjectDto.getLeadingEmployee() : projectEntity.getLeadingEmployee());
- projectEntity.setContractor(updateProjectDto.getContractor() != null ? updateProjectDto.getContractor() : projectEntity.getContractor());
- projectEntity.setContractorName(updateProjectDto.getContractorName() != null ? updateProjectDto.getContractorName() : projectEntity.getContractorName());
- projectEntity.setComment(updateProjectDto.getComment() != null ? updateProjectDto.getComment() : projectEntity.getComment());
- projectEntity.setStartDate(updateProjectDto.getStartDate() != null ? updateProjectDto.getStartDate() : projectEntity.getStartDate());
- projectEntity.setPlannedEndDate(updateProjectDto.getPlannedEndDate() != null ? updateProjectDto.getPlannedEndDate() : projectEntity.getPlannedEndDate());
- projectEntity.setEndDate(updateProjectDto.getEndDate() != null ? updateProjectDto.getEndDate() : projectEntity.getEndDate());
-
- if (updateProjectDto.getEmployees() != null) {
- projectEntity.getEmployees().clear();
-
- projectEntity.setEmployees(updateProjectDto.getEmployees());
- }
-
- return projectEntity;
- }
-}
+package de.szut.lf8_starter.project;
+
+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 jakarta.validation.Valid;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ProjectMapper {
+ public ProjectEntity mapCreateDtoToEntity(CreateProjectDto createProjectDto) {
+ ProjectEntity projectEntity = new ProjectEntity();
+
+ projectEntity.setName(createProjectDto.getName());
+ projectEntity.setComment(createProjectDto.getComment());
+ projectEntity.setLeadingEmployee(createProjectDto.getLeadingEmployee());
+ projectEntity.setEmployees(createProjectDto.getEmployees());
+ projectEntity.setContractor(createProjectDto.getContractor());
+ projectEntity.setContractorName(createProjectDto.getContractorName());
+ projectEntity.setStartDate(createProjectDto.getStartDate());
+ projectEntity.setPlannedEndDate(createProjectDto.getPlannedEndDate());
+ projectEntity.setEndDate(createProjectDto.getEndDate());
+
+ return projectEntity;
+ }
+
+ public GetProjectDto mapToGetDto(ProjectEntity projectEntity) {
+ GetProjectDto getProjectDto = new GetProjectDto();
+
+ getProjectDto.setId(projectEntity.getId());
+ getProjectDto.setName(projectEntity.getName());
+ getProjectDto.setComment(projectEntity.getComment());
+ getProjectDto.setLeadingEmployee(projectEntity.getLeadingEmployee());
+ getProjectDto.setEmployees(projectEntity.getEmployees());
+ getProjectDto.setContractor(projectEntity.getContractor());
+ getProjectDto.setContractorName(projectEntity.getContractorName());
+ getProjectDto.setStartDate(projectEntity.getStartDate());
+ getProjectDto.setPlannedEndDate(projectEntity.getPlannedEndDate());
+ getProjectDto.setEndDate(projectEntity.getEndDate());
+
+ return getProjectDto;
+ }
+
+ public ProjectEntity mapUpdateDtoToEntity(UpdateProjectDto updateProjectDto, ProjectEntity projectEntity) {
+ projectEntity.setName(updateProjectDto.getName() != null ? updateProjectDto.getName() : projectEntity.getName());
+ projectEntity.setLeadingEmployee(updateProjectDto.getLeadingEmployee() != null ? updateProjectDto.getLeadingEmployee() : projectEntity.getLeadingEmployee());
+ projectEntity.setContractor(updateProjectDto.getContractor() != null ? updateProjectDto.getContractor() : projectEntity.getContractor());
+ projectEntity.setContractorName(updateProjectDto.getContractorName() != null ? updateProjectDto.getContractorName() : projectEntity.getContractorName());
+ projectEntity.setComment(updateProjectDto.getComment() != null ? updateProjectDto.getComment() : projectEntity.getComment());
+ projectEntity.setStartDate(updateProjectDto.getStartDate() != null ? updateProjectDto.getStartDate() : projectEntity.getStartDate());
+ projectEntity.setPlannedEndDate(updateProjectDto.getPlannedEndDate() != null ? updateProjectDto.getPlannedEndDate() : projectEntity.getPlannedEndDate());
+ projectEntity.setEndDate(updateProjectDto.getEndDate() != null ? updateProjectDto.getEndDate() : projectEntity.getEndDate());
+
+ if (updateProjectDto.getEmployees() != null) {
+ projectEntity.getEmployees().clear();
+
+ projectEntity.setEmployees(updateProjectDto.getEmployees());
+ }
+
+ return projectEntity;
+ }
+}
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 ef19d44..917607f 100644
--- a/src/main/java/de/szut/lf8_starter/project/ProjectService.java
+++ b/src/main/java/de/szut/lf8_starter/project/ProjectService.java
@@ -1,33 +1,33 @@
-package de.szut.lf8_starter.project;
-
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-import java.util.Optional;
-
-@Service
-public class ProjectService {
- private final ProjectRepository projectRepository;
-
- public ProjectService(ProjectRepository projectRepository) {
- this.projectRepository = projectRepository;
- }
-
- public ProjectEntity create(ProjectEntity projectEntity) {
- return this.projectRepository.save(projectEntity);
- }
-
- public List readAll() {
- return this.projectRepository.findAll();
- }
-
- public Optional findById(Long id) {
- return projectRepository.findById(id);
- }
-
- public ProjectEntity update(ProjectEntity project) {
- this.projectRepository.save(project);
-
- return project;
- }
-}
+package de.szut.lf8_starter.project;
+
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class ProjectService {
+ private final ProjectRepository projectRepository;
+
+ public ProjectService(ProjectRepository projectRepository) {
+ this.projectRepository = projectRepository;
+ }
+
+ public ProjectEntity create(ProjectEntity projectEntity) {
+ return this.projectRepository.save(projectEntity);
+ }
+
+ public List readAll() {
+ return this.projectRepository.findAll();
+ }
+
+ public Optional findById(Long id) {
+ return projectRepository.findById(id);
+ }
+
+ public ProjectEntity update(ProjectEntity project) {
+ this.projectRepository.save(project);
+
+ return project;
+ }
+}
diff --git a/src/main/java/de/szut/lf8_starter/project/action/GetProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/GetProjectAction.java
index 9b4c1e5..d8e4207 100644
--- a/src/main/java/de/szut/lf8_starter/project/action/GetProjectAction.java
+++ b/src/main/java/de/szut/lf8_starter/project/action/GetProjectAction.java
@@ -1,46 +1,46 @@
-package de.szut.lf8_starter.project.action;
-
-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 io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Content;
-import io.swagger.v3.oas.annotations.media.Schema;
-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
-@RequestMapping(value = "projects")
-public class GetProjectAction {
- private final ProjectService projectService;
- private final ProjectMapper projectMapper;
-
- public GetProjectAction(ProjectService projectService, ProjectMapper projectMapper) {
- this.projectService = projectService;
- this.projectMapper = projectMapper;
- }
-
- @Operation(summary = "Find project by ID")
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "Project found", content = {
- @Content(mediaType = "application/json", schema = @Schema(implementation = GetProjectDto.class))
- }),
- @ApiResponse(responseCode = "404", description = "Project not found", content = @Content)
- })
- @GetMapping("/{id}")
- public ResponseEntity findArticleById(@PathVariable Long id) {
- Optional project = this.projectService.findById(id);
-
- if (project.isEmpty()) {
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
-
- return new ResponseEntity<>(this.projectMapper.mapToGetDto(project.get()), HttpStatus.OK);
- }
-}
+package de.szut.lf8_starter.project.action;
+
+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 io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+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
+@RequestMapping(value = "projects")
+public class GetProjectAction {
+ private final ProjectService projectService;
+ private final ProjectMapper projectMapper;
+
+ public GetProjectAction(ProjectService projectService, ProjectMapper projectMapper) {
+ this.projectService = projectService;
+ this.projectMapper = projectMapper;
+ }
+
+ @Operation(summary = "Find project by ID")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Project found", content = {
+ @Content(mediaType = "application/json", schema = @Schema(implementation = GetProjectDto.class))
+ }),
+ @ApiResponse(responseCode = "404", description = "Project not found", content = @Content)
+ })
+ @GetMapping("/{id}")
+ public ResponseEntity findArticleById(@PathVariable Long id) {
+ Optional project = this.projectService.findById(id);
+
+ if (project.isEmpty()) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+
+ return new ResponseEntity<>(this.projectMapper.mapToGetDto(project.get()), HttpStatus.OK);
+ }
+}
diff --git a/src/main/java/de/szut/lf8_starter/project/action/UpdateProjectAction.java b/src/main/java/de/szut/lf8_starter/project/action/UpdateProjectAction.java
index e145837..9c174e3 100644
--- a/src/main/java/de/szut/lf8_starter/project/action/UpdateProjectAction.java
+++ b/src/main/java/de/szut/lf8_starter/project/action/UpdateProjectAction.java
@@ -1,50 +1,50 @@
-package de.szut.lf8_starter.project.action;
-
-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 io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Content;
-import io.swagger.v3.oas.annotations.media.Schema;
-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.*;
-
-import java.util.Optional;
-
-@RestController
-@RequestMapping(value = "/projects")
-public class UpdateProjectAction {
- private final ProjectService projectService;
- private final ProjectMapper projectMapper;
-
- public UpdateProjectAction(ProjectService projectService, ProjectMapper mappingService) {
- this.projectService = projectService;
- this.projectMapper = mappingService;
- }
-
- @Operation(summary = "Update a project by ID")
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "Project updated successfully",
- content = @Content(mediaType = "application/json", schema = @Schema(implementation = GetProjectDto.class))),
- @ApiResponse(responseCode = "404", description = "Project not found", content = @Content)
- })
- @PutMapping("/{id}")
- public ResponseEntity updateSupplier(@PathVariable Long id, @Valid @RequestBody UpdateProjectDto updateProjectDto) {
- Optional project = this.projectService.findById(id);
-
- if (project.isEmpty()) {
- return new ResponseEntity<>(HttpStatus.NOT_FOUND);
- }
-
- ProjectEntity updatedProject = this.projectMapper.mapUpdateDtoToEntity(updateProjectDto, project.get());
- this.projectService.update(updatedProject);
-
- return new ResponseEntity<>(this.projectMapper.mapToGetDto(updatedProject), HttpStatus.OK);
- }
-}
+package de.szut.lf8_starter.project.action;
+
+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 io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+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.*;
+
+import java.util.Optional;
+
+@RestController
+@RequestMapping(value = "/projects")
+public class UpdateProjectAction {
+ private final ProjectService projectService;
+ private final ProjectMapper projectMapper;
+
+ public UpdateProjectAction(ProjectService projectService, ProjectMapper mappingService) {
+ this.projectService = projectService;
+ this.projectMapper = mappingService;
+ }
+
+ @Operation(summary = "Update a project by ID")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Project updated successfully",
+ content = @Content(mediaType = "application/json", schema = @Schema(implementation = GetProjectDto.class))),
+ @ApiResponse(responseCode = "404", description = "Project not found", content = @Content)
+ })
+ @PutMapping("/{id}")
+ public ResponseEntity updateSupplier(@PathVariable Long id, @Valid @RequestBody UpdateProjectDto updateProjectDto) {
+ Optional project = this.projectService.findById(id);
+
+ if (project.isEmpty()) {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+
+ ProjectEntity updatedProject = this.projectMapper.mapUpdateDtoToEntity(updateProjectDto, project.get());
+ this.projectService.update(updatedProject);
+
+ return new ResponseEntity<>(this.projectMapper.mapToGetDto(updatedProject), HttpStatus.OK);
+ }
+}
diff --git a/src/main/java/de/szut/lf8_starter/project/dto/UpdateProjectDto.java b/src/main/java/de/szut/lf8_starter/project/dto/UpdateProjectDto.java
index c3a95f1..6d94232 100644
--- a/src/main/java/de/szut/lf8_starter/project/dto/UpdateProjectDto.java
+++ b/src/main/java/de/szut/lf8_starter/project/dto/UpdateProjectDto.java
@@ -1,36 +1,36 @@
-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 lombok.Getter;
-import lombok.Setter;
-
-import java.time.LocalDate;
-import java.util.List;
-
-@Getter
-@Setter
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class UpdateProjectDto {
- private String name;
-
- private Long leadingEmployee;
-
- private List employees;
-
- private Long contractor;
-
- private String contractorName;
-
- private String comment;
-
- @JsonFormat(pattern = "dd.MM.yyyy")
- private LocalDate startDate;
-
- @JsonFormat(pattern = "dd.MM.yyyy")
- private LocalDate plannedEndDate;
-
- @JsonFormat(pattern = "dd.MM.yyyy")
- private LocalDate endDate;
-}
+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 lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Getter
+@Setter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public class UpdateProjectDto {
+ private String name;
+
+ private Long leadingEmployee;
+
+ private List employees;
+
+ private Long contractor;
+
+ private String contractorName;
+
+ private String comment;
+
+ @JsonFormat(pattern = "dd.MM.yyyy")
+ private LocalDate startDate;
+
+ @JsonFormat(pattern = "dd.MM.yyyy")
+ private LocalDate plannedEndDate;
+
+ @JsonFormat(pattern = "dd.MM.yyyy")
+ private LocalDate endDate;
+}
diff --git a/src/main/java/de/szut/lf8_starter/security/KeycloakLogoutHandler.java b/src/main/java/de/szut/lf8_starter/security/KeycloakLogoutHandler.java
index 3051a80..b35df69 100644
--- a/src/main/java/de/szut/lf8_starter/security/KeycloakLogoutHandler.java
+++ b/src/main/java/de/szut/lf8_starter/security/KeycloakLogoutHandler.java
@@ -1,49 +1,49 @@
-package de.szut.lf8_starter.security;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.oauth2.core.oidc.user.OidcUser;
-import org.springframework.security.web.authentication.logout.LogoutHandler;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-@Slf4j
-@Component
-public class KeycloakLogoutHandler implements LogoutHandler {
-
-
- private final RestTemplate restTemplate;
-
- public KeycloakLogoutHandler(RestTemplate restTemplate) {
- this.restTemplate = restTemplate;
- }
-
- @Override
- public void logout(HttpServletRequest request, HttpServletResponse response, Authentication auth) {
- logout(auth);
- }
-
- public void logout(Authentication auth) {
- logoutFromKeycloak((OidcUser) auth.getPrincipal());
- }
-
- private void logoutFromKeycloak(OidcUser user) {
- String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";
- UriComponentsBuilder builder = UriComponentsBuilder
- .fromUriString(endSessionEndpoint)
- .queryParam("id_token_hint", user.getIdToken().getTokenValue());
-
- ResponseEntity logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class);
- if (logoutResponse.getStatusCode().is2xxSuccessful()) {
- log.info("Successfulley logged out from Keycloak");
- } else {
- log.error("Could not propagate logout to Keycloak");
- }
- }
-
-}
+package de.szut.lf8_starter.security;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@Slf4j
+@Component
+public class KeycloakLogoutHandler implements LogoutHandler {
+
+
+ private final RestTemplate restTemplate;
+
+ public KeycloakLogoutHandler(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }
+
+ @Override
+ public void logout(HttpServletRequest request, HttpServletResponse response, Authentication auth) {
+ logout(auth);
+ }
+
+ public void logout(Authentication auth) {
+ logoutFromKeycloak((OidcUser) auth.getPrincipal());
+ }
+
+ private void logoutFromKeycloak(OidcUser user) {
+ String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";
+ UriComponentsBuilder builder = UriComponentsBuilder
+ .fromUriString(endSessionEndpoint)
+ .queryParam("id_token_hint", user.getIdToken().getTokenValue());
+
+ ResponseEntity logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class);
+ if (logoutResponse.getStatusCode().is2xxSuccessful()) {
+ log.info("Successfulley logged out from Keycloak");
+ } else {
+ log.error("Could not propagate logout to Keycloak");
+ }
+ }
+
+}
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 f64bdd6..cd03823 100644
--- a/src/main/java/de/szut/lf8_starter/security/KeycloakSecurityConfig.java
+++ b/src/main/java/de/szut/lf8_starter/security/KeycloakSecurityConfig.java
@@ -1,94 +1,94 @@
-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;
-import org.springframework.security.web.session.HttpSessionEventPublisher;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-
-@Configuration
-@EnableWebSecurity
-class KeycloakSecurityConfig {
-
- private static final String REALM_ACCESS_CLAIM = "realm_access";
- private static final String ROLES_CLAIM = "roles";
-
- KeycloakSecurityConfig() {
- }
-
- @Bean
- public SessionRegistry sessionRegistry() {
- return new SessionRegistryImpl();
- }
-
- @Bean
- protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
- return new RegisterSessionAuthenticationStrategy(sessionRegistry());
- }
-
- @Bean
- public HttpSessionEventPublisher httpSessionEventPublisher() {
- return new HttpSessionEventPublisher();
- }
-
-
- @Bean
- public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
-
- http.authorizeHttpRequests(auth -> auth
- .requestMatchers(new AntPathRequestMatcher("/welcome"))
- .permitAll()
- .requestMatchers(
- new AntPathRequestMatcher("/swagger"),
- new AntPathRequestMatcher("/swagger-ui/**"),
- new AntPathRequestMatcher("/v3/api-docs/**"))
- .permitAll()
- .requestMatchers(new AntPathRequestMatcher("/hello/**"))
- .hasRole("user")
- .requestMatchers(new AntPathRequestMatcher("/roles"))
- .authenticated()
- .requestMatchers(new AntPathRequestMatcher("/"))
- .permitAll()
- .anyRequest()
- .authenticated()).oauth2ResourceServer(spec -> spec.jwt(Customizer.withDefaults()));
- return http.build();
- }
-
- @Bean
- public JwtAuthenticationConverter jwtAuthenticationConverter() {
- JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
- jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwt -> {
- List grantedAuthorities = new ArrayList<>();
-
- Map realmAccess = jwt.getClaim(REALM_ACCESS_CLAIM);
- if (realmAccess != null && realmAccess.containsKey(ROLES_CLAIM)) {
- List roles = (List) realmAccess.get(ROLES_CLAIM);
- for (String role : roles) {
- grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role));
- }
- }
-
- return grantedAuthorities;
- });
- return jwtAuthenticationConverter;
- }
-}
+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;
+import org.springframework.security.web.session.HttpSessionEventPublisher;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+@Configuration
+@EnableWebSecurity
+class KeycloakSecurityConfig {
+
+ private static final String REALM_ACCESS_CLAIM = "realm_access";
+ private static final String ROLES_CLAIM = "roles";
+
+ KeycloakSecurityConfig() {
+ }
+
+ @Bean
+ public SessionRegistry sessionRegistry() {
+ return new SessionRegistryImpl();
+ }
+
+ @Bean
+ protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
+ return new RegisterSessionAuthenticationStrategy(sessionRegistry());
+ }
+
+ @Bean
+ public HttpSessionEventPublisher httpSessionEventPublisher() {
+ return new HttpSessionEventPublisher();
+ }
+
+
+ @Bean
+ public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
+
+ http.authorizeHttpRequests(auth -> auth
+ .requestMatchers(new AntPathRequestMatcher("/welcome"))
+ .permitAll()
+ .requestMatchers(
+ new AntPathRequestMatcher("/swagger"),
+ new AntPathRequestMatcher("/swagger-ui/**"),
+ new AntPathRequestMatcher("/v3/api-docs/**"))
+ .permitAll()
+ .requestMatchers(new AntPathRequestMatcher("/hello/**"))
+ .hasRole("user")
+ .requestMatchers(new AntPathRequestMatcher("/roles"))
+ .authenticated()
+ .requestMatchers(new AntPathRequestMatcher("/"))
+ .permitAll()
+ .anyRequest()
+ .authenticated()).oauth2ResourceServer(spec -> spec.jwt(Customizer.withDefaults()));
+ return http.build();
+ }
+
+ @Bean
+ public JwtAuthenticationConverter jwtAuthenticationConverter() {
+ JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
+ jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwt -> {
+ List grantedAuthorities = new ArrayList<>();
+
+ Map realmAccess = jwt.getClaim(REALM_ACCESS_CLAIM);
+ if (realmAccess != null && realmAccess.containsKey(ROLES_CLAIM)) {
+ List roles = (List) realmAccess.get(ROLES_CLAIM);
+ for (String role : roles) {
+ grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role));
+ }
+ }
+
+ return grantedAuthorities;
+ });
+ return jwtAuthenticationConverter;
+ }
+}
diff --git a/src/main/java/de/szut/lf8_starter/welcome/WelcomeController.java b/src/main/java/de/szut/lf8_starter/welcome/WelcomeController.java
index 4effa6b..41293bc 100644
--- a/src/main/java/de/szut/lf8_starter/welcome/WelcomeController.java
+++ b/src/main/java/de/szut/lf8_starter/welcome/WelcomeController.java
@@ -1,25 +1,25 @@
-package de.szut.lf8_starter.welcome;
-
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.Collection;
-
-@RestController
-public class WelcomeController {
-
- @GetMapping("/welcome")
- public String welcome() {
- return "welcome to lf8_starter";
- }
-
- @GetMapping("/roles")
- public ResponseEntity> getRoles(Authentication authentication) {
- return ResponseEntity.ok((Collection) authentication.getAuthorities());
- }
-
-
-}
+package de.szut.lf8_starter.welcome;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Collection;
+
+@RestController
+public class WelcomeController {
+
+ @GetMapping("/welcome")
+ public String welcome() {
+ return "welcome to lf8_starter";
+ }
+
+ @GetMapping("/roles")
+ public ResponseEntity> getRoles(Authentication authentication) {
+ return ResponseEntity.ok((Collection) authentication.getAuthorities());
+ }
+
+
+}
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 c8e17cc..751d002 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
@@ -1,59 +1,59 @@
-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.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
-@AutoConfigureMockMvc(addFilters = false)
-class GetProjectActionTest {
- @Autowired
- private MockMvc mockMvc;
- @Autowired
- private ProjectRepository projectRepository;
-
- @Test
- void getProjectTest() throws Exception {
- var 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(1L, 2L, 3L));
- this.projectRepository.save(project);
-
- this.mockMvc.perform(get("/projects/1"))
- .andExpect(status().isOk())
- .andExpect(jsonPath("id").value(1))
- .andExpect(jsonPath("comment").value("comment"))
- .andExpect(jsonPath("contractor").value(1))
- .andExpect(jsonPath("contractor_name").value("contractorName"))
- .andExpect(jsonPath("end_date").value("01.01.2024"))
- .andExpect(jsonPath("leading_employee").value(1))
- .andExpect(jsonPath("name").value("name"))
- .andExpect(jsonPath("start_date").value("01.01.2021"))
- .andExpect(jsonPath("employees").isArray())
- .andExpect(jsonPath("employees", hasSize(3)));
- }
-
- @Test
- void getProjectShouldReturnNotFoundResponseWhenProjectIsNotFound() throws Exception {
- this.mockMvc.perform(get("/projects/2")).andExpect(status().isNotFound());
- }
-}
+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.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
+@AutoConfigureMockMvc(addFilters = false)
+class GetProjectActionTest {
+ @Autowired
+ private MockMvc mockMvc;
+ @Autowired
+ private ProjectRepository projectRepository;
+
+ @Test
+ void getProjectTest() throws Exception {
+ var 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(1L, 2L, 3L));
+ this.projectRepository.save(project);
+
+ this.mockMvc.perform(get("/projects/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("id").value(1))
+ .andExpect(jsonPath("comment").value("comment"))
+ .andExpect(jsonPath("contractor").value(1))
+ .andExpect(jsonPath("contractor_name").value("contractorName"))
+ .andExpect(jsonPath("end_date").value("01.01.2024"))
+ .andExpect(jsonPath("leading_employee").value(1))
+ .andExpect(jsonPath("name").value("name"))
+ .andExpect(jsonPath("start_date").value("01.01.2021"))
+ .andExpect(jsonPath("employees").isArray())
+ .andExpect(jsonPath("employees", hasSize(3)));
+ }
+
+ @Test
+ void getProjectShouldReturnNotFoundResponseWhenProjectIsNotFound() throws Exception {
+ this.mockMvc.perform(get("/projects/2")).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 5d3cf40..4e53c8c 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
@@ -1,140 +1,140 @@
-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;
-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.web.servlet.MockMvc;
-
-import java.time.LocalDate;
-import java.util.Arrays;
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
-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 UpdateProjectActionTest {
- @Autowired
- private MockMvc mockMvc;
- @Autowired
- private ProjectRepository projectRepository;
-
- @Test
- void updateProjectShouldUpdateProject() 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(1L, 2L, 3L));
- this.projectRepository.save(project);
-
- String content = """
- {
- "name": "updatedName",
- "leading_employee": 2,
- "employees": [3, 4, 5],
- "contractor": 6,
- "contractor_name": "Updated Contractor name",
- "comment": "new goal of project",
- "start_date": "01.01.2021",
- "planned_end_date": "01.01.2022"
- }
- """;
-
- final var contentAsString = this.mockMvc.perform(
- put("/projects/1").content(content).contentType(MediaType.APPLICATION_JSON)
- )
- .andExpect(status().isOk())
- .andExpect(jsonPath("name", is("updatedName")))
- .andExpect(jsonPath("leading_employee", is(2)))
- .andExpect(jsonPath("employees", is(Arrays.asList(3, 4, 5))))
- .andExpect(jsonPath("contractor", is(6)))
- .andExpect(jsonPath("contractor_name", is("Updated Contractor name")))
- .andExpect(jsonPath("comment", is("new goal of project")))
- .andExpect(jsonPath("start_date", is("01.01.2021")))
- .andExpect(jsonPath("planned_end_date", is("01.01.2022")))
- .andReturn()
- .getResponse()
- .getContentAsString();
-
- final var id = Long.parseLong(new JSONObject(contentAsString).get("id").toString());
-
- final var existingProject = this.projectRepository.findById(id);
- assertThat(existingProject.get().getName()).isEqualTo("updatedName");
- assertThat(existingProject.get().getLeadingEmployee()).isEqualTo(2);
- assertThat(existingProject.get().getContractor()).isEqualTo(6);
- assertThat(existingProject.get().getContractorName()).isEqualTo("Updated Contractor name");
- assertThat(existingProject.get().getComment()).isEqualTo("new goal of project");
- assertThat(existingProject.get().getStartDate()).isEqualTo(LocalDate.of(2021, 1, 1));
- assertThat(existingProject.get().getPlannedEndDate()).isEqualTo(LocalDate.of(2022, 1, 1));
-
- }
-
- @Test
- void updateProjectShouldUpdateProjectPartially() throws Exception {
- ProjectEntity project = new ProjectEntity();
- project.setId(1);
- project.setName("name");
- project.setLeadingEmployee(1);
- project.setContractor(1);
- project.setComment("comment");
- project.setEmployees(List.of(1L, 2L, 3L));
- project.setContractorName("contractorName");
- project.setStartDate(LocalDate.of(2021, 1, 1));
- project.setPlannedEndDate(LocalDate.of(2023, 1, 1));
- project.setEndDate(LocalDate.of(2024, 1, 1));
- this.projectRepository.save(project);
-
- String content = """
- {}
- """;
-
- final var 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("contractor", is(1)))
- .andExpect(jsonPath("contractor_name", is("contractorName")))
- .andExpect(jsonPath("comment", is("comment")))
- .andExpect(jsonPath("start_date", is("01.01.2021")))
- .andExpect(jsonPath("planned_end_date", is("01.01.2023")))
- .andExpect(jsonPath("end_date", is("01.01.2024")))
- .andReturn()
- .getResponse()
- .getContentAsString();
-
- final var id = Long.parseLong(new JSONObject(contentAsString).get("id").toString());
-
- final var existingProject = this.projectRepository.findById(id);
- assertThat(existingProject.get().getName()).isEqualTo("name");
- assertThat(existingProject.get().getLeadingEmployee()).isEqualTo(1);
- assertThat(existingProject.get().getContractor()).isEqualTo(1);
- assertThat(existingProject.get().getContractorName()).isEqualTo("contractorName");
- assertThat(existingProject.get().getComment()).isEqualTo("comment");
- assertThat(existingProject.get().getStartDate()).isEqualTo(LocalDate.of(2021, 1, 1));
- assertThat(existingProject.get().getPlannedEndDate()).isEqualTo(LocalDate.of(2023, 1, 1));
- assertThat(existingProject.get().getEndDate()).isEqualTo(LocalDate.of(2024, 1, 1));
- }
-
- @Test
- void updateProjectShouldReturnNotFoundResponseWhenProjectIsNotFound() throws Exception {
- this.mockMvc.perform(put("/projects/2").content("{}").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isNotFound());
- }
-}
+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;
+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.web.servlet.MockMvc;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+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 UpdateProjectActionTest {
+ @Autowired
+ private MockMvc mockMvc;
+ @Autowired
+ private ProjectRepository projectRepository;
+
+ @Test
+ void updateProjectShouldUpdateProject() 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(1L, 2L, 3L));
+ this.projectRepository.save(project);
+
+ String content = """
+ {
+ "name": "updatedName",
+ "leading_employee": 2,
+ "employees": [3, 4, 5],
+ "contractor": 6,
+ "contractor_name": "Updated Contractor name",
+ "comment": "new goal of project",
+ "start_date": "01.01.2021",
+ "planned_end_date": "01.01.2022"
+ }
+ """;
+
+ final var contentAsString = this.mockMvc.perform(
+ put("/projects/1").content(content).contentType(MediaType.APPLICATION_JSON)
+ )
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("name", is("updatedName")))
+ .andExpect(jsonPath("leading_employee", is(2)))
+ .andExpect(jsonPath("employees", is(Arrays.asList(3, 4, 5))))
+ .andExpect(jsonPath("contractor", is(6)))
+ .andExpect(jsonPath("contractor_name", is("Updated Contractor name")))
+ .andExpect(jsonPath("comment", is("new goal of project")))
+ .andExpect(jsonPath("start_date", is("01.01.2021")))
+ .andExpect(jsonPath("planned_end_date", is("01.01.2022")))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ final var id = Long.parseLong(new JSONObject(contentAsString).get("id").toString());
+
+ final var existingProject = this.projectRepository.findById(id);
+ assertThat(existingProject.get().getName()).isEqualTo("updatedName");
+ assertThat(existingProject.get().getLeadingEmployee()).isEqualTo(2);
+ assertThat(existingProject.get().getContractor()).isEqualTo(6);
+ assertThat(existingProject.get().getContractorName()).isEqualTo("Updated Contractor name");
+ assertThat(existingProject.get().getComment()).isEqualTo("new goal of project");
+ assertThat(existingProject.get().getStartDate()).isEqualTo(LocalDate.of(2021, 1, 1));
+ assertThat(existingProject.get().getPlannedEndDate()).isEqualTo(LocalDate.of(2022, 1, 1));
+
+ }
+
+ @Test
+ void updateProjectShouldUpdateProjectPartially() throws Exception {
+ ProjectEntity project = new ProjectEntity();
+ project.setId(1);
+ project.setName("name");
+ project.setLeadingEmployee(1);
+ project.setContractor(1);
+ project.setComment("comment");
+ project.setEmployees(List.of(1L, 2L, 3L));
+ project.setContractorName("contractorName");
+ project.setStartDate(LocalDate.of(2021, 1, 1));
+ project.setPlannedEndDate(LocalDate.of(2023, 1, 1));
+ project.setEndDate(LocalDate.of(2024, 1, 1));
+ this.projectRepository.save(project);
+
+ String content = """
+ {}
+ """;
+
+ final var 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("contractor", is(1)))
+ .andExpect(jsonPath("contractor_name", is("contractorName")))
+ .andExpect(jsonPath("comment", is("comment")))
+ .andExpect(jsonPath("start_date", is("01.01.2021")))
+ .andExpect(jsonPath("planned_end_date", is("01.01.2023")))
+ .andExpect(jsonPath("end_date", is("01.01.2024")))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ final var id = Long.parseLong(new JSONObject(contentAsString).get("id").toString());
+
+ final var existingProject = this.projectRepository.findById(id);
+ assertThat(existingProject.get().getName()).isEqualTo("name");
+ assertThat(existingProject.get().getLeadingEmployee()).isEqualTo(1);
+ assertThat(existingProject.get().getContractor()).isEqualTo(1);
+ assertThat(existingProject.get().getContractorName()).isEqualTo("contractorName");
+ assertThat(existingProject.get().getComment()).isEqualTo("comment");
+ assertThat(existingProject.get().getStartDate()).isEqualTo(LocalDate.of(2021, 1, 1));
+ assertThat(existingProject.get().getPlannedEndDate()).isEqualTo(LocalDate.of(2023, 1, 1));
+ assertThat(existingProject.get().getEndDate()).isEqualTo(LocalDate.of(2024, 1, 1));
+ }
+
+ @Test
+ void updateProjectShouldReturnNotFoundResponseWhenProjectIsNotFound() throws Exception {
+ this.mockMvc.perform(put("/projects/2").content("{}").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isNotFound());
+ }
+}