diff --git a/docker-compose.yml b/docker-compose.yml index e8a2a1453a0eb4455a9d4c435189aa4d1aee5fe6..e804fa00b5e05004ec5ed912348e2502313ddda9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -94,6 +94,10 @@ services: depends_on: fda-container-service: condition: service_healthy + fda-broker-service: + condition: service_healthy + fda-authentication-service: + condition: service_healthy logging: driver: json-file @@ -134,6 +138,8 @@ services: depends_on: fda-discovery-service: condition: service_healthy + fda-metadata-db: + condition: service_healthy logging: driver: json-file @@ -156,6 +162,8 @@ services: depends_on: fda-table-service: condition: service_healthy + fda-authentication-service: + condition: service_healthy logging: driver: json-file @@ -178,7 +186,11 @@ services: - /var/run/docker.sock:/var/run/docker.sock - /tmp:/tmp depends_on: - fda-database-service: + fda-authentication-service: + condition: service_healthy + fda-search-service: + condition: service_started + fda-broker-service: condition: service_healthy logging: driver: json-file @@ -199,6 +211,10 @@ services: depends_on: fda-query-service: condition: service_healthy + fda-authentication-service: + condition: service_healthy + logging: + driver: json-file fda-analyse-service: restart: on-failure diff --git a/fda-table-service/pom.xml b/fda-table-service/pom.xml index ca2be8d0b8d010009fed9daac408ece1bd813603..84fd8c287b189a413c0073c3134239a03c0c62db 100644 --- a/fda-table-service/pom.xml +++ b/fda-table-service/pom.xml @@ -28,8 +28,6 @@ <mapstruct.version>1.4.2.Final</mapstruct.version> <docker.version>3.2.7</docker.version> <testcontainers.version>1.15.2</testcontainers.version> - <swagger.version>2.1.7</swagger.version> - <springfox.version>3.0.0</springfox.version> <jacoco.version>0.8.7</jacoco.version> <opencsv.version>5.4</opencsv.version> <super-csv.version>2.4.0</super-csv.version> @@ -193,17 +191,6 @@ <artifactId>mapstruct</artifactId> <version>${mapstruct.version}</version> </dependency> - <!-- Swagger --> - <dependency> - <groupId>io.springfox</groupId> - <artifactId>springfox-boot-starter</artifactId> - <version>${springfox.version}</version> - </dependency> - <dependency> - <groupId>io.swagger</groupId> - <artifactId>swagger-codegen-maven-plugin</artifactId> - <version>2.4.21</version> - </dependency> </dependencies> <build> diff --git a/fda-table-service/rest-service/src/main/java/at/tuwien/FdaTableServiceApplication.java b/fda-table-service/rest-service/src/main/java/at/tuwien/FdaTableServiceApplication.java index 90bb253a4c549241394f0696b4b094faa9b46ab4..627cfa68a4d342b87c9828be5289fe9410cb7144 100644 --- a/fda-table-service/rest-service/src/main/java/at/tuwien/FdaTableServiceApplication.java +++ b/fda-table-service/rest-service/src/main/java/at/tuwien/FdaTableServiceApplication.java @@ -7,12 +7,10 @@ import org.springframework.data.elasticsearch.repository.config.EnableElasticsea import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; -import springfox.documentation.oas.annotations.EnableOpenApi; -@SpringBootApplication @EnableJpaAuditing -@EnableOpenApi +@SpringBootApplication @EnableTransactionManagement @EnableElasticsearchRepositories(basePackages = {"at.tuwien.repository.elastic"}) @EnableJpaRepositories(basePackages = {"at.tuwien.repository.jpa"}) diff --git a/fda-table-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java b/fda-table-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java index dad2860b67931dfb268b7500d25ecc11a791a284..c841ecbd15a14a4cb85268944a1c7946cda21bf9 100644 --- a/fda-table-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java +++ b/fda-table-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java @@ -1,40 +1,45 @@ package at.tuwien.config; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springdoc.core.GroupedOpenApi; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.oas.annotations.EnableOpenApi; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.service.Contact; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; - -import java.util.Collections; @Configuration -@EnableOpenApi public class SwaggerConfig { + @Value("${app.version:unknown}") + private String version; + @Bean - public Docket tableApi() { - return new Docket(DocumentationType.SWAGGER_2) - .apiInfo(apiInfo()) - .select() - .paths(PathSelectors.ant("/api/**")) - .build(); + public OpenAPI springShopOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("Database Repository Table Service API") + .contact(new Contact() + .name("Prof. Andreas Rauber") + .email("andreas.rauber@tuwien.ac.at")) + .description("Service that manages the tables") + .version(version) + .license(new License() + .name("Apache 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0"))) + .externalDocs(new ExternalDocumentation() + .description("Wiki Documentation") + .url("https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/wikis")); } - private ApiInfo apiInfo() { - return new ApiInfo("FDA-Table-Service API", - "Service API for table service", - "1.0", - null, - new Contact("Ao.Univ.Prof. Andreas Rauber", "http://www.ifs.tuwien.ac.at/~andi/", "rauber@ifs.tuwien.ac.at"), - "API license", - null, - Collections.emptyList()); - - + @Bean + public GroupedOpenApi publicApi() { + return GroupedOpenApi.builder() + .group("table-service") + .pathsToMatch("/api/**") + .build(); } } diff --git a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index 8af1bfe4e6cd1a95b405635c76f1bbd072f3c889..33344c261f9e99e621799a6dfd5d61a3d6e0e32d 100644 --- a/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/fda-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -6,11 +6,8 @@ import at.tuwien.exception.*; import at.tuwien.mapper.TableMapper; import at.tuwien.service.MessageQueueService; import at.tuwien.service.TableService; -import at.tuwien.service.impl.RabbitMqService; -import at.tuwien.service.impl.TableServiceImpl; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -43,11 +40,7 @@ public class TableEndpoint { @GetMapping @Transactional(readOnly = true) - @ApiOperation(value = "List all tables", notes = "Lists the tables in the metadata database for this database.") - @ApiResponses({ - @ApiResponse(code = 200, message = "All tables are listed."), - @ApiResponse(code = 401, message = "Not authorized to list all tables."), - }) + @Operation(summary = "List all tables", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<List<TableBriefDto>> findAll(@NotNull @PathVariable("id") Long id, @NotNull @PathVariable("databaseId") Long databaseId) throws DatabaseNotFoundException { @@ -60,15 +53,7 @@ public class TableEndpoint { @PostMapping @Transactional @PreAuthorize("hasRole('ROLE_RESEARCHER')") - @ApiOperation(value = "Create a table", notes = "Creates a new table for a database, requires a running container.") - @ApiResponses({ - @ApiResponse(code = 201, message = "The table was created."), - @ApiResponse(code = 400, message = "The creation form contains invalid data."), - @ApiResponse(code = 401, message = "Not authorized to create a tables."), - @ApiResponse(code = 404, message = "The database does not exist."), - @ApiResponse(code = 405, message = "The container is not running."), - @ApiResponse(code = 409, message = "The table name already exists."), - }) + @Operation(summary = "Create a table", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<TableBriefDto> create(@NotNull @PathVariable("id") Long id, @NotNull @PathVariable("databaseId") Long databaseId, @NotNull @Valid @RequestBody TableCreateDto createDto) @@ -84,12 +69,7 @@ public class TableEndpoint { @GetMapping("/{tableId}") @Transactional(readOnly = true) - @ApiOperation(value = "Get information about table", notes = "Lists the information of a table from the metadata database for this database.") - @ApiResponses({ - @ApiResponse(code = 200, message = "All tables are listed."), - @ApiResponse(code = 401, message = "Not authorized to list all tables."), - @ApiResponse(code = 404, message = "Table not found in metadata database."), - }) + @Operation(summary = "Get information about table", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<TableDto> findById(@NotNull @PathVariable("id") Long id, @NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId) @@ -103,13 +83,7 @@ public class TableEndpoint { @PutMapping("/{tableId}") @Transactional - @ApiOperation(value = "Update a table", notes = "Update a table in the database.") - @ApiResponses({ - @ApiResponse(code = 200, message = "Updated the table."), - @ApiResponse(code = 400, message = "The update form contains invalid data."), - @ApiResponse(code = 401, message = "Not authorized to update tables."), - @ApiResponse(code = 404, message = "The table is not found in database."), - }) + @Operation(summary = "Update a table", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<TableBriefDto> update(@NotNull @PathVariable("id") Long id, @NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId) { @@ -120,12 +94,7 @@ public class TableEndpoint { @DeleteMapping("/{tableId}") @Transactional @PreAuthorize("hasRole('ROLE_DEVELOPER') or hasRole('ROLE_DATA_STEWARD')") - @ApiOperation(value = "Delete a table", notes = "Delete a table in the database.") - @ApiResponses({ - @ApiResponse(code = 200, message = "Deleted the table."), - @ApiResponse(code = 401, message = "Not authorized to delete tables."), - @ApiResponse(code = 404, message = "The table is not found in database."), - }) + @Operation(summary = "Delete a table", security = @SecurityRequirement(name = "bearerAuth")) @ResponseStatus(HttpStatus.OK) public void delete(@NotNull @PathVariable("id") Long id, @NotNull @PathVariable("databaseId") Long databaseId, diff --git a/fda-table-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java b/fda-table-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java index e3e4b5fa11e1f8fa6b2b573a4d094750172fada2..ccb427157856b85c56431296a384309eef670c08 100644 --- a/fda-table-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java +++ b/fda-table-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java @@ -7,24 +7,27 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice public class ApiExceptionHandler extends ResponseEntityExceptionHandler { + @ResponseStatus(HttpStatus.NOT_ACCEPTABLE) @ExceptionHandler({AmqpException.class}) - public ResponseEntity<Object> handle(AmqpException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(AmqpException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.BAD_REQUEST) + .status(HttpStatus.NOT_ACCEPTABLE) .message(e.getLocalizedMessage()) .code("error.amqp.queue") .build(); return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler({ArbitraryPrimaryKeysException.class}) - public ResponseEntity<Object> handle(ArbitraryPrimaryKeysException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(ArbitraryPrimaryKeysException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.BAD_REQUEST) .message(e.getLocalizedMessage()) @@ -33,8 +36,9 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) @ExceptionHandler({DatabaseConnectionException.class}) - public ResponseEntity<Object> handle(DatabaseConnectionException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(DatabaseConnectionException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.METHOD_NOT_ALLOWED) .message(e.getLocalizedMessage()) @@ -43,8 +47,9 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler({DatabaseNotFoundException.class}) - public ResponseEntity<Object> handle(DatabaseNotFoundException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(DatabaseNotFoundException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.NOT_FOUND) .message(e.getLocalizedMessage()) @@ -53,8 +58,9 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({DataProcessingException.class}) - public ResponseEntity<Object> handle(DataProcessingException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(DataProcessingException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.BAD_REQUEST) .message(e.getLocalizedMessage()) @@ -63,8 +69,9 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({FileStorageException.class}) - public ResponseEntity<Object> handle(FileStorageException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(FileStorageException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.BAD_REQUEST) .message(e.getLocalizedMessage()) @@ -73,28 +80,31 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({ImageNotSupportedException.class}) - public ResponseEntity<Object> handle(ImageNotSupportedException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(ImageNotSupportedException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.CONFLICT) + .status(HttpStatus.BAD_REQUEST) .message(e.getLocalizedMessage()) .code("error.database.image") .build(); return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({PaginationException.class}) - public ResponseEntity<Object> handle(PaginationException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(PaginationException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.CONFLICT) + .status(HttpStatus.BAD_REQUEST) .message(e.getLocalizedMessage()) .code("error.database.pagination") .build(); return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.CONFLICT) @ExceptionHandler({TableNameExistsException.class}) - public ResponseEntity<Object> handle(TableNameExistsException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(TableNameExistsException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.CONFLICT) .message(e.getLocalizedMessage()) @@ -103,8 +113,9 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({TableMalformedException.class}) - public ResponseEntity<Object> handle(TableMalformedException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(TableMalformedException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.BAD_REQUEST) .message(e.getLocalizedMessage()) @@ -113,8 +124,9 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); } + @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler({TableNotFoundException.class}) - public ResponseEntity<Object> handle(TableNotFoundException e, WebRequest request) { + public ResponseEntity<ApiErrorDto> handle(TableNotFoundException e, WebRequest request) { final ApiErrorDto response = ApiErrorDto.builder() .status(HttpStatus.NOT_FOUND) .message(e.getLocalizedMessage()) diff --git a/fda-table-service/rest-service/src/main/resources/application-docker.yml b/fda-table-service/rest-service/src/main/resources/application-docker.yml index 36ea0e753886917e19caeabeafc2c24a7a12ba14..3e7c28daa20efabe84ae41610bce874d12fef741 100644 --- a/fda-table-service/rest-service/src/main/resources/application-docker.yml +++ b/fda-table-service/rest-service/src/main/resources/application-docker.yml @@ -1,3 +1,4 @@ +app.version: '@project.version@' spring: main.banner-mode: off datasource: diff --git a/fda-table-service/rest-service/src/main/resources/application.yml b/fda-table-service/rest-service/src/main/resources/application.yml index e92185997555999905688db1cf41343ad2c031cf..0bc78ce248c1e6e1297ca759bafc3ec35d35e34a 100644 --- a/fda-table-service/rest-service/src/main/resources/application.yml +++ b/fda-table-service/rest-service/src/main/resources/application.yml @@ -1,3 +1,4 @@ +app.version: '@project.version@' spring: main.banner-mode: off datasource: diff --git a/fda-table-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java b/fda-table-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java index 58a1d637f3926c247c369ee0378412bf1713f2a7..cfbd29d78c7d865755e37c3c51b8f1cd801c96ac 100644 --- a/fda-table-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java +++ b/fda-table-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java @@ -2,6 +2,8 @@ package at.tuwien.config; import at.tuwien.auth.AuthTokenFilter; import at.tuwien.gateway.AuthenticationServiceGateway; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.SecurityScheme; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -21,6 +23,12 @@ import javax.servlet.http.HttpServletResponse; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) +@SecurityScheme( + name = "bearerAuth", + type = SecuritySchemeType.HTTP, + bearerFormat = "JWT", + scheme = "bearer" +) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final AuthenticationServiceGateway authenticationServiceGateway; @@ -58,16 +66,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { http.authorizeRequests() /* our public endpoints */ .antMatchers(HttpMethod.GET, "/api/container/**/database/**/table/**").permitAll() - .antMatchers("/v2/api-docs", - "/configuration/ui", - "/swagger-resources", - "/configuration/security", - "/swagger-ui.html", - "/webjars/**", - "/swagger-resources/configuration/ui", - "/swagger-ui.html", - "/v3/api-docs/**", - "/swagger-ui/**").permitAll() + .antMatchers("/v3/api-docs.yaml", + "/v3/api-docs/**", + "/swagger-ui/**", + "/swagger-ui.html").permitAll() /* our private endpoints */ .anyRequest().authenticated(); /* add JWT token filter */ diff --git a/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java b/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java index 978329fefda3622fe6f96b0fc99e5830e3108ce4..afb4f7f792dbc95967a084487962cc00e25139f6 100644 --- a/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java +++ b/fda-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java @@ -13,19 +13,14 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.entities.database.table.columns.TableColumnType; -import at.tuwien.entities.database.table.columns.concepts.ColumnConcept; import at.tuwien.entities.database.table.columns.concepts.Concept; import at.tuwien.exception.ImageNotSupportedException; import at.tuwien.exception.TableMalformedException; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModelProperty; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; import org.mapstruct.Named; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; import java.text.Normalizer; import java.util.*; import java.util.regex.Pattern;