diff --git a/api/employee.yml b/api/employee.yml new file mode 100644 index 0000000..0682f35 --- /dev/null +++ b/api/employee.yml @@ -0,0 +1,472 @@ +openapi: 3.0.1 +info: + title: Employees Management Micro-Service + description: "\n## Overview\n\nEmployees Management Service API manages the employees\ + \ of HighTec Gmbh including their qualifications. It offers the possibility to\ + \ create, read, update and delete employees and qualifications. Existing employees\ + \ can be assigned new qualifications or have them withdrawn. \nThe API is organized\ + \ around REST. It has predictable resource-oriented URLs, accepts JSON-encoded\ + \ request bodies, returns JSON-encoded responses, uses standard HTTP response\ + \ codes and authentication.\n\n## Authentication\n\nEmployees Management Service\ + \ API uses JWTs to authenticate requests. You will receive a bearer token by making\ + \ a POST-Request in IntelliJ on:\n\n\n```\nPOST http://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token\n\ + Content-Type: application/x-www-form-urlencoded\ngrant_type=password&client_id=employee-management-service&username=user&password=test\n\ + ```\n\n\nor by CURL\n```\ncurl -X POST 'http://keycloak.szut.dev/auth/realms/szut/protocol/openid-connect/token'\n\ + --header 'Content-Type: application/x-www-form-urlencoded'\n--data-urlencode 'grant_type=password'\n\ + --data-urlencode 'client_id=employee-management-service'\n--data-urlencode 'username=user'\n\ + --data-urlencode 'password=test'\n```\n\nTo get a bearer-token in Postman, you\ + \ have to follow the instructions in \n [Postman-Documentation](https://documenter.getpostman.com/view/7294517/SzmfZHnd).\n\ + \nAll API requests must be made over HTTPS. Calls made over plain HTTP will fail.\ + \ API requests without authentication will also fail. Each request has the URL\ + \ \n `https://employee.szut.dev` and the address of the desired resource." + version: 1.0.1 +servers: +- url: "" +security: +- bearerAuth: [] +paths: + /qualifications/{id}: + put: + tags: + - qualification-controller + summary: updates a qualification + operationId: updateQualification + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QualificationPostDTO' + required: true + responses: + "401": + description: not authorized + "400": + description: invalid JSON posted + "200": + description: updated qualification + content: + application/json: + schema: + $ref: '#/components/schemas/QualificationPostDTO' + "404": + description: resource not found + delete: + tags: + - qualification-controller + summary: deletes a qualification by id + operationId: deleteQualificationByDesignation + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + responses: + "401": + description: not authorized + "403": + description: qualification is in use + "204": + description: delete successful + "404": + description: resource not found + /employees/{id}: + get: + tags: + - employee-controller + summary: find employee by id + operationId: findById + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + responses: + "401": + description: not authorized + "200": + description: employee + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeResponseDTO' + "404": + description: resource not found + put: + tags: + - employee-controller + summary: updates employee by id - only changes the fields that are posted + operationId: updateEmployee + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeRequestPutDTO' + required: true + responses: + "401": + description: not authorized + "200": + description: employee + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeResponseDTO' + "404": + description: resource not found + delete: + tags: + - employee-controller + summary: deletes a employee by id + operationId: deleteCustomer + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + responses: + "401": + description: not authorized + "204": + description: delete successful + "404": + description: resource not found + /qualifications: + get: + tags: + - qualification-controller + summary: delivers a list of all available qualifications + operationId: findAll + responses: + "401": + description: not authorized + "200": + description: list of qualifications + content: + application/json: + schema: + $ref: '#/components/schemas/QualificationPostDTO' + post: + tags: + - qualification-controller + summary: creates a new qualification with its id and designation + operationId: createQualification + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QualificationPostDTO' + required: true + responses: + "401": + description: not authorized + "201": + description: created qualification + content: + application/json: + schema: + $ref: '#/components/schemas/QualificationPostDTO' + "400": + description: invalid JSON posted + /employees: + get: + tags: + - employee-controller + summary: delivers a list of all employees + operationId: findAll_1 + responses: + "401": + description: not authorized + "200": + description: list of employees + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeResponseDTO' + post: + tags: + - employee-controller + summary: creates a new employee + operationId: createEmployee + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeRequestDTO' + required: true + responses: + "401": + description: not authorized + "400": + description: invalid JSON posted + "201": + description: created employee + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeResponseDTO' + /employees/{id}/qualifications: + get: + tags: + - employee-controller + summary: finds all qualifications of an employee by id + operationId: findAllQualificationOfAEmployeeById + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + responses: + "401": + description: not authorized + "200": + description: employee with a list of his qualifications + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeNameAndSkillDataDTO' + "404": + description: resource not found + post: + tags: + - employee-controller + summary: adds a qualification to an employee by id + operationId: addQualificationToEmployeeById + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QualificationPostDTO' + required: true + responses: + "401": + description: not authorized + "400": + description: invalid JSON posted or employee already has this qualification + "200": + description: employee with a list of his qualifications + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeNameAndSkillDataDTO' + "404": + description: resource not found + delete: + tags: + - employee-controller + summary: deletes a qualification of an employee by id + operationId: removeQualificationFromEmployee + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QualificationPostDTO' + required: true + responses: + "401": + description: not authorized + "200": + description: employee with a list of his qualifications + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeeNameAndSkillDataDTO' + "404": + description: resource not found + /qualifications/{id}/employees: + get: + tags: + - qualification-controller + summary: find employees by qualification id + operationId: findAllEmployeesByQualification + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: List of employees who have the desired qualification + content: + application/json: + schema: + $ref: '#/components/schemas/EmployeesForAQualificationDTO' + "401": + description: not authorized + "404": + description: qualification id does not exist +components: + schemas: + QualificationPostDTO: + required: + - skill + type: object + properties: + skill: + type: string + EmployeeRequestPutDTO: + type: object + properties: + lastName: + type: string + firstName: + type: string + street: + type: string + postcode: + type: string + city: + type: string + phone: + type: string + skillSet: + type: array + items: + type: integer + format: int64 + EmployeeResponseDTO: + required: + - city + - firstName + - lastName + - phone + - postcode + - street + type: object + properties: + id: + type: integer + format: int64 + lastName: + type: string + firstName: + type: string + street: + type: string + postcode: + maxLength: 5 + minLength: 5 + type: string + city: + type: string + phone: + type: string + skillSet: + type: array + items: + $ref: '#/components/schemas/QualificationGetDTO' + QualificationGetDTO: + type: object + properties: + skill: + type: string + id: + type: integer + format: int64 + EmployeeRequestDTO: + required: + - city + - firstName + - lastName + - phone + - postcode + - street + type: object + properties: + lastName: + type: string + firstName: + type: string + street: + type: string + postcode: + maxLength: 5 + minLength: 5 + type: string + city: + type: string + phone: + type: string + skillSet: + type: array + items: + type: integer + format: int64 + EmployeeNameAndSkillDataDTO: + type: object + properties: + id: + type: integer + format: int64 + lastName: + type: string + firstName: + type: string + skillSet: + uniqueItems: true + type: array + items: + $ref: '#/components/schemas/QualificationPostDTO' + EmployeeNameDataDTO: + type: object + properties: + id: + type: integer + format: int64 + lastName: + type: string + firstName: + type: string + EmployeesForAQualificationDTO: + type: object + properties: + qualification: + $ref: '#/components/schemas/QualificationGetDTO' + employees: + uniqueItems: true + type: array + items: + $ref: '#/components/schemas/EmployeeNameDataDTO' + securitySchemes: + bearerAuth: + type: http + name: bearerAuth + scheme: bearer + bearerFormat: JWT diff --git a/src/main/resources/api.yml b/api/pmt.yml similarity index 100% rename from src/main/resources/api.yml rename to api/pmt.yml diff --git a/build.gradle.kts b/build.gradle.kts index d8e2076..ad78b7f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,7 +23,7 @@ checkstyle { } spotbugs { - toolVersion = "4.8.6" + toolVersion = "4.8.6" effort.set(Effort.MAX) reportLevel.set(Confidence.LOW) } @@ -75,20 +75,27 @@ dependencies { swaggerSources { register("pmt") { - setInputFile(file("${rootDir}/src/main/resources/api.yml")) - code.configFile = file("${rootDir}/src/main/resources/gen-config.json") + setInputFile(file("${rootDir}/api/pmt.yml")) + code.configFile = file("${rootDir}/gen/config-pmt.json") val validationTask = validation code(delegateClosureOf { language = "spring" code.rawOptions = - listOf("--ignore-file-override=" + file("${rootDir}/src/main/resources/.codegen-ignore").absolutePath) + listOf("--ignore-file-override=" + file("${rootDir}/gen/.ignore-pmt").absolutePath) dependsOn(validationTask) }) } + create("employee") { + setInputFile(file("${rootDir}/api/employee.yml")) + code.configFile = file("${rootDir}/gen/config-employee.json") + code(delegateClosureOf { + language = "java" + }) + } } tasks { - + withType() withType { reports { xml.required.set(true) @@ -96,7 +103,6 @@ tasks { } } withType { - excludeFilter.set(file("${rootDir}/src/main/resources/spotbugs-exclude.xml")) } processResources { @@ -107,6 +113,7 @@ tasks { } named("compileJava").configure { dependsOn(swaggerSources.getByName("pmt").code) + dependsOn(swaggerSources.getByName("employee").code) } } @@ -114,5 +121,8 @@ sourceSets { main { java.srcDir("${swaggerSources.getByName("pmt").code.outputDir}/src/main/java") resources.srcDir("${swaggerSources.getByName("pmt").code.outputDir}/src/main/resources") + + java.srcDir("${swaggerSources.getByName("employee").code.outputDir}/src/main/java") + resources.srcDir("${swaggerSources.getByName("employee").code.outputDir}/src/main/resources") } } diff --git a/src/main/resources/.codegen-ignore b/gen/.ignore-pmt similarity index 100% rename from src/main/resources/.codegen-ignore rename to gen/.ignore-pmt diff --git a/gen/config-employee.json b/gen/config-employee.json new file mode 100644 index 0000000..8d96886 --- /dev/null +++ b/gen/config-employee.json @@ -0,0 +1,11 @@ +{ + "modelPackage": "de.hmmh.pmt.employee.dtos", + "apiPackage": "de.hmmh.pmt.employee.api", + "invokerPackage": "de.hmmh.pmt.employee", + "java8": false, + "java11": true, + "dateLibrary": "java11", + "library": "resttemplate", + "serializableModel": true, + "jakarta": true +} \ No newline at end of file diff --git a/src/main/resources/gen-config.json b/gen/config-pmt.json similarity index 100% rename from src/main/resources/gen-config.json rename to gen/config-pmt.json diff --git a/src/main/java/de/hmmh/pmt/ApiController.java b/src/main/java/de/hmmh/pmt/ApiController.java index d210430..b173e85 100644 --- a/src/main/java/de/hmmh/pmt/ApiController.java +++ b/src/main/java/de/hmmh/pmt/ApiController.java @@ -1,18 +1,25 @@ package de.hmmh.pmt; import com.fasterxml.jackson.databind.ObjectMapper; +import de.hmmh.pmt.employee.ApiClient; import de.hmmh.pmt.oas.DefaultApi; import de.hmmh.pmt.dtos.HelloOut; import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.client.RestTemplate; import java.util.Optional; @Controller @RequestMapping("${openapi.projectManagement.base-path:/api/v1}") public class ApiController implements DefaultApi { + + @Autowired + private ApiClient apiClient; @Override public Optional getObjectMapper() { diff --git a/src/main/java/de/hmmh/pmt/Config.java b/src/main/java/de/hmmh/pmt/Config.java new file mode 100644 index 0000000..392d80a --- /dev/null +++ b/src/main/java/de/hmmh/pmt/Config.java @@ -0,0 +1,13 @@ +package de.hmmh.pmt; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class Config { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/src/main/java/de/hmmh/pmt/auth/JWT.java b/src/main/java/de/hmmh/pmt/auth/JWT.java index 5360d62..a1f26c3 100644 --- a/src/main/java/de/hmmh/pmt/auth/JWT.java +++ b/src/main/java/de/hmmh/pmt/auth/JWT.java @@ -2,6 +2,7 @@ package de.hmmh.pmt.auth; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -25,7 +26,8 @@ public class JWT implements LogoutHandler { private static final String OIDC_LOGOUT_ROUTE = "/protocol/openid-connect/logout"; private static final String OIDC_TOKEN_HINT_QUERY_PARAMETER = "id_token_hin"; - private final RestTemplate template = new RestTemplate(); + @Autowired + private RestTemplate template; @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {