diff --git a/.docs/api/ui.md b/.docs/api/ui.md index 4ac5c7bad871284cc322bf7be0c8585942f63ea5..cfffcebc0f733e6569b88f8dc3a31b867cc745e5 100644 --- a/.docs/api/ui.md +++ b/.docs/api/ui.md @@ -20,26 +20,67 @@ image as well, in this example we want to mount a custom logo `my_logo.png` into <figcaption>Figure 1: Architecture of the UI microservice</figcaption> </figure> -Text values like the version :material-numeric-2-circle-outline: and title :material-numeric-3-circle-outline: can be -configured as well via the Nuxt runtime configuration through single environment variables or `.env` files - -```yaml title=".env" -NUXT_PUBLIC_TITLE="My overriden title" -NUXT_PUBLIC_LOGO="/app/.output/public/my_logo.png" -... -``` - -To work, you need to mount the `my_logo.png` file into the `dbrepo-ui` container via the `docker-compose.yml` file (or -if you use a Kubernetes deployment via ConfigMap and Volumes). - -```yaml title="docker-compose.yml" -services: - dbrepo-ui: - image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.4.5 - volumes: - - ./my_logo.png:/app/.output/public/my_logo.png - ... -``` +=== "Docker Compose" + + Text values like the version :material-numeric-2-circle-outline: and title :material-numeric-3-circle-outline: can be + configured as well via the Nuxt runtime configuration through single environment variables or `.env` files. + + ```yaml title=".env" + NUXT_PUBLIC_TITLE="My overriden title" + NUXT_PUBLIC_LOGO="/my_logo.png" + NUXT_PUBLIC_ICON="/favicon.ico" + ... + ``` + + To work, you need to mount the `my_logo.png` file into the `dbrepo-ui` container via the `docker-compose.yml` file. + + ```yaml title="docker-compose.yml" + services: + dbrepo-ui: + image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.4.5 + volumes: + - ./my_logo.png:/app/.output/public/my_logo.png + - ./favicon.ico:/app/.output/public/favicon.ico + environment: + ... + ... + ``` + + If you want to override more environment variables, extend the dictionary in `environment:` + +=== "Kubernetes" + + Text values like the version :material-numeric-2-circle-outline: and title :material-numeric-3-circle-outline: can be + configured as well via the Nuxt runtime configuration through setting the variables in the `values.yaml` file. + + ```yaml title="values.yaml" + ui: + public: + logo: "/my_logo.png" + icon: "/favicon.ico" + extraVolumes: + - name: images-map + configMap: + name: ui-config + extraVolumeMounts: + - name: images-map + mountPath: /static/ + ``` + + To work, you need to mount the `my_logo.png` file into the dbrepo-ui deployment via a ConfigMap and Volumes. For this, + encode the files in base64 with `cat my_logo.png | base64`. + + ```yaml title="dbrepo-ui-custom.yaml" + apiVersion: v1 + kind: ConfigMap + metadata: + name: ui-config + binaryData: + my_logo.png: | + <base64> + favicon.ico: | + <base64> + ``` ### Architecture diff --git a/dbrepo-auth-service/dbrepo-realm.json b/dbrepo-auth-service/dbrepo-realm.json index a39f7de1b0ab0611057af4890ee281bb202609ca..2c6effa792933ab8d68cf8286a6d5417e5119bfe 100644 --- a/dbrepo-auth-service/dbrepo-realm.json +++ b/dbrepo-auth-service/dbrepo-realm.json @@ -2143,7 +2143,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper" ] } }, { "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1", @@ -2169,7 +2169,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper" ] } } ], "org.keycloak.storage.UserStorageProvider" : [ { @@ -2209,8 +2209,8 @@ "config" : { "ldap.attribute" : [ "cn" ], "is.mandatory.in.ldap" : [ "true" ], - "read.only" : [ "false" ], "always.read.value.from.ldap" : [ "true" ], + "read.only" : [ "false" ], "user.model.attribute" : [ "firstName" ] } }, { @@ -2233,17 +2233,17 @@ "config" : { "membership.attribute.type" : [ "DN" ], "group.name.ldap.attribute" : [ "cn" ], - "preserve.group.inheritance" : [ "false" ], "membership.user.ldap.attribute" : [ "uid" ], + "preserve.group.inheritance" : [ "false" ], "groups.dn" : [ "ou=users,dc=dbrepo,dc=at" ], "mode" : [ "LDAP_ONLY" ], "user.roles.retrieve.strategy" : [ "LOAD_GROUPS_BY_MEMBER_ATTRIBUTE" ], - "membership.ldap.attribute" : [ "member" ], "ignore.missing.groups" : [ "false" ], + "membership.ldap.attribute" : [ "member" ], "group.object.classes" : [ "groupOfNames" ], "memberof.ldap.attribute" : [ "memberOf" ], - "groups.path" : [ "/" ], - "drop.non.existing.groups.during.sync" : [ "false" ] + "drop.non.existing.groups.during.sync" : [ "false" ], + "groups.path" : [ "/" ] } }, { "id" : "b6ff3285-35af-4e86-8bb4-d94b8e0d70bb", @@ -2267,31 +2267,31 @@ "is.mandatory.in.ldap" : [ "true" ], "attribute.force.default" : [ "false" ], "is.binary.attribute" : [ "false" ], - "always.read.value.from.ldap" : [ "false" ], "read.only" : [ "false" ], + "always.read.value.from.ldap" : [ "false" ], "user.model.attribute" : [ "username" ] } } ] }, "config" : { - "pagination" : [ "false" ], "fullSyncPeriod" : [ "-1" ], + "pagination" : [ "false" ], "startTls" : [ "false" ], - "usersDn" : [ "ou=users,dc=dbrepo,dc=at" ], "connectionPooling" : [ "true" ], + "usersDn" : [ "ou=users,dc=dbrepo,dc=at" ], "cachePolicy" : [ "DEFAULT" ], "useKerberosForPasswordAuthentication" : [ "false" ], "importEnabled" : [ "true" ], "enabled" : [ "true" ], + "changedSyncPeriod" : [ "-1" ], "usernameLDAPAttribute" : [ "uid" ], - "bindCredential" : [ "admin" ], "bindDn" : [ "cn=admin,dc=dbrepo,dc=at" ], - "changedSyncPeriod" : [ "-1" ], + "bindCredential" : [ "admin" ], "lastSync" : [ "1719252666" ], "vendor" : [ "other" ], "uuidLDAPAttribute" : [ "entryUUID" ], - "connectionUrl" : [ "ldap://identity-service:1389" ], "allowKerberosAuthentication" : [ "false" ], + "connectionUrl" : [ "ldap://identity-service:1389" ], "syncRegistrations" : [ "true" ], "authType" : [ "simple" ], "useTruststoreSpi" : [ "always" ], diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java index 60ebbf5de68ec59c4c66190d15ef60e1b11d8e96..507d9f33bd4ad66dba9094b2f6bf4311a761f0b0 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java @@ -38,6 +38,7 @@ import java.security.Principal; import java.sql.SQLException; import java.time.Instant; import java.util.List; +import java.util.UUID; @Log4j2 @RestController @@ -193,9 +194,8 @@ public class SubsetEndpoint { @PostMapping @Observed(name = "dbrepo_subset_create") - @PreAuthorize("hasAuthority('execute-query')") @Operation(summary = "Create subset", - description = "Creates a subset in the query store of the data database. Requires role `execute-query`", + description = "Creates a subset in the query store of the data database. Requires role `execute-query` for private databases.", security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -236,7 +236,7 @@ public class SubsetEndpoint { }) public ResponseEntity<QueryResultDto> create(@NotNull @PathVariable("databaseId") Long databaseId, @Valid @RequestBody ExecuteStatementDto data, - @NotNull Principal principal, + Principal principal, @RequestParam(required = false) Long page, @RequestParam(required = false) Long size, @RequestParam(required = false) Instant timestamp) @@ -244,13 +244,19 @@ public class SubsetEndpoint { QueryNotFoundException, StorageUnavailableException, QueryMalformedException, SidecarExportException, StorageNotFoundException, QueryStoreInsertException, TableMalformedException, PaginationException, QueryNotSupportedException, NotAllowedException, UserNotFoundException, MetadataServiceException { - log.debug("endpoint create subset in database, databaseId={}, data.statement={}, principal.name={}, " + - "page={}, size={}, timestamp={}", databaseId, data.getStatement(), principal.getName(), page, size, + log.debug("endpoint create subset in database, databaseId={}, data.statement={}, page={}, size={}, " + + "timestamp={}", databaseId, data.getStatement(), page, size, timestamp); /* check */ endpointValidator.validateDataParams(page, size); endpointValidator.validateForbiddenStatements(data.getStatement()); /* parameters */ + final UUID userId; + if (principal == null) { + userId = metadataServiceGateway.getSystemUserId(); + } else { + userId = UserUtil.getId(principal); + } if (page == null) { page = 0L; log.debug("page not set: default to {}", page); @@ -267,8 +273,8 @@ public class SubsetEndpoint { final PrivilegedDatabaseDto database = metadataServiceGateway.getDatabaseById(databaseId); final QueryResultDto queryResult; try { - queryResult = subsetService.execute(database, data.getStatement(), timestamp, UserUtil.getId(principal), - page, size, null, null); + queryResult = subsetService.execute(database, data.getStatement(), timestamp, userId, page, size, null, + null); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e); @@ -343,9 +349,9 @@ public class SubsetEndpoint { } try { final QueryDto query = subsetService.findById(database, subsetId); - final HttpHeaders headers = new HttpHeaders(); - headers.set("Access-Control-Expose-Headers", "X-Count"); if (request.getMethod().equals("HEAD")) { + final HttpHeaders headers = new HttpHeaders(); + headers.set("Access-Control-Expose-Headers", "X-Count"); final Long count = subsetService.reExecuteCount(database, query); headers.set("X-Count", "" + count); return ResponseEntity.ok() @@ -356,7 +362,6 @@ public class SubsetEndpoint { result.setId(subsetId); log.trace("re-execute query resulted in result {}", result); return ResponseEntity.ok() - .headers(headers) .body(result); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); @@ -410,8 +415,8 @@ public class SubsetEndpoint { DatabaseUnavailableException, QueryNotFoundException, UserNotFoundException, MetadataServiceException { log.debug("endpoint persist query, databaseId={}, queryId={}, data.persist={}, principal.name={}", databaseId, queryId, data.getPersist(), principal.getName()); - metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); final PrivilegedDatabaseDto database = metadataServiceGateway.getDatabaseById(databaseId); + metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); try { subsetService.persist(database, queryId, data.getPersist()); final QueryDto dto = subsetService.findById(database, queryId); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index e26a37b43aaa1b5307cf87d294e1dedc5cef87c4..4af577bed5771e66502ab48e78509c096666d151 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -99,6 +99,12 @@ public class TableEndpoint { RemoteUnavailableException, TableMalformedException, DatabaseUnavailableException, TableExistsException, TableNotFoundException, QueryMalformedException, MetadataServiceException { log.debug("endpoint create table, databaseId={}, data.name={}", databaseId, data.getName()); + /* check */ + if (data.getConstraints().getPrimaryKey().isEmpty()) { + log.error("Table must have a primary key"); + throw new TableMalformedException("Table must have a primary key"); + } + /* create */ final PrivilegedDatabaseDto database = metadataServiceGateway.getDatabaseById(databaseId); try { return ResponseEntity.status(HttpStatus.CREATED) @@ -218,13 +224,12 @@ public class TableEndpoint { log.error("Failed find table data: authentication required"); throw new NotAllowedException("Failed to find table data: authentication required"); } - final DatabaseAccessDto access = metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); - endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal)); + metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); } - final HttpHeaders headers = new HttpHeaders(); - headers.set("Access-Control-Expose-Headers", "X-Count"); try { if (request.getMethod().equals("HEAD")) { + final HttpHeaders headers = new HttpHeaders(); + headers.set("Access-Control-Expose-Headers", "X-Count"); headers.set("X-Count", "" + tableService.getCount(table, timestamp)); return ResponseEntity.ok() .headers(headers) @@ -232,7 +237,6 @@ public class TableEndpoint { } final QueryResultDto dto = tableService.getData(table, timestamp, page, size); return ResponseEntity.ok() - .headers(headers) .body(dto); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); @@ -542,10 +546,10 @@ public class TableEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<InputStreamResource> exportData(@NotBlank @PathVariable("databaseId") Long databaseId, - @NotBlank @PathVariable("tableId") Long tableId, - @RequestParam(required = false) Instant timestamp, - Principal principal) + public ResponseEntity<InputStreamResource> exportDataset(@NotBlank @PathVariable("databaseId") Long databaseId, + @NotBlank @PathVariable("tableId") Long tableId, + @RequestParam(required = false) Instant timestamp, + Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, NotAllowedException, StorageUnavailableException, QueryMalformedException, SidecarExportException, StorageNotFoundException, MetadataServiceException { @@ -672,8 +676,7 @@ public class TableEndpoint { final PrivilegedTableDto table = metadataServiceGateway.getTableById(databaseId, tableId); table.setDatabase(metadataServiceGateway.getDatabaseById(databaseId)); try { - final TableStatisticDto dto = tableService.getStatistics(table); - return ResponseEntity.ok(dto); + return ResponseEntity.ok(tableService.getStatistics(table)); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database", e); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java index e4ddab603685103027dcb6dfad936b882c987294..a4c07c3f55ff5ea2fab84b6293c7cc6a424c278c 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java @@ -264,9 +264,9 @@ public class ViewEndpoint { metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); } try { - final HttpHeaders headers = new HttpHeaders(); - headers.set("Access-Control-Expose-Headers", "X-Count"); if (request.getMethod().equals("HEAD")) { + final HttpHeaders headers = new HttpHeaders(); + headers.set("Access-Control-Expose-Headers", "X-Count"); headers.set("X-Count", "" + viewService.count(view, timestamp)); return ResponseEntity.ok() .headers(headers) @@ -275,7 +275,6 @@ public class ViewEndpoint { final QueryResultDto result = viewService.data(view, timestamp, page, size); log.trace("get view data resulted in result {}", result); return ResponseEntity.ok() - .headers(headers) .body(result); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java index cbbb4c76b0c12e7846151746093728212a4d3b8d..b15254702a491908482663163defb530e5cbcf6a 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java @@ -21,7 +21,14 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { @ResponseStatus(code = HttpStatus.UNAUTHORIZED) @ExceptionHandler(AccessDeniedException.class) public ResponseEntity<ApiErrorDto> handle(AccessDeniedException e) { - return generic_handle(e.getClass(), e.getLocalizedMessage()); + final HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", "application/problem+json"); + final ApiErrorDto response = ApiErrorDto.builder() + .code("error.access.denied") + .message(e.getLocalizedMessage()) + .status(HttpStatus.UNAUTHORIZED) + .build(); + return new ResponseEntity<>(response, headers, HttpStatus.UNAUTHORIZED); } @Hidden @@ -472,7 +479,7 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { return generic_handle(e.getClass(), e.getLocalizedMessage()); } - private ResponseEntity<ApiErrorDto> generic_handle(Class<?> exceptionClass, String message) { + public ResponseEntity<ApiErrorDto> generic_handle(Class<?> exceptionClass, String message) { final HttpHeaders headers = new HttpHeaders(); headers.set("Content-Type", "application/problem+json"); final ResponseStatus annotation = exceptionClass.getAnnotation(ResponseStatus.class); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java index cf868742400bc407c18d57c58984bcae7a7883ca..3cbb865293553e91a6730cfd731c62bbfc79c915 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java @@ -73,7 +73,7 @@ public class EndpointValidator { log.error("Failed to create table data: insufficient table write access"); throw new NotAllowedException("Failed to create table data: insufficient table write access"); } - log.trace("sufficient write access {}", access); + log.trace("sufficient write access {} for user {} and owner {}", access, user, owner); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java index 8f73fa1b53b41be7d8cbfbe71f25a27c94ccbb01..b0c332a63b5b87e786ca928daa7bc8bfa8a7ed3c 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java @@ -320,6 +320,27 @@ public class MariaDbConfig { return rows; } + public static List<Map<String, byte[]>> selectQueryByteArr(PrivilegedDatabaseDto database, String query, Set<String> columns) + throws SQLException { + final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); + log.trace("connect to database {}", jdbc); + final List<Map<String, byte[]>> rows = new LinkedList<>(); + try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { + final Statement statement = connection.createStatement(); + log.trace("execute query: {}", query); + final ResultSet result = statement.executeQuery(query); + log.trace("map result set to columns: {}", columns); + while (result.next()) { + final Map<String, byte[]> row = new HashMap<>(); + for (String column : columns) { + row.put(column, result.getBytes(column)); + } + rows.add(row); + } + } + return rows; + } + public static void execute(PrivilegedDatabaseDto database, String query) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java index 62f095c82e79df65bd5a9407f735a432dedd502c..74da5a9eb13c6e541f67f660e09e98ea8986a2d0 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbContainerConfig.java @@ -1,11 +1,15 @@ package at.tuwien.config; import at.tuwien.test.BaseTest; +import org.codehaus.plexus.util.FileUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.images.PullPolicy; +import java.io.File; +import java.io.IOException; + /** * This class configures the MariaDB container for the integration tests. */ diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java index 1dc008dbebffdd0a02ef0ffb12a12b9f6e8e6f08..3beb5626b3b8a2a001ac365a376cffea68b7b3ee 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java @@ -1,5 +1,6 @@ package at.tuwien.endpoint; +import at.tuwien.api.database.AccessTypeDto; import at.tuwien.api.database.internal.PrivilegedDatabaseDto; import at.tuwien.api.user.UserDto; import at.tuwien.endpoints.AccessEndpoint; @@ -73,6 +74,26 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void create_unavailable_fails() throws UserNotFoundException, DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException, SQLException, DatabaseMalformedException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getPrivilegedUserById(USER_4_ID)) + .thenReturn(USER_4_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(accessService) + .create(DATABASE_1_PRIVILEGED_DTO, USER_4_PRIVILEGED_DTO, AccessTypeDto.READ); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + accessEndpoint.create(DATABASE_1_ID, USER_4_ID, UPDATE_DATABASE_ACCESS_READ_DTO); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) public void create_databaseNotFound_fails() throws DatabaseNotFoundException, RemoteUnavailableException, @@ -125,13 +146,50 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(metadataServiceGateway.getPrivilegedUserById(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getUserById(USER_1_ID)) + .thenReturn(USER_1_DTO); /* test */ accessEndpoint.update(DATABASE_1_ID, USER_1_ID, UPDATE_DATABASE_ACCESS_READ_DTO); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void update_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, SQLException, + UserNotFoundException, DatabaseMalformedException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getUserById(USER_1_ID)) + .thenReturn(USER_1_DTO); + doThrow(SQLException.class) + .when(accessService) + .update(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.READ); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + accessEndpoint.update(DATABASE_1_ID, USER_1_ID, UPDATE_DATABASE_ACCESS_READ_DTO); + }); + } + + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void update_noAccess_fails() throws DatabaseNotFoundException, RemoteUnavailableException, + UserNotFoundException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getUserById(USER_4_ID)) + .thenReturn(USER_4_DTO); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + accessEndpoint.update(DATABASE_1_ID, USER_4_ID, UPDATE_DATABASE_ACCESS_READ_DTO); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME) public void update_noRole_fails() { @@ -195,6 +253,23 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { accessEndpoint.revoke(DATABASE_1_ID, USER_1_ID); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void revoke_noAccess_fails() throws UserNotFoundException, DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getPrivilegedUserById(USER_4_ID)) + .thenReturn(USER_4_PRIVILEGED_DTO); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + accessEndpoint.revoke(DATABASE_1_ID, USER_4_ID); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME) public void revoke_noRole_fails() { @@ -239,4 +314,24 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void revoke_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, + UserNotFoundException, MetadataServiceException, SQLException, DatabaseMalformedException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getUserById(USER_1_ID)) + .thenReturn(USER_1_DTO); + doThrow(SQLException.class) + .when(accessService) + .delete(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + accessEndpoint.revoke(DATABASE_1_ID, USER_1_ID); + }); + } + } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java index c2b04d5aa914b86ef3edde0ebdf5d82f130c1e40..067687c2ed3762481d800c1d1126c074d99c2fe4 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java @@ -1,6 +1,7 @@ package at.tuwien.endpoint; import at.tuwien.api.database.AccessTypeDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.user.PrivilegedUserDto; import at.tuwien.endpoints.DatabaseEndpoint; import at.tuwien.exception.*; @@ -16,11 +17,14 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.sql.SQLException; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -54,10 +58,24 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) public void create_succeeds() throws DatabaseUnavailableException, RemoteUnavailableException, - QueryStoreCreateException, ContainerNotFoundException, DatabaseMalformedException, MetadataServiceException { + QueryStoreCreateException, ContainerNotFoundException, DatabaseMalformedException, + MetadataServiceException, SQLException { + + /* mock */ + when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) + .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + when(databaseService.create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + doNothing() + .when(queryService) + .createQueryStore(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + doNothing() + .when(accessService) + .create(eq(DATABASE_1_PRIVILEGED_DTO), any(PrivilegedUserDto.class), any(AccessTypeDto.class)); /* test */ - databaseEndpoint.create(DATABASE_1_CREATE_INTERNAL); + final ResponseEntity<DatabaseDto> response = databaseEndpoint.create(DATABASE_1_CREATE_INTERNAL); + assertEquals(HttpStatus.CREATED, response.getStatusCode()); } @Test @@ -83,6 +101,24 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void create_unavailable_fails() throws RemoteUnavailableException, ContainerNotFoundException, + SQLException, DatabaseMalformedException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) + .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(databaseService) + .create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + databaseEndpoint.create(DATABASE_1_CREATE_INTERNAL); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) public void create_containerNotFound_fails() throws RemoteUnavailableException, ContainerNotFoundException, @@ -125,10 +161,32 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { public void update_succeeds() throws DatabaseUnavailableException, RemoteUnavailableException, DatabaseMalformedException, DatabaseNotFoundException, MetadataServiceException { + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + /* test */ databaseEndpoint.update(DATABASE_1_ID, USER_1_UPDATE_PASSWORD_DTO); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void update_unavailable_fails() throws RemoteUnavailableException, DatabaseMalformedException, + DatabaseNotFoundException, MetadataServiceException, SQLException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(databaseService) + .update(DATABASE_1_PRIVILEGED_DTO, USER_1_UPDATE_PASSWORD_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + databaseEndpoint.update(DATABASE_1_ID, USER_1_UPDATE_PASSWORD_DTO); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME) public void update_noRole_fails() throws RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java index 6a0015b6f61e567fbf65f2e8f97f6c568fe142fa..1c54280a222f294be84bdf5c24b5b8ee4e0e6caf 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java @@ -46,7 +46,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { private SubsetEndpoint subsetEndpoint; @MockBean - private SubsetService queryService; + private SubsetService subsetService; @MockBean private HttpServletRequest httpServletRequest; @@ -64,21 +64,43 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void findAllById_succeeds() throws DatabaseUnavailableException, NotAllowedException, QueryNotFoundException, + public void list_succeeds() throws DatabaseUnavailableException, NotAllowedException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, SQLException, MetadataServiceException { + /* mock */ + when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null)) + .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO)); + /* test */ - final List<QueryDto> response = generic_findAllById(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO); + final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO); assertEquals(6, response.size()); } @Test @WithAnonymousUser - public void findAllById_databaseNotFound_fails() { + public void list_databaseNotFound_fails() { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_findAllById(null, null); + generic_list(null, null); + }); + } + + @Test + @WithAnonymousUser + public void list_unavailable_fails() throws NotAllowedException, SQLException, QueryNotFoundException, + DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(subsetService) + .findAll(DATABASE_3_PRIVILEGED_DTO, null); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO); }); } @@ -92,9 +114,28 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(QUERY_5_DTO); /* test */ - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.APPLICATION_JSON, null); + generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null); + } + + @Test + @WithAnonymousUser + public void findById_format_fails() throws DatabaseNotFoundException, RemoteUnavailableException, SQLException, + UserNotFoundException, NotAllowedException, QueryNotFoundException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(QUERY_5_DTO); + + /* test */ + assertThrows(FormatNotAvailableException.class, () -> { + generic_findById(QUERY_5_ID, MediaType.APPLICATION_PDF, null); + }); } @Test @@ -111,11 +152,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(queryService.export(any(PrivilegedDatabaseDto.class), any(QueryDto.class), any(Instant.class), anyString())) + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(QUERY_5_DTO); + when(subsetService.export(any(PrivilegedDatabaseDto.class), any(QueryDto.class), any(Instant.class), anyString())) .thenReturn(mock); /* test */ - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.parseMediaType("text/csv"), null); + generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), null); } @Test @@ -132,16 +175,18 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(queryService.export(any(PrivilegedDatabaseDto.class), any(QueryDto.class), any(Instant.class), anyString())) + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(QUERY_5_DTO); + when(subsetService.export(any(PrivilegedDatabaseDto.class), any(QueryDto.class), any(Instant.class), anyString())) .thenReturn(mock); /* test */ - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.parseMediaType("text/csv"), Instant.now()); + generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), Instant.now()); } @Test @WithAnonymousUser - public void findById_fails() throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException { + public void findById_notFound_fails() throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException { /* mock */ doThrow(DatabaseNotFoundException.class) @@ -150,7 +195,53 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.APPLICATION_JSON, null); + generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null); + }); + } + + @Test + @WithAnonymousUser + public void findById_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, + MetadataServiceException, SQLException, UserNotFoundException, NotAllowedException, QueryNotFoundException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(subsetService) + .findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null); + }); + } + + @Test + @WithAnonymousUser + public void findById_unavailableExport_fails() throws DatabaseNotFoundException, RemoteUnavailableException, + MetadataServiceException, SQLException, StorageUnavailableException, QueryMalformedException, + SidecarExportException, StorageNotFoundException, UserNotFoundException, NotAllowedException, + QueryNotFoundException { + final ExportResourceDto mock = ExportResourceDto.builder() + .filename("deadbeef") + .resource(new InputStreamResource(InputStream.nullInputStream())) + .build(); + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(QUERY_5_DTO); + when(subsetService.export(any(PrivilegedDatabaseDto.class), any(QueryDto.class), any(Instant.class), anyString())) + .thenReturn(mock); + doThrow(SQLException.class) + .when(subsetService) + .export(eq(DATABASE_3_PRIVILEGED_DTO), eq(QUERY_5_DTO), any(Instant.class), anyString()); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), null); }); } @@ -168,7 +259,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(queryService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_1_ID), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_1_ID), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(QUERY_5_RESULT_DTO); /* test */ @@ -202,13 +293,35 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(queryService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_1_ID), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_1_ID), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(QUERY_5_RESULT_DTO); /* test */ subsetEndpoint.create(DATABASE_3_ID, request, USER_1_PRINCIPAL, null, null, null); } + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) + public void create_unavailable_succeeds() throws UserNotFoundException, QueryStoreInsertException, + TableMalformedException, NotAllowedException, QueryNotFoundException, DatabaseNotFoundException, + RemoteUnavailableException, SQLException, MetadataServiceException { + final ExecuteStatementDto request = ExecuteStatementDto.builder() + .statement(QUERY_5_STATEMENT) + .build(); + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(subsetService) + .execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_1_ID), eq(0L), eq(10L), eq(null), eq(null)); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + subsetEndpoint.create(DATABASE_3_ID, request, USER_1_PRINCIPAL, null, null, null); + }); + } + @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) public void create_databaseNotFound_fails() throws RemoteUnavailableException, @@ -230,15 +343,38 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_4_USERNAME) - public void create_noRole_fails() { + public void create_noRole_fails() throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException, UserNotFoundException, QueryStoreInsertException, TableMalformedException, NotAllowedException, SQLException, QueryNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, SidecarExportException, QueryNotSupportedException, PaginationException, StorageNotFoundException { final ExecuteStatementDto request = ExecuteStatementDto.builder() .statement(QUERY_5_STATEMENT) .build(); + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_4_ID), eq(0L), eq(10L), eq(null), eq(null))) + .thenReturn(QUERY_5_RESULT_DTO); + /* test */ - assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - subsetEndpoint.create(DATABASE_3_ID, request, USER_4_PRINCIPAL, null, null, null); - }); + subsetEndpoint.create(DATABASE_3_ID, request, USER_4_PRINCIPAL, null, null, null); + } + + @Test + @WithAnonymousUser + public void create_anonymous_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException, UserNotFoundException, QueryStoreInsertException, TableMalformedException, NotAllowedException, SQLException, QueryNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, SidecarExportException, QueryNotSupportedException, PaginationException, StorageNotFoundException { + final ExecuteStatementDto request = ExecuteStatementDto.builder() + .statement(QUERY_5_STATEMENT) + .build(); + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(metadataServiceGateway.getSystemUserId()) + .thenReturn(USER_LOCAL_ADMIN_ID); + when(subsetService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_LOCAL_ADMIN_ID), eq(0L), eq(10L), eq(null), eq(null))) + .thenReturn(QUERY_5_RESULT_DTO); + + /* test */ + subsetEndpoint.create(DATABASE_3_ID, request, null, null, null, null); } @Test @@ -249,11 +385,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(queryService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(queryService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) + when(subsetService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) .thenReturn(QUERY_5_RESULT_NUMBER); - when(queryService.reExecute(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO, 0L, 10L, null, null)) + when(subsetService.reExecute(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO, 0L, 10L, null, null)) .thenReturn(QUERY_5_RESULT_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -261,23 +397,20 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ final ResponseEntity<QueryResultDto> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID, null, httpServletRequest, null, null); assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getHeaders().get("Access-Control-Expose-Headers")); - assertEquals(1, response.getHeaders().get("Access-Control-Expose-Headers").size()); - assertEquals("X-Count", response.getHeaders().get("Access-Control-Expose-Headers").get(0)); assertNotNull(response.getBody()); } @Test - public void getData_onlyHead_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, + public void getData_head_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, TableMalformedException, QueryMalformedException, DatabaseUnavailableException, PaginationException, MetadataServiceException { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(queryService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(queryService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) + when(subsetService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) .thenReturn(QUERY_5_RESULT_NUMBER); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -301,11 +434,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); - when(queryService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); - when(queryService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) + when(subsetService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) .thenReturn(QUERY_1_RESULT_NUMBER); - when(queryService.reExecute(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, 0L, 10L, null, null)) + when(subsetService.reExecute(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, 0L, 10L, null, null)) .thenReturn(QUERY_1_RESULT_DTO); /* test */ @@ -349,16 +482,16 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME) - public void getData_privateOnlyHead_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, + public void getData_privateHead_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException, TableMalformedException, QueryMalformedException, QueryNotFoundException, PaginationException, SQLException, MetadataServiceException { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(queryService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); - when(queryService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) + when(subsetService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) .thenReturn(QUERY_1_RESULT_NUMBER); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -371,6 +504,29 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { assertEquals(QUERY_1_RESULT_NUMBER, Long.parseLong(response.getHeaders().get("X-Count").get(0))); } + @Test + @WithMockUser(username = USER_1_USERNAME) + public void getData_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, SQLException, + UserNotFoundException, NotAllowedException, TableMalformedException, QueryNotFoundException, + MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + .thenReturn(QUERY_1_DTO); + when(httpServletRequest.getMethod()) + .thenReturn("GET"); + doThrow(SQLException.class) + .when(subsetService) + .reExecute(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, 0L, 10L, null, null); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null); + }); + } + @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"persist-query"}) public void persist_succeeds() throws NotAllowedException, RemoteUnavailableException, DatabaseNotFoundException, @@ -386,9 +542,9 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); doNothing() - .when(queryService) + .when(subsetService) .persist(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID, true); - when(queryService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -447,16 +603,37 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { }); } - protected List<QueryDto> generic_findAllById(Long databaseId, PrivilegedDatabaseDto database) - throws DatabaseUnavailableException, NotAllowedException, QueryNotFoundException, DatabaseNotFoundException, - RemoteUnavailableException, SQLException, MetadataServiceException { + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"persist-query"}) + public void persist_unavailable_fails() throws NotAllowedException, RemoteUnavailableException, + MetadataServiceException, QueryStorePersistException, SQLException, DatabaseNotFoundException { + final QueryPersistDto request = QueryPersistDto.builder() + .persist(true) + .build(); + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_3_ID)) + .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); + doThrow(SQLException.class) + .when(subsetService) + .persist(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID, true); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + subsetEndpoint.persist(DATABASE_3_ID, QUERY_5_ID, request, USER_3_PRINCIPAL); + }); + } + + protected List<QueryDto> generic_list(Long databaseId, PrivilegedDatabaseDto database) throws NotAllowedException, + DatabaseUnavailableException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, + MetadataServiceException { /* mock */ if (database != null) { when(metadataServiceGateway.getDatabaseById(databaseId)) .thenReturn(database); - when(queryService.findAll(database, null)) - .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO)); } else { doThrow(DatabaseNotFoundException.class) .when(metadataServiceGateway) @@ -469,15 +646,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { return response.getBody(); } - protected void generic_findById(Long subsetId, QueryDto subset, MediaType accept, Instant timestamp) - throws UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, - NotAllowedException, QueryMalformedException, QueryNotFoundException, DatabaseNotFoundException, - SidecarExportException, RemoteUnavailableException, FormatNotAvailableException, StorageNotFoundException, - SQLException, MetadataServiceException { + protected void generic_findById(Long subsetId, MediaType accept, Instant timestamp) throws UserNotFoundException, + DatabaseUnavailableException, StorageUnavailableException, NotAllowedException, QueryMalformedException, + QueryNotFoundException, DatabaseNotFoundException, SidecarExportException, RemoteUnavailableException, + FormatNotAvailableException, StorageNotFoundException, MetadataServiceException { /* mock */ - when(queryService.findById(DATABASE_3_PRIVILEGED_DTO, subsetId)) - .thenReturn(subset); when(mockHttpServletRequest.getHeader("Accept")) .thenReturn(accept.toString()); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java index 76c34ef47cd2d13f698f3d552b9aebbbb4175492..f03f4c3f189c98013e6fa5d4bb01a5cfd4fe211f 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java @@ -1,9 +1,11 @@ package at.tuwien.endpoint; import at.tuwien.ExportResourceDto; +import at.tuwien.api.database.DatabaseAccessDto; import at.tuwien.api.database.query.ImportCsvDto; import at.tuwien.api.database.query.QueryResultDto; import at.tuwien.api.database.table.*; +import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.endpoints.TableEndpoint; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; @@ -14,12 +16,16 @@ import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -29,6 +35,7 @@ import java.sql.SQLException; import java.time.Instant; import java.util.HashMap; import java.util.List; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -50,6 +57,22 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @MockBean private MetadataServiceGateway metadataServiceGateway; + public static Stream<Arguments> size_arguments() { + return Stream.of( + Arguments.arguments("zero", 0L), + Arguments.arguments("neg zero", -0L), + Arguments.arguments("negative", -1L) + ); + } + + public static Stream<Arguments> anyAccess_parameters() { + return Stream.of( + Arguments.arguments("read", DATABASE_1_USER_2_READ_ACCESS_DTO), + Arguments.arguments("write_own", DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO), + Arguments.arguments("write_all", DATABASE_1_USER_2_WRITE_ALL_ACCESS_DTO) + ); + } + @BeforeEach public void beforeEach() { genesis(); @@ -98,6 +121,85 @@ public class TableEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void create_unavailable_fails() throws TableMalformedException, DatabaseNotFoundException, SQLException, + TableExistsException, RemoteUnavailableException, TableNotFoundException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(tableService) + .createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.create(DATABASE_1_ID, TABLE_4_CREATE_INTERNAL_DTO); + }); + } + + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void create_missingPrimaryKey_fails() { + + /* test */ + assertThrows(TableMalformedException.class, () -> { + tableEndpoint.create(DATABASE_1_ID, TABLE_1_CREATE_INTERNAL_INVALID_DTO); + }); + } + + @Test + @WithAnonymousUser + public void statistic_succeeds() throws DatabaseUnavailableException, TableNotFoundException, + TableMalformedException, DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException, + SQLException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(tableService.getStatistics(any(PrivilegedTableDto.class))) + .thenReturn(TABLE_8_STATISTIC_DTO); + + /* test */ + final ResponseEntity<TableStatisticDto> response = tableEndpoint.statistic(DATABASE_3_ID, TABLE_8_ID); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @WithAnonymousUser + public void statistic_unavailable_fails() throws TableNotFoundException, TableMalformedException, + RemoteUnavailableException, MetadataServiceException, SQLException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(tableService) + .getStatistics(any(PrivilegedTableDto.class)); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.statistic(DATABASE_3_ID, TABLE_8_ID); + }); + } + + @Test + @WithAnonymousUser + public void statistic_notFound_fails() throws TableNotFoundException, RemoteUnavailableException, + MetadataServiceException { + + /* mock */ + doThrow(TableNotFoundException.class) + .when(metadataServiceGateway) + .getTableById(DATABASE_1_ID, TABLE_1_ID); + + /* test */ + assertThrows(TableNotFoundException.class, () -> { + tableEndpoint.statistic(DATABASE_1_ID, TABLE_1_ID); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) public void delete_succeeds() throws RemoteUnavailableException, DatabaseUnavailableException, @@ -141,6 +243,24 @@ public class TableEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void delete_unavailable_fails() throws RemoteUnavailableException, TableNotFoundException, SQLException, + MetadataServiceException, QueryMalformedException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(tableService) + .delete(TABLE_1_PRIVILEGED_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.delete(DATABASE_1_ID, TABLE_1_ID); + }); + } + @Test @WithAnonymousUser public void getData_succeeds() throws DatabaseUnavailableException, TableNotFoundException, TableMalformedException, @@ -150,18 +270,129 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) .thenReturn(TABLE_8_PRIVILEGED_DTO); - when(tableService.getCount(eq(TABLE_8_PRIVILEGED_DTO), any(Instant.class))) - .thenReturn(TABLE_8_DATA_COUNT); when(tableService.getData(eq(TABLE_8_PRIVILEGED_DTO), any(Instant.class), eq(0L), eq(10L))) .thenReturn(TABLE_8_DATA_DTO); /* test */ final ResponseEntity<QueryResultDto> response = tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, httpServletRequest, null); assertEquals(HttpStatus.OK, response.getStatusCode()); + + } + + @Test + @WithAnonymousUser + public void getData_head_succeeds() throws DatabaseUnavailableException, TableNotFoundException, TableMalformedException, + SQLException, QueryMalformedException, RemoteUnavailableException, PaginationException, MetadataServiceException, + NotAllowedException { + final HttpServletRequest mock = mock(HttpServletRequest.class); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(mock.getMethod()) + .thenReturn("HEAD"); + when(tableService.getCount(eq(TABLE_8_PRIVILEGED_DTO), any(Instant.class))) + .thenReturn(3L); + when(tableService.getData(eq(TABLE_8_PRIVILEGED_DTO), any(Instant.class), eq(0L), eq(10L))) + .thenReturn(TABLE_8_DATA_DTO); + + /* test */ + final ResponseEntity<QueryResultDto> response = tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, mock, null); + assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getHeaders().get("Access-Control-Expose-Headers")); - assertEquals(1, response.getHeaders().get("Access-Control-Expose-Headers").size()); assertEquals("X-Count", response.getHeaders().get("Access-Control-Expose-Headers").get(0)); + assertNotNull(response.getHeaders().get("X-Count")); + assertEquals("3", response.getHeaders().get("X-Count").get(0)); + + } + + @Test + @WithAnonymousUser + public void getData_privateAnonymous_fails() throws TableNotFoundException, RemoteUnavailableException, + MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + /* test */ + assertThrows(NotAllowedException.class, () -> { + tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, null); + }); + } + + @Test + @WithMockUser(username = USER_2_USERNAME) + public void getData_privateNoAccess_fails() throws TableNotFoundException, RemoteUnavailableException, + MetadataServiceException, NotAllowedException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + doThrow(NotAllowedException.class) + .when(metadataServiceGateway) + .getAccess(DATABASE_1_ID, USER_2_ID); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, USER_2_PRINCIPAL); + }); + } + + @Test + @WithAnonymousUser + public void getData_unavailable_fails() throws TableNotFoundException, RemoteUnavailableException, + MetadataServiceException, TableMalformedException, SQLException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(tableService) + .getData(eq(TABLE_8_PRIVILEGED_DTO), any(Instant.class), eq(0L), eq(10L)); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, httpServletRequest, null); + }); + } + + @Test + @WithMockUser(username = USER_2_USERNAME) + public void getData_privateAccessUnavailable_fails() throws TableNotFoundException, RemoteUnavailableException, + MetadataServiceException, NotAllowedException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + doThrow(RemoteUnavailableException.class) + .when(metadataServiceGateway) + .getAccess(DATABASE_1_ID, USER_2_ID); + + /* test */ + assertThrows(RemoteUnavailableException.class, () -> { + tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, USER_2_PRINCIPAL); + }); + } + + @ParameterizedTest + @WithMockUser(username = USER_2_USERNAME) + @MethodSource("anyAccess_parameters") + public void getData_private_succeeds(String name, DatabaseAccessDto access) throws DatabaseUnavailableException, + TableNotFoundException, TableMalformedException, SQLException, QueryMalformedException, + RemoteUnavailableException, PaginationException, MetadataServiceException, NotAllowedException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_2_ID)) + .thenReturn(access); + when(tableService.getData(eq(TABLE_1_PRIVILEGED_DTO), any(Instant.class), eq(0L), eq(10L))) + .thenReturn(TABLE_1_DATA_DTO); + + /* test */ + final ResponseEntity<QueryResultDto> response = tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, USER_2_PRINCIPAL); + assertEquals(HttpStatus.OK, response.getStatusCode()); } @Test @@ -182,7 +413,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) - public void createTuple_succeeds() throws DatabaseUnavailableException, TableNotFoundException, + public void insertRawTuple_succeeds() throws DatabaseUnavailableException, TableNotFoundException, TableMalformedException, NotAllowedException, QueryMalformedException, RemoteUnavailableException, SQLException, StorageUnavailableException, StorageNotFoundException, MetadataServiceException { final TupleDto request = TupleDto.builder() @@ -211,7 +442,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME) - public void createTuple_noRole_fails() { + public void insertRawTuple_noRole_fails() { final TupleDto request = TupleDto.builder() .data(new HashMap<>() {{ put(COLUMN_8_1_INTERNAL_NAME, 7L); @@ -227,7 +458,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void createTuple_tableNotFound_fails() throws TableNotFoundException, RemoteUnavailableException, + public void insertRawTuple_tableNotFound_fails() throws TableNotFoundException, RemoteUnavailableException, MetadataServiceException { final TupleDto request = TupleDto.builder() .data(new HashMap<>() {{ @@ -249,7 +480,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void createTuple_readAccess_fails() throws TableNotFoundException, RemoteUnavailableException, + public void insertRawTuple_readAccess_fails() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, MetadataServiceException { final TupleDto request = TupleDto.builder() .data(new HashMap<>() {{ @@ -270,9 +501,36 @@ public class TableEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) + public void insertRawTuple_unavailable_fails() throws TableNotFoundException, RemoteUnavailableException, + NotAllowedException, MetadataServiceException, TableMalformedException, StorageUnavailableException, + SQLException, QueryMalformedException, StorageNotFoundException { + final TupleDto request = TupleDto.builder() + .data(new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, 7L); + put(COLUMN_8_2_INTERNAL_NAME, 23.0); + }}) + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) + .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); + doThrow(SQLException.class) + .when(tableService) + .createTuple(TABLE_8_PRIVILEGED_DTO, request); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + }); + } + @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) - public void createTuple_writeOwnAccess_succeeds() throws TableNotFoundException, RemoteUnavailableException, + public void insertRawTuple_writeOwnAccess_succeeds() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, DatabaseUnavailableException, TableMalformedException, QueryMalformedException, StorageUnavailableException, StorageNotFoundException, MetadataServiceException { final TupleDto request = TupleDto.builder() @@ -294,7 +552,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void createTuple_writeOwnAccessForeign_fails() throws TableNotFoundException, RemoteUnavailableException, + public void insertRawTuple_writeOwnAccessForeign_fails() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, MetadataServiceException { final TupleDto request = TupleDto.builder() .data(new HashMap<>() {{ @@ -317,7 +575,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void createTuple_writeAllAccessForeign_succeeds() throws TableNotFoundException, RemoteUnavailableException, + public void insertRawTuple_writeAllAccessForeign_succeeds() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, DatabaseUnavailableException, TableMalformedException, QueryMalformedException, StorageUnavailableException, StorageNotFoundException, MetadataServiceException { final TupleDto request = TupleDto.builder() @@ -439,6 +697,35 @@ public class TableEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) + public void updateTuple_unavailable_fails() throws TableNotFoundException, RemoteUnavailableException, SQLException, + NotAllowedException, MetadataServiceException, TableMalformedException, QueryMalformedException { + final TupleUpdateDto request = TupleUpdateDto.builder() + .keys(new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, 6L); + }}) + .data(new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, 6L); + put(COLUMN_8_2_INTERNAL_NAME, 23.0); + }}) + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_3_ID)) + .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); + doThrow(SQLException.class) + .when(tableService) + .updateTuple(TABLE_8_PRIVILEGED_DTO, request); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + }); + } + @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) public void updateTuple_writeOwnAccess_succeeds() throws DatabaseUnavailableException, TableNotFoundException, @@ -615,6 +902,31 @@ public class TableEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"delete-table-data"}) + public void deleteTuple_unavailable_fails() throws TableNotFoundException, RemoteUnavailableException, SQLException, + NotAllowedException, MetadataServiceException, TableMalformedException, QueryMalformedException { + final TupleDeleteDto request = TupleDeleteDto.builder() + .keys(new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, 6L); + }}) + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) + .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); + doThrow(SQLException.class) + .when(tableService) + .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + }); + } + @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"delete-table-data"}) public void deleteTuple_writeOwnAccess_succeeds() throws TableNotFoundException, RemoteUnavailableException, @@ -724,6 +1036,17 @@ public class TableEndpointUnitTest extends AbstractUnitTest { }); } + @ParameterizedTest + @MethodSource("size_arguments") + @WithAnonymousUser + public void getHistory_invalidSize_fails(String name, Long size) { + + /* test */ + assertThrows(PaginationException.class, () -> { + tableEndpoint.getHistory(DATABASE_1_ID, TABLE_1_ID, size, null); + }); + } + @Test @WithMockUser(username = USER_4_USERNAME) public void getHistory_privateNoAccess_fails() throws NotAllowedException, RemoteUnavailableException, @@ -742,6 +1065,42 @@ public class TableEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_2_USERNAME) + public void getHistory_private_succeeds() throws NotAllowedException, RemoteUnavailableException, SQLException, + TableNotFoundException, MetadataServiceException, DatabaseUnavailableException, PaginationException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_2_ID)) + .thenReturn(DATABASE_1_USER_2_READ_ACCESS_DTO); + when(tableService.history(TABLE_1_PRIVILEGED_DTO, 10L)) + .thenReturn(List.of()); + + /* test */ + final ResponseEntity<List<TableHistoryDto>> response = tableEndpoint.getHistory(DATABASE_1_ID, TABLE_1_ID, null, USER_2_PRINCIPAL); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @WithAnonymousUser + public void getHistory_unavailable_succeeds() throws RemoteUnavailableException, SQLException, + TableNotFoundException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(tableService) + .history(TABLE_8_PRIVILEGED_DTO, 100L); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.getHistory(DATABASE_3_ID, TABLE_8_ID, null, null); + }); + } + @Test @WithAnonymousUser public void getHistory_tableNotFound_fails() throws TableNotFoundException, RemoteUnavailableException, @@ -775,7 +1134,32 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(mock); /* test */ - final ResponseEntity<InputStreamResource> response = tableEndpoint.exportData(DATABASE_3_ID, TABLE_8_ID, null, null); + final ResponseEntity<InputStreamResource> response = tableEndpoint.exportDataset(DATABASE_3_ID, TABLE_8_ID, null, null); + assertEquals(HttpStatus.OK, response.getStatusCode()); + + } + + @ParameterizedTest + @WithMockUser(username = USER_2_USERNAME) + @MethodSource("anyAccess_parameters") + public void exportData_private_succeeds(String name, DatabaseAccessDto access) throws DatabaseUnavailableException, TableNotFoundException, NotAllowedException, + StorageUnavailableException, QueryMalformedException, SidecarExportException, RemoteUnavailableException, + StorageNotFoundException, SQLException, MetadataServiceException { + final ExportResourceDto mock = ExportResourceDto.builder() + .filename("deadbeef") + .resource(new InputStreamResource(InputStream.nullInputStream())) + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_2_ID)) + .thenReturn(access); + when(tableService.exportDataset(eq(TABLE_1_PRIVILEGED_DTO), any(Instant.class))) + .thenReturn(mock); + + /* test */ + final ResponseEntity<InputStreamResource> response = tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_1_ID, null, USER_2_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); } @@ -794,19 +1178,92 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.exportData(DATABASE_1_ID, TABLE_1_ID, null, null); + tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_1_ID, null, null); + }); + } + + @Test + @WithMockUser(username = USER_1_USERNAME) + public void exportData_unavailable_fails() throws TableNotFoundException, RemoteUnavailableException, + MetadataServiceException, StorageUnavailableException, SQLException, QueryMalformedException, + SidecarExportException, StorageNotFoundException { + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(tableService) + .exportDataset(eq(TABLE_1_PRIVILEGED_DTO), any(Instant.class)); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_1_ID, null, USER_1_PRINCIPAL); }); + } + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"system"}) + public void getSchema_succeeds() throws DatabaseUnavailableException, TableNotFoundException, + RemoteUnavailableException, SQLException, MetadataServiceException, DatabaseNotFoundException, + DatabaseMalformedException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(tableService.getSchemas(DATABASE_3_PRIVILEGED_DTO)) + .thenReturn(List.of(TABLE_8_DTO)); + + /* test */ + final ResponseEntity<List<TableDto>> response = tableEndpoint.getSchema(DATABASE_3_ID); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @WithAnonymousUser + public void getSchema_anonymous_succeeds() { + + /* test */ + assertThrows(AccessDeniedException.class, () -> { + tableEndpoint.getSchema(DATABASE_3_ID); + }); + } + + @Test + @WithMockUser(username = USER_4_USERNAME) + public void getSchema_noRole_succeeds() { + + /* test */ + assertThrows(AccessDeniedException.class, () -> { + tableEndpoint.getSchema(DATABASE_3_ID); + }); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"system"}) + public void getSchema_unavailable_fails() throws TableNotFoundException, RemoteUnavailableException, SQLException, + MetadataServiceException, DatabaseNotFoundException, DatabaseMalformedException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(tableService) + .getSchemas(DATABASE_3_PRIVILEGED_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.getSchema(DATABASE_3_ID); + }); } @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) - public void importData_succeeds() throws DatabaseUnavailableException, TableNotFoundException, + public void importDataset_succeeds() throws DatabaseUnavailableException, TableNotFoundException, SidecarImportException, NotAllowedException, QueryMalformedException, RemoteUnavailableException, StorageNotFoundException, SQLException, MetadataServiceException { final ImportCsvDto request = ImportCsvDto.builder() .skipLines(1L) - .lineTermination("\\n") + .lineTermination(null) .location("deadbeef") .build(); @@ -829,7 +1286,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_4_USERNAME) - public void importData_noRole_fails() { + public void importDataset_noRole_fails() { final ImportCsvDto request = ImportCsvDto.builder() .skipLines(1L) .lineTermination("\\n") @@ -844,7 +1301,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void importData_tableNotFound_fails() throws TableNotFoundException, RemoteUnavailableException, + public void importDataset_tableNotFound_fails() throws TableNotFoundException, RemoteUnavailableException, MetadataServiceException { final ImportCsvDto request = ImportCsvDto.builder() .skipLines(1L) @@ -865,7 +1322,59 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void importData_readAccess_fails() throws TableNotFoundException, RemoteUnavailableException, + public void importDataset_unavailable_fails() throws RemoteUnavailableException, SidecarImportException, + SQLException, QueryMalformedException, StorageNotFoundException, TableNotFoundException, + MetadataServiceException, NotAllowedException { + final ImportCsvDto request = ImportCsvDto.builder() + .skipLines(1L) + .lineTermination("\\n") + .location("deadbeef") + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_3_ID)) + .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); + doThrow(SQLException.class) + .when(tableService) + .importDataset(any(PrivilegedTableDto.class), eq(request)); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) + public void importDataset_writeOwnAccess_fails() throws RemoteUnavailableException, SidecarImportException, + SQLException, QueryMalformedException, StorageNotFoundException, TableNotFoundException, + MetadataServiceException, NotAllowedException { + final ImportCsvDto request = ImportCsvDto.builder() + .skipLines(1L) + .lineTermination("\\n") + .location("deadbeef") + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_3_ID)) + .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); + doThrow(SQLException.class) + .when(tableService) + .importDataset(any(PrivilegedTableDto.class), eq(request)); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) + public void importDataset_readAccess_fails() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, MetadataServiceException { final ImportCsvDto request = ImportCsvDto.builder() .skipLines(1L) @@ -887,7 +1396,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) - public void importData_writeOwnAccess_succeeds() throws TableNotFoundException, RemoteUnavailableException, + public void importDataset_writeOwnAccess_succeeds() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, DatabaseUnavailableException, SidecarImportException, QueryMalformedException, StorageNotFoundException, MetadataServiceException { final ImportCsvDto request = ImportCsvDto.builder() @@ -908,7 +1417,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void importData_writeOwnAccessForeign_fails() throws TableNotFoundException, RemoteUnavailableException, + public void importDataset_writeOwnAccessForeign_fails() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, MetadataServiceException { final ImportCsvDto request = ImportCsvDto.builder() .skipLines(1L) @@ -930,7 +1439,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"insert-table-data"}) - public void importData_writeAllAccessForeign_succeeds() throws TableNotFoundException, RemoteUnavailableException, + public void importDataset_writeAllAccessForeign_succeeds() throws TableNotFoundException, RemoteUnavailableException, NotAllowedException, DatabaseUnavailableException, SidecarImportException, QueryMalformedException, StorageNotFoundException, MetadataServiceException { final ImportCsvDto request = ImportCsvDto.builder() @@ -949,4 +1458,90 @@ public class TableEndpointUnitTest extends AbstractUnitTest { tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); } + @Test + @WithMockUser(username = USER_2_USERNAME, authorities = {"insert-table-data"}) + public void importDataset_privateForeign_succeeds() throws TableNotFoundException, RemoteUnavailableException, + NotAllowedException, DatabaseUnavailableException, SidecarImportException, QueryMalformedException, + StorageNotFoundException, MetadataServiceException { + final ImportCsvDto request = ImportCsvDto.builder() + .skipLines(1L) + .lineTermination("\\n") + .location("deadbeef") + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_2_ID)) + .thenReturn(DATABASE_1_USER_2_WRITE_ALL_ACCESS_DTO); + + /* test */ + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL); + } + + @Test + @WithMockUser(username = USER_2_USERNAME, authorities = {"insert-table-data"}) + public void importDataset_private_succeeds() throws TableNotFoundException, RemoteUnavailableException, + NotAllowedException, DatabaseUnavailableException, SidecarImportException, QueryMalformedException, + StorageNotFoundException, MetadataServiceException { + final ImportCsvDto request = ImportCsvDto.builder() + .skipLines(1L) + .lineTermination("\\n") + .location("deadbeef") + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_2_ID)) + .thenReturn(TABLE_2_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_2_ID)) + .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO); + + /* test */ + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL); + } + + @Test + @WithMockUser(username = USER_2_USERNAME, authorities = {"insert-table-data"}) + public void importDataset_privateForeign_fails() throws TableNotFoundException, RemoteUnavailableException, + NotAllowedException, MetadataServiceException { + final ImportCsvDto request = ImportCsvDto.builder() + .skipLines(1L) + .lineTermination("\\n") + .location("deadbeef") + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_2_ID)) + .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL); + }); + } + + @Test + @WithMockUser(username = USER_2_USERNAME, authorities = {"insert-table-data"}) + public void importDataset_privateReadAccess_fails() throws TableNotFoundException, RemoteUnavailableException, + NotAllowedException, MetadataServiceException { + final ImportCsvDto request = ImportCsvDto.builder() + .skipLines(1L) + .lineTermination("\\n") + .location("deadbeef") + .build(); + + /* mock */ + when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_2_ID)) + .thenReturn(TABLE_2_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_2_ID)) + .thenReturn(DATABASE_1_USER_2_READ_ACCESS_DTO); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL); + }); + } + } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java index 543b4a5cf299d48a8a4594f95e8586752d4278cc..fd1030c17bee1f169c01d33da8af86f339feecbe 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java @@ -17,11 +17,14 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.sql.SQLException; import java.time.Instant; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -64,6 +67,24 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { assertEquals(HttpStatus.CREATED, response.getStatusCode()); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void create_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, SQLException, + ViewMalformedException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(viewService) + .create(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + viewEndpoint.create(DATABASE_1_ID, VIEW_1_CREATE_DTO); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME) public void create_noRole_fails() throws DatabaseNotFoundException, RemoteUnavailableException, ViewMalformedException, @@ -97,6 +118,76 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { }); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void getSchema_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, SQLException, + DatabaseMalformedException, DatabaseUnavailableException, ViewNotFoundException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(viewService.getSchemas(DATABASE_1_PRIVILEGED_DTO)) + .thenReturn(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO)); + + /* test */ + final ResponseEntity<List<ViewDto>> response = viewEndpoint.getSchema(DATABASE_1_ID); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @WithAnonymousUser + public void getSchema_anonymous_fails() { + + /* test */ + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { + viewEndpoint.getSchema(DATABASE_1_ID); + }); + } + + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void getSchema_databaseNotFound_fails() throws DatabaseNotFoundException, RemoteUnavailableException, + MetadataServiceException { + + /* mock */ + doThrow(DatabaseNotFoundException.class) + .when(metadataServiceGateway) + .getDatabaseById(DATABASE_1_ID); + + /* test */ + assertThrows(DatabaseNotFoundException.class, () -> { + viewEndpoint.getSchema(DATABASE_1_ID); + }); + } + + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void getSchema_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, + SQLException, DatabaseMalformedException, ViewNotFoundException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(viewService) + .getSchemas(DATABASE_1_PRIVILEGED_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + viewEndpoint.getSchema(DATABASE_1_ID); + }); + } + + @Test + @WithAnonymousUser + public void delete_anonymous_fails() { + + /* test */ + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { + viewEndpoint.delete(DATABASE_1_ID, VIEW_1_ID); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) public void delete_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, ViewMalformedException, @@ -114,6 +205,24 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } + @Test + @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) + public void delete_unavailable_fails() throws RemoteUnavailableException, ViewMalformedException, SQLException, + MetadataServiceException, ViewNotFoundException { + + /* mock */ + when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) + .thenReturn(VIEW_1_PRIVILEGED_DTO); + doThrow(SQLException.class) + .when(viewService) + .delete(VIEW_1_PRIVILEGED_DTO); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + viewEndpoint.delete(DATABASE_1_ID, VIEW_1_ID); + }); + } + @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME) public void delete_noRole_fails() throws DatabaseNotFoundException, RemoteUnavailableException, ViewMalformedException, @@ -150,7 +259,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) - public void getData_succeeds() throws RemoteUnavailableException, ViewNotFoundException, ViewMalformedException, + public void getData_private_succeeds() throws RemoteUnavailableException, ViewNotFoundException, ViewMalformedException, SQLException, DatabaseUnavailableException, QueryMalformedException, PaginationException, NotAllowedException, MetadataServiceException { @@ -167,40 +276,79 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* test */ final ResponseEntity<QueryResultDto> response = viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, USER_1_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getHeaders().get("Access-Control-Expose-Headers")); - assertEquals(1, response.getHeaders().get("Access-Control-Expose-Headers").size()); - assertEquals("X-Count", response.getHeaders().get("Access-Control-Expose-Headers").get(0)); assertNotNull(response.getBody()); } @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) - public void getData_onlyHead_succeeds() throws RemoteUnavailableException, ViewNotFoundException, + public void getData_privateHead_succeeds() throws RemoteUnavailableException, ViewNotFoundException, ViewMalformedException, SQLException, DatabaseUnavailableException, QueryMalformedException, PaginationException, NotAllowedException, MetadataServiceException { /* mock */ - when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_3_ID)) + .thenReturn(VIEW_3_PRIVILEGED_DTO); when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_1_ID)) .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); - when(viewService.count(eq(VIEW_1_PRIVILEGED_DTO), any(Instant.class))) - .thenReturn(VIEW_1_DATA_COUNT); + when(viewService.count(eq(VIEW_3_PRIVILEGED_DTO), any(Instant.class))) + .thenReturn(VIEW_3_DATA_COUNT); /* test */ - final ResponseEntity<QueryResultDto> response = viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, USER_1_PRINCIPAL); + final ResponseEntity<QueryResultDto> response = viewEndpoint.getData(DATABASE_1_ID, VIEW_3_ID, null, null, null, httpServletRequest, USER_1_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getHeaders().get("X-Count")); assertEquals(1, response.getHeaders().get("X-Count").size()); - assertEquals(VIEW_1_DATA_COUNT, Long.parseLong(response.getHeaders().get("X-Count").get(0))); + assertEquals(VIEW_3_DATA_COUNT, Long.parseLong(response.getHeaders().get("X-Count").get(0))); assertNotNull(response.getHeaders().get("Access-Control-Expose-Headers")); assertEquals(1, response.getHeaders().get("Access-Control-Expose-Headers").size()); assertEquals("X-Count", response.getHeaders().get("Access-Control-Expose-Headers").get(0)); assertNull(response.getBody()); } + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) + public void getData_unavailable_fails() throws RemoteUnavailableException, ViewNotFoundException, SQLException, + ViewMalformedException, NotAllowedException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) + .thenReturn(VIEW_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getAccess(DATABASE_1_ID, USER_1_ID)) + .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); + when(httpServletRequest.getMethod()) + .thenReturn("GET"); + doThrow(SQLException.class) + .when(viewService) + .data(eq(VIEW_1_PRIVILEGED_DTO), any(Instant.class), eq(0L), eq(10L)); + + /* test */ + assertThrows(DatabaseUnavailableException.class, () -> { + viewEndpoint.getData(DATABASE_1_ID, VIEW_1_ID, null, null, null, httpServletRequest, USER_1_PRINCIPAL); + }); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) + public void getData_privateNoAccess_succeeds() throws RemoteUnavailableException, ViewNotFoundException, + NotAllowedException, MetadataServiceException { + + /* mock */ + when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_3_ID)) + .thenReturn(VIEW_3_PRIVILEGED_DTO); + when(httpServletRequest.getMethod()) + .thenReturn("GET"); + doThrow(NotAllowedException.class) + .when(metadataServiceGateway) + .getAccess(DATABASE_1_ID, USER_1_ID); + + /* test */ + assertThrows(NotAllowedException.class, () -> { + viewEndpoint.getData(DATABASE_1_ID, VIEW_3_ID, null, null, null, httpServletRequest, USER_1_PRINCIPAL); + }); + } + @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) public void getData_viewNotFound_fails() throws RemoteUnavailableException, ViewNotFoundException, diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ce56ce82da975f8f41d7233b0a017143044457ed --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java @@ -0,0 +1,1232 @@ +package at.tuwien.handlers; + +import at.tuwien.api.error.ApiErrorDto; +import at.tuwien.exception.*; +import at.tuwien.test.AbstractUnitTest; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static at.tuwien.test.utils.EndpointUtils.getErrorCodes; +import static at.tuwien.test.utils.EndpointUtils.getExceptions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Log4j2 +@ExtendWith(SpringExtension.class) +@SpringBootTest +public class ApiExceptionHandlerTest extends AbstractUnitTest { + + @Autowired + private ApiExceptionHandler apiExceptionHandler; + + @Test + public void handle_succeeds() throws ClassNotFoundException, IOException { + final List<Method> handlers = Arrays.asList(ApiExceptionHandler.class.getMethods()); + final List<String> errorCodes = getErrorCodes(); + + /* test */ + for (Class<?> exception : getExceptions()) { + final Optional<Method> optional = handlers.stream().filter(h -> Arrays.asList(h.getParameterTypes()).contains(exception)).findFirst(); + if (optional.isEmpty()) { + Assertions.fail("Exception " + exception.getName() + " does not have a corresponding handle method in the endpoint"); + } + final Method method = optional.get(); + /* exception */ + assertNotNull(exception.getDeclaredAnnotation(ResponseStatus.class).code()); + Assertions.assertNotEquals(exception.getDeclaredAnnotation(ResponseStatus.class).code(), HttpStatus.INTERNAL_SERVER_ERROR); + assertNotNull(exception.getDeclaredAnnotation(ResponseStatus.class).reason(), "Exception " + exception.getName() + " does not provide a reason code"); + Assertions.assertTrue(errorCodes.contains(exception.getDeclaredAnnotation(ResponseStatus.class).reason()), "Exception code " + exception.getDeclaredAnnotation(ResponseStatus.class).reason() + " does have a reason code mapped in localized ui error messages"); + /* handler method */ + Assertions.assertEquals(method.getDeclaredAnnotation(ResponseStatus.class).code(), exception.getDeclaredAnnotation(ResponseStatus.class).code()); + } + } + + @Test + public void generic_handle_succeeds() { + + /* test */ + apiExceptionHandler.generic_handle(DatabaseNotFoundException.class, "msg"); + } + + @Test + public void handle_AccessDeniedException_succeeds() { + final AccessDeniedException request = new AccessDeniedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + Assertions.assertNotEquals(response.getStatusCode(), HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals("error.access.denied", response.getBody().getCode()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_AccessNotFoundException_succeeds() { + final AccessNotFoundException request = new AccessNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_AccountNotSetupException_succeeds() { + final AccountNotSetupException request = new AccountNotSetupException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_AuthServiceConnectionException_succeeds() { + final AuthServiceConnectionException request = new AuthServiceConnectionException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_AuthServiceException_succeeds() { + final AuthServiceException request = new AuthServiceException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_BrokerServiceConnectionException_succeeds() { + final BrokerServiceConnectionException request = new BrokerServiceConnectionException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_BrokerServiceException_succeeds() { + final BrokerServiceException request = new BrokerServiceException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ConceptNotFoundException_succeeds() { + final ConceptNotFoundException request = new ConceptNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ContainerAlreadyExistsException_succeeds() { + final ContainerAlreadyExistsException request = new ContainerAlreadyExistsException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ContainerNotFoundException_succeeds() { + final ContainerNotFoundException request = new ContainerNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_CredentialsInvalidException_succeeds() { + final CredentialsInvalidException request = new CredentialsInvalidException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_DatabaseMalformedException_succeeds() { + final DatabaseMalformedException request = new DatabaseMalformedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_DatabaseNotFoundException_succeeds() { + final DatabaseNotFoundException request = new DatabaseNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_DatabaseUnavailableException_succeeds() { + final DatabaseUnavailableException request = new DatabaseUnavailableException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_DoiNotFoundException_succeeds() { + final DoiNotFoundException request = new DoiNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_EmailExistsException_succeeds() { + final EmailExistsException request = new EmailExistsException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ExchangeNotFoundException_succeeds() { + final ExchangeNotFoundException request = new ExchangeNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ExternalServiceException_succeeds() { + final ExternalServiceException request = new ExternalServiceException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_FilterBadRequestException_succeeds() { + final FilterBadRequestException request = new FilterBadRequestException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_FormatNotAvailableException_succeeds() { + final FormatNotAvailableException request = new FormatNotAvailableException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_IdentifierNotFoundException_succeeds() { + final IdentifierNotFoundException request = new IdentifierNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_IdentifierNotSupportedException_succeeds() { + final IdentifierNotSupportedException request = new IdentifierNotSupportedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ImageAlreadyExistsException_succeeds() { + final ImageAlreadyExistsException request = new ImageAlreadyExistsException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ImageInvalidException_succeeds() { + final ImageInvalidException request = new ImageInvalidException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ImageNotFoundException_succeeds() { + final ImageNotFoundException request = new ImageNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_LicenseNotFoundException_succeeds() { + final LicenseNotFoundException request = new LicenseNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_MalformedException_succeeds() { + final MalformedException request = new MalformedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_MessageNotFoundException_succeeds() { + final MessageNotFoundException request = new MessageNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_MetadataServiceConnectionException_succeeds() { + final MetadataServiceConnectionException request = new MetadataServiceConnectionException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_MetadataServiceException_succeeds() { + final MetadataServiceException request = new MetadataServiceException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_NotAllowedException_succeeds() { + final NotAllowedException request = new NotAllowedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_OntologyNotFoundException_succeeds() { + final OntologyNotFoundException request = new OntologyNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_OrcidNotFoundException_succeeds() { + final OrcidNotFoundException request = new OrcidNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_PaginationException_succeeds() { + final PaginationException request = new PaginationException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueryMalformedException_succeeds() { + final QueryMalformedException request = new QueryMalformedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueryNotFoundException_succeeds() { + final QueryNotFoundException request = new QueryNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueryNotSupportedException_succeeds() { + final QueryNotSupportedException request = new QueryNotSupportedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueueNotFoundException_succeeds() { + final QueueNotFoundException request = new QueueNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueryStoreCreateException_succeeds() { + final QueryStoreCreateException request = new QueryStoreCreateException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueryStoreGCException_succeeds() { + final QueryStoreGCException request = new QueryStoreGCException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueryStoreInsertException_succeeds() { + final QueryStoreInsertException request = new QueryStoreInsertException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_QueryStorePersistException_succeeds() { + final QueryStorePersistException request = new QueryStorePersistException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_RemoteUnavailableException_succeeds() { + final RemoteUnavailableException request = new RemoteUnavailableException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_RorNotFoundException_succeeds() { + final RorNotFoundException request = new RorNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_SearchServiceConnectionException_succeeds() { + final SearchServiceConnectionException request = new SearchServiceConnectionException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_SearchServiceException_succeeds() { + final SearchServiceException request = new SearchServiceException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_SemanticEntityNotFoundException_succeeds() { + final SemanticEntityNotFoundException request = new SemanticEntityNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_DataServiceConnectionException_succeeds() { + final DataServiceConnectionException request = new DataServiceConnectionException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_DataServiceException_succeeds() { + final DataServiceException request = new DataServiceException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_SidecarExportException_succeeds() { + final SidecarExportException request = new SidecarExportException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_SidecarImportException_succeeds() { + final SidecarImportException request = new SidecarImportException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_SortException_succeeds() { + final SortException request = new SortException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_StorageNotFoundException_succeeds() { + final StorageNotFoundException request = new StorageNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_StorageUnavailableException_succeeds() { + final StorageUnavailableException request = new StorageUnavailableException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_TableExistsException_succeeds() { + final TableExistsException request = new TableExistsException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_TableMalformedException_succeeds() { + final TableMalformedException request = new TableMalformedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_TableSchemaException_succeeds() { + final TableSchemaException request = new TableSchemaException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_TableNotFoundException_succeeds() { + final TableNotFoundException request = new TableNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_UnitNotFoundException_succeeds() { + final UnitNotFoundException request = new UnitNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_UriMalformedException_succeeds() { + final UriMalformedException request = new UriMalformedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_UserExistsException_succeeds() { + final UserExistsException request = new UserExistsException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_UserNotFoundException_succeeds() { + final UserNotFoundException request = new UserNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ViewMalformedException_succeeds() { + final ViewMalformedException request = new ViewMalformedException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ViewNotFoundException_succeeds() { + final ViewNotFoundException request = new ViewNotFoundException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + + + @Test + public void handle_ViewSchemaException_succeeds() { + final ViewSchemaException request = new ViewSchemaException("msg"); + + /* test */ + final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(request); + assertNotNull(response); + assertNotNull(request.getClass().getDeclaredAnnotation(ResponseStatus.class).code()); + final HttpStatus httpStatus = request.getClass().getDeclaredAnnotation(ResponseStatus.class).code(); + Assertions.assertNotEquals(httpStatus, HttpStatus.INTERNAL_SERVER_ERROR); + assertEquals(httpStatus, response.getStatusCode()); + assertNotNull(response.getBody()); + assertNotNull(response.getBody().getCode()); + assertEquals(httpStatus, response.getBody().getStatus()); + assertEquals("msg", response.getBody().getMessage()); + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java index 5898372633b0d63c5b256f6b567f8a8869583ab7..b331a03fa60915290380b5ff233fd51a0996ee36 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -196,12 +196,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - tableEndpoint.exportData(DATABASE_1_ID, TABLE_1_ID, null, USER_1_PRINCIPAL); - } catch (Exception e) { - /* ignore */ - } - try { - tableEndpoint.exportData(DATABASE_1_ID, TABLE_1_ID, null, USER_1_PRINCIPAL); + tableEndpoint.exportDataset(DATABASE_1_ID, TABLE_1_ID, null, USER_1_PRINCIPAL); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java index aeaae0ecf2626ff88027a210bd8e24a5c66d4ede..b33a76506c15b77df3e0243754ca57b6dfcd9ed4 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java @@ -11,9 +11,10 @@ import at.tuwien.exception.*; import at.tuwien.gateway.DataDatabaseSidecarGateway; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.test.AbstractUnitTest; +import at.tuwien.utils.FileUtils; import com.google.common.hash.Hashing; import lombok.extern.log4j.Log4j2; -import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomUtils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -387,13 +388,13 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { protected void export_generic() throws StorageUnavailableException, SQLException, QueryMalformedException, SidecarExportException, MetadataServiceException, RemoteUnavailableException, StorageNotFoundException, IOException, InterruptedException { - final String filename = "68b329da9893e34099c7d8ad5cb9c940"; + final String filename = RandomStringUtils.randomAlphanumeric(40).toLowerCase() + ".tmp"; + EXPORT_RESOURCE_DTO.setFilename(filename); /* pre-condition */ Thread.sleep(1000) /* wait for test container some more */; /* mock */ - FileUtils.deleteQuietly(new File(s3Config.getS3FilePath() + "/" + filename)); doNothing() .when(dataDatabaseSidecarGateway) .exportFile(anyString(), anyInt(), eq(filename)); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index e12b5bdc75962076ab52466b6c12493d109ccf64..4ebaba4931327a80ba319ae6407e3dc3547e1dc7 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -18,6 +18,8 @@ import at.tuwien.api.database.table.constraints.unique.UniqueDto; import at.tuwien.api.database.table.internal.TableCreateDto; import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; +import at.tuwien.config.QueryConfig; +import at.tuwien.config.S3Config; import at.tuwien.exception.*; import at.tuwien.gateway.DataDatabaseSidecarGateway; import at.tuwien.gateway.MetadataServiceGateway; @@ -43,6 +45,9 @@ import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.charset.Charset; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermissions; import java.sql.SQLException; import java.time.Instant; import java.util.*; @@ -63,6 +68,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { @Autowired private TableService tableService; + @Autowired + private QueryConfig queryConfig; + @MockBean private MetadataServiceGateway metadataServiceGateway; @@ -86,7 +94,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* metadata database */ MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_INTERNALNAME); MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_DTO); } @Test @@ -252,6 +262,35 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { assertEquals("0.2", result.get(0).get("rainfall")); } + @Test + public void createTuple_autogeneratedBlob_succeeds() throws SQLException, RemoteUnavailableException, ContainerNotFoundException, + TableNotFoundException, TableMalformedException, QueryMalformedException, StorageUnavailableException, + StorageNotFoundException, MetadataServiceException { + final String s3key = "2eec905f-17ed-41de-b12f-283c0aa3e4f9"; + final byte[] s3data = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + /* add row with primary key */ + final TupleDto request = TupleDto.builder() + .data(new HashMap<>() {{ + put("value", "24.3"); + put("raw", s3key); + }}) + .build(); + + /* mock */ + when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) + .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + when(storageService.getBytes(s3key)) + .thenReturn(s3data); + when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + + /* test */ + tableService.createTuple(TABLE_8_PRIVILEGED_DTO, request); + final List<Map<String, byte[]>> result = MariaDbConfig.selectQueryByteArr(DATABASE_3_PRIVILEGED_DTO, "SELECT raw FROM mfcc WHERE raw IS NOT NULL", Set.of("raw")); + assertNotNull(result.get(0).get("raw")); + assertArrayEquals(s3data, result.get(0).get("raw")); + } + @Test public void createTuple_notInOrder_succeeds() throws SQLException, RemoteUnavailableException, ContainerNotFoundException, TableNotFoundException, TableMalformedException, QueryMalformedException, @@ -468,7 +507,16 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { final TableDto response = tableService.createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO); assertEquals(TABLE_4_NAME, response.getName()); assertEquals(TABLE_4_INTERNALNAME, response.getInternalName()); - assertEquals(TABLE_4_COLUMNS.size(), response.getColumns().size()); + final List<ColumnDto> columns = response.getColumns(); + assertEquals(TABLE_4_COLUMNS.size(), columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "timestamp", "timestamp", ColumnTypeDto.TIMESTAMP, null, null, false, queryConfig.getDefaultTimestampFormatId(), null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "value", "value", ColumnTypeDto.DECIMAL, 10L, 10L, true, null, null); + final ConstraintsDto constraints = response.getConstraints(); + assertNotNull(constraints); + final Set<PrimaryKeyDto> primaryKey = constraints.getPrimaryKey(); + Assertions.assertEquals(1, primaryKey.size()); + final Set<String> checks = constraints.getChecks(); + Assertions.assertEquals(0, checks.size()); } @Test @@ -532,6 +580,66 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }); } + @Test + public void create_compositePrimaryKey_fails() throws TableNotFoundException, TableMalformedException, SQLException, + TableExistsException { + final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() + .name("composite_primary_key") + .columns(List.of(ColumnCreateDto.builder() + .name("name") + .type(ColumnTypeDto.VARCHAR) + .size(255L) + .nullAllowed(false) + .build(), + ColumnCreateDto.builder() + .name("lat") + .type(ColumnTypeDto.DECIMAL) + .size(10L) + .d(10L) + .nullAllowed(false) + .build(), + ColumnCreateDto.builder() + .name("lng") + .type(ColumnTypeDto.DECIMAL) + .size(10L) + .d(10L) + .nullAllowed(false) + .build())) + .constraints(ConstraintsCreateDto.builder() + .primaryKey(Set.of("lat", "lng")) + .foreignKeys(List.of()) + .checks(Set.of()) + .uniques(List.of()) + .build()) + .build(); + + /* test */ + final TableDto response = tableService.createTable(DATABASE_1_PRIVILEGED_DTO, request); + assertEquals("composite_primary_key", response.getName()); + assertEquals("composite_primary_key", response.getInternalName()); + final List<ColumnDto> columns = response.getColumns(); + assertEquals(3, columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "name", "name", ColumnTypeDto.VARCHAR, 255L, null, false, null, null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "lat", "lat", ColumnTypeDto.DECIMAL, 10L, 10L, false, null, null); + assertColumn(columns.get(2), null, null, DATABASE_1_ID, "lng", "lng", ColumnTypeDto.DECIMAL, 10L, 10L, false, null, null); + final ConstraintsDto constraints = response.getConstraints(); + assertNotNull(constraints); + final Set<String> checks = constraints.getChecks(); + assertNotNull(checks); + assertEquals(0, checks.size()); + final List<PrimaryKeyDto> primaryKeys = new LinkedList<>(constraints.getPrimaryKey()); + assertNotNull(primaryKeys); + assertEquals(2, primaryKeys.size()); + assertEquals("lat", primaryKeys.get(0).getColumn().getInternalName()); + assertEquals("lng", primaryKeys.get(1).getColumn().getInternalName()); + final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); + assertNotNull(foreignKeys); + assertEquals(0, foreignKeys.size()); + final List<UniqueDto> uniques = constraints.getUniques(); + assertNotNull(uniques); + assertEquals(0, uniques.size()); + } + @Test public void create_needSequence_succeeds() throws TableNotFoundException, TableMalformedException, SQLException, TableExistsException { @@ -672,7 +780,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ final File source = new File("src/test/resources/csv/weather_aus.csv"); - final File target = new File("/tmp/weather_aus.csv"); + final File target = new File("/tmp/weather_aus.csv") /* must be /tmp */; log.trace("copy dataset from {} to {}", source.toPath().toAbsolutePath(), target.toPath().toAbsolutePath()); FileUtils.copyFile(source, target); doNothing() diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/FileUtils.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/FileUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..5746d8dfcfa1de0be7bd707e1c8b3cabf3e82d30 --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/FileUtils.java @@ -0,0 +1,14 @@ +package at.tuwien.utils; + +import java.io.File; +import java.io.IOException; + +public class FileUtils { + + public static void delete(File file) throws IOException { + if (file.exists()) { + org.apache.commons.io.FileUtils.forceDelete(file); + } + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java index 13806e93ddd1cbeb7a8e1c0ab4e0fe38db0830ad..13ddfce8d3c171b79096d2e0d1d05948848a8c86 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java @@ -8,6 +8,13 @@ import static org.junit.jupiter.api.Assertions.*; public class UserUtilTest extends BaseTest { + @Test + public void constructor_succeeds() { + + /* test */ + new UserUtil(); + } + @Test public void hasRole_succeeds() { assertTrue(UserUtil.hasRole(USER_1_PRINCIPAL, "find-container")); diff --git a/dbrepo-data-service/rest-service/src/test/resources/init/musicology.sql b/dbrepo-data-service/rest-service/src/test/resources/init/musicology.sql index 4d2c8deb43ede5de84cd321a302e97ef84038508..a2fc3f2b313cdd536e8ccba075bf7353be2b1438 100644 --- a/dbrepo-data-service/rest-service/src/test/resources/init/musicology.sql +++ b/dbrepo-data-service/rest-service/src/test/resources/init/musicology.sql @@ -6,7 +6,8 @@ CREATE SEQUENCE seq_mfcc; CREATE TABLE mfcc ( id BIGINT PRIMARY KEY NOT NULL DEFAULT nextval(`seq_mfcc`), - value DECIMAL NOT NULL + value DECIMAL NOT NULL, + raw LONGBLOB NULL ) WITH SYSTEM VERSIONING; INSERT INTO `mfcc` (`value`) diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/config/KeycloakConfig.java b/dbrepo-data-service/services/src/main/java/at/tuwien/config/KeycloakConfig.java index e0d7d0321513387f1e1c9c235c1c4b51e309be1d..cc6960ba39be294a61e059f3a3f45ecfc8e820fd 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/config/KeycloakConfig.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/config/KeycloakConfig.java @@ -1,12 +1,8 @@ package at.tuwien.config; -import at.tuwien.interceptor.KeycloakInterceptor; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.DefaultUriBuilderFactory; @Getter @Configuration @@ -26,13 +22,4 @@ public class KeycloakConfig { @Value("${dbrepo.keycloak.clientSecret}") private String keycloakClientSecret; - - @Bean("keycloakRestTemplate") - public RestTemplate brokerRestTemplate() { - final RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(keycloakEndpoint)); - restTemplate.getInterceptors() - .add(new KeycloakInterceptor(keycloakUsername, keycloakPassword, keycloakEndpoint)); - return restTemplate; - } } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java b/dbrepo-data-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java index 1560c14b7aaa6272c76515a734a1ad99f7075222..e1f763b3b7924748fe80f4485bbef2d3b05cfa23 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java @@ -54,7 +54,8 @@ public class WebSecurityConfig { ); final OrRequestMatcher publicEndpoints = new OrRequestMatcher( new AntPathRequestMatcher("/api/**", "GET"), - new AntPathRequestMatcher("/api/**", "HEAD") + new AntPathRequestMatcher("/api/**", "HEAD"), + new AntPathRequestMatcher("/api/database/**/subset", "POST") ); /* enable CORS and disable CSRF */ http = http.cors().and().csrf().disable(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java index d16c8c8eba81efd22a64757a6dd1eb51dc56318f..282e7d593feec58991dc9f4530e3314fa208eb29 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java @@ -8,6 +8,7 @@ import at.tuwien.api.database.table.TableStatisticDto; import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.PrivilegedUserDto; +import at.tuwien.api.user.UserBriefDto; import at.tuwien.api.user.UserDto; import at.tuwien.exception.*; import jakarta.validation.constraints.NotNull; @@ -22,9 +23,9 @@ public interface MetadataServiceGateway { * * @param containerId The container id * @return The container with privileged connection information, if successful. - * @throws ContainerNotFoundException The table was not found in the metadata service. + * @throws ContainerNotFoundException The table was not found in the metadata service. * @throws RemoteUnavailableException The remote service is not available. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws MetadataServiceException The remote service returned invalid data. */ PrivilegedContainerDto getContainerById(Long containerId) throws RemoteUnavailableException, ContainerNotFoundException, MetadataServiceException; @@ -36,7 +37,7 @@ public interface MetadataServiceGateway { * @return The database, if successful. * @throws DatabaseNotFoundException The database was not found in the metadata service. * @throws RemoteUnavailableException The remote service is not available. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws MetadataServiceException The remote service returned invalid data. */ PrivilegedDatabaseDto getDatabaseById(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException; @@ -48,7 +49,7 @@ public interface MetadataServiceGateway { * @return The database, if successful. * @throws DatabaseNotFoundException The database was not found in the metadata service. * @throws RemoteUnavailableException The remote service is not available. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws MetadataServiceException The remote service returned invalid data. */ PrivilegedDatabaseDto getDatabaseByInternalName(String internalName) throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException; @@ -61,19 +62,20 @@ public interface MetadataServiceGateway { * @return The table, if successful. * @throws TableNotFoundException The table was not found in the metadata service. * @throws RemoteUnavailableException The remote service is not available. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws MetadataServiceException The remote service returned invalid data. */ PrivilegedTableDto getTableById(Long databaseId, Long id) throws TableNotFoundException, RemoteUnavailableException, MetadataServiceException; /** * Get a view with given database id and view id from the metadata service. + * * @param databaseId The database id. * @param id The view id. * @return The view, if successful. - * @throws ViewNotFoundException The view was not found in the metadata service. + * @throws ViewNotFoundException The view was not found in the metadata service. * @throws RemoteUnavailableException The remote service is not available. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws MetadataServiceException The remote service returned invalid data. */ PrivilegedViewDto getViewById(Long databaseId, Long id) throws RemoteUnavailableException, ViewNotFoundException, MetadataServiceException; @@ -85,10 +87,21 @@ public interface MetadataServiceGateway { * @return The user, if successful. * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. * @throws UserNotFoundException The user was not found in the metadata service. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws MetadataServiceException The remote service returned invalid data. */ UserDto getUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException; + /** + * Get a user with given username from the metadata service. + * + * @return The user, if successful. Otherwise empty list. + * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. + * @throws UserNotFoundException The user was not found in the metadata service. + * @throws MetadataServiceException The remote service returned invalid data. + */ + UUID getSystemUserId() throws RemoteUnavailableException, UserNotFoundException, + MetadataServiceException; + /** * Get a user with given user id from the metadata service. * @@ -96,42 +109,45 @@ public interface MetadataServiceGateway { * @return The user, if successful. * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. * @throws UserNotFoundException The user was not found in the metadata service. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws MetadataServiceException The remote service returned invalid data. */ PrivilegedUserDto getPrivilegedUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException; /** * Get database access for a given user and database id from the metadata service. + * * @param databaseId The database id. - * @param userId The user id. + * @param userId The user id. * @return The database access, if successful. * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. - * @throws NotAllowedException The access to this database is denied for the given user. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws NotAllowedException The access to this database is denied for the given user. + * @throws MetadataServiceException The remote service returned invalid data. */ DatabaseAccessDto getAccess(Long databaseId, UUID userId) throws RemoteUnavailableException, NotAllowedException, MetadataServiceException; /** * Get a list of identifiers for a given database id and optional subset id. + * * @param databaseId The database id. - * @param subsetId The subset id. Optional. + * @param subsetId The subset id. Optional. * @return The list of identifiers. * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. - * @throws DatabaseNotFoundException The database was not found. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws DatabaseNotFoundException The database was not found. + * @throws MetadataServiceException The remote service returned invalid data. */ List<IdentifierDto> getIdentifiers(@NotNull Long databaseId, Long subsetId) throws MetadataServiceException, RemoteUnavailableException, DatabaseNotFoundException; /** * Update the table statistics in the metadata service. + * * @param databaseId The database id. - * @param tableId The table id. + * @param tableId The table id. * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. - * @throws TableNotFoundException The table was not found. - * @throws MetadataServiceException The remote service returned invalid data. + * @throws TableNotFoundException The table was not found. + * @throws MetadataServiceException The remote service returned invalid data. */ void updateTableStatistics(Long databaseId, Long tableId) throws TableNotFoundException, MetadataServiceException, RemoteUnavailableException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java index b4cb2ff5043c46e1e5ece49a45690ef04782bd3c..640ef7172ac2b750815e17a0bdf11b02b5ed997b 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java @@ -10,7 +10,9 @@ import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.PrivilegedUserDto; +import at.tuwien.api.user.UserBriefDto; import at.tuwien.api.user.UserDto; +import at.tuwien.config.GatewayConfig; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.mapper.MetadataMapper; @@ -35,11 +37,14 @@ import java.util.UUID; public class MetadataServiceGatewayImpl implements MetadataServiceGateway { private final RestTemplate restTemplate; + private final GatewayConfig gatewayConfig; private final MetadataMapper metadataMapper; @Autowired - public MetadataServiceGatewayImpl(RestTemplate restTemplate, MetadataMapper metadataMapper) { + public MetadataServiceGatewayImpl(RestTemplate restTemplate, GatewayConfig gatewayConfig, + MetadataMapper metadataMapper) { this.restTemplate = restTemplate; + this.gatewayConfig = gatewayConfig; this.metadataMapper = metadataMapper; } @@ -226,6 +231,34 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { return response.getBody(); } + @Override + public UUID getSystemUserId() throws RemoteUnavailableException, UserNotFoundException, + MetadataServiceException { + final ResponseEntity<UserBriefDto[]> response; + try { + response = restTemplate.exchange("/api/user?username=" + gatewayConfig.getSystemUsername(), HttpMethod.GET, HttpEntity.EMPTY, UserBriefDto[].class); + } catch (ResourceAccessException | HttpServerErrorException e) { + log.error("Failed to find user with username {}: {}", gatewayConfig.getSystemUsername(), e.getMessage()); + throw new RemoteUnavailableException("Failed to find user with username " + gatewayConfig.getSystemUsername() + ": " + e.getMessage(), e); + } catch (HttpClientErrorException.NotFound e) { + log.error("Failed to find user with username {}: not found: {}", gatewayConfig.getSystemUsername(), e.getMessage()); + throw new UserNotFoundException("Failed to find user with username " + gatewayConfig.getSystemUsername() + ": " + e.getMessage(), e); + } + if (!response.getStatusCode().equals(HttpStatus.OK)) { + log.error("Failed to find user with username {}: service responded unsuccessful: {}", gatewayConfig.getSystemUsername(), response.getStatusCode()); + throw new MetadataServiceException("Failed to find user with username " + gatewayConfig.getSystemUsername() + ": service responded unsuccessful: " + response.getStatusCode()); + } + if (response.getBody() == null) { + log.error("Failed to find user with username {}: body is empty", gatewayConfig.getSystemUsername()); + throw new MetadataServiceException("Failed to find user with username " + gatewayConfig.getSystemUsername() + ": body is empty"); + } + if (response.getBody().length != 1) { + log.error("Failed to find system user: expected exactly one result but got {}", response.getBody().length); + throw new MetadataServiceException("Failed to find system user: expected exactly one result but got " + response.getBody().length); + } + return response.getBody()[0].getId(); + } + @Override public PrivilegedUserDto getPrivilegedUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java b/dbrepo-data-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java deleted file mode 100644 index 78fb5adc61fd2420cfc62e72cb4aa4c700c3b82b..0000000000000000000000000000000000000000 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java +++ /dev/null @@ -1,55 +0,0 @@ -package at.tuwien.interceptor; - -import at.tuwien.api.keycloak.TokenDto; -import lombok.extern.log4j.Log4j2; -import org.springframework.http.*; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.client.ResourceAccessException; -import org.springframework.web.client.RestTemplate; - -import java.io.IOException; - -@Log4j2 -public class KeycloakInterceptor implements ClientHttpRequestInterceptor { - - private final String adminUsername; - private final String adminPassword; - private final String keycloakEndpoint; - - public KeycloakInterceptor(String adminUsername, String adminPassword, String keycloakEndpoint) { - this.adminUsername = adminUsername; - this.adminPassword = adminPassword; - this.keycloakEndpoint = keycloakEndpoint; - } - - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) - throws IOException { - final RestTemplate restTemplate = new RestTemplate(); - final HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - final MultiValueMap<String, String> payload = new LinkedMultiValueMap<>(); - payload.add("username", adminUsername); - payload.add("password", adminPassword); - payload.add("grant_type", "password"); - payload.add("client_id", "admin-cli"); - final ResponseEntity<TokenDto> response; - try { - response = restTemplate.exchange(keycloakEndpoint + "/realms/master/protocol/openid-connect/token", - HttpMethod.POST, new HttpEntity<>(payload, headers), TokenDto.class); - } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { - log.error("Failed to obtain admin token: {}", e.getMessage()); - return execution.execute(request, body); - } - if (response.getBody() == null) { - return execution.execute(request, body); - } - request.getHeaders().set("Authorization", "Bearer " + response.getBody().getAccessToken()); - return execution.execute(request, body); - } -} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java index 56888cde6421d176940e72fc24c4fdd44796ef2f..d870215771628283ddb9e115a755c176d7f69240 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java @@ -837,17 +837,8 @@ public interface MariaDbMapper { statement.setNull(idx, Types.BLOB); break; } - try { - final ByteArrayOutputStream boas = new ByteArrayOutputStream(); - try (ObjectOutputStream ois = new ObjectOutputStream(boas)) { - ois.writeObject(value); - statement.setBlob(idx, new ByteArrayInputStream(boas.toByteArray())); - } - - } catch (IOException e) { - log.error("Failed to set blob/tinyblob/mediumblob/longblob: {}", e.getMessage()); - throw new SQLException("Failed to set blob: " + e.getMessage(), e); - } + final byte[] data = (byte[]) value; + statement.setBlob(idx, new ByteArrayInputStream(data)); break; case TEXT, CHAR, VARCHAR, TINYTEXT, MEDIUMTEXT, LONGTEXT, ENUM, SET: if (value == null) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/StorageService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/StorageService.java index e03878b8c19197e4347897c555a91d985c10fb72..c1f546ce4462d60eb08b1f34683efd7f05dca0f9 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/StorageService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/StorageService.java @@ -15,6 +15,7 @@ public interface StorageService { * @param key The object key. * @return The input stream, if successful. * @throws StorageUnavailableException The object failed to be loaded from the Storage Service. + * @throws StorageNotFoundException The key was not found in the Storage Service. */ InputStream getObject(String bucket, String key) throws StorageUnavailableException, StorageNotFoundException; @@ -24,6 +25,7 @@ public interface StorageService { * @param key The object key. * @return The byte array. * @throws StorageUnavailableException The object failed to be loaded from the Storage Service. + * @throws StorageNotFoundException The key was not found in the Storage Service. */ byte[] getBytes(String key) throws StorageUnavailableException, StorageNotFoundException; @@ -34,6 +36,7 @@ public interface StorageService { * @param key The object key. * @return The byte array. * @throws StorageUnavailableException The object failed to be loaded from the Storage Service. + * @throws StorageNotFoundException The key was not found in the Storage Service. */ byte[] getBytes(String bucket, String key) throws StorageUnavailableException, StorageNotFoundException; @@ -43,6 +46,7 @@ public interface StorageService { * @param key The object key. * @return The export resource, if successful. * @throws StorageUnavailableException The object failed to be loaded from the Storage Service. + * @throws StorageNotFoundException The key was not found in the Storage Service. */ ExportResourceDto getResource(String key) throws StorageUnavailableException, StorageNotFoundException; @@ -53,6 +57,7 @@ public interface StorageService { * @param key The object key. * @return The export resource, if successful. * @throws StorageUnavailableException The object failed to be loaded from the Storage Service. + * @throws StorageNotFoundException The key was not found in the Storage Service. */ ExportResourceDto getResource(String bucket, String key) throws StorageUnavailableException, StorageNotFoundException; diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java index 3c3ff101fead4b51caadc8c207848d2b962f98eb..56250a2917f2083d53bbeec51f6e3b1dae0e0cd1 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java @@ -26,16 +26,60 @@ public interface SubsetService { void createQueryStore(PrivilegedContainerDto container, String databaseName) throws SQLException, QueryStoreCreateException; + /** + * Creates a subset from the given statement at given time in the given database. + * + * @param database The database. + * @param statement The subset statement. + * @param timestamp The timestamp as of which the data is queried. If smaller than <now>, historic data is queried. + * @param userId The user id of the creating user. + * @param page The page number. Optional but requires size to be set too. + * @param size The page size. Optional but requires page to be set too. + * @param sortDirection The sort direction. + * @param sortColumn The column that is sorted. + * @return The query result. + * @throws QueryStoreInsertException The query store refused to insert the query. + * @throws SQLException The connection to the database could not be established. + * @throws QueryNotFoundException The query was not found for re-execution. + * @throws TableMalformedException The table is malformed. + * @throws UserNotFoundException The user was not found. + * @throws NotAllowedException The operation is not allowed. + * @throws RemoteUnavailableException The privileged database information could not be found in the Metadata Service. + * @throws DatabaseNotFoundException The database was not found in the Metadata Service. + * @throws MetadataServiceException The Metadata Service responded unexpected. + */ QueryResultDto execute(PrivilegedDatabaseDto database, String statement, Instant timestamp, UUID userId, Long page, Long size, SortTypeDto sortDirection, String sortColumn) throws QueryStoreInsertException, SQLException, QueryNotFoundException, TableMalformedException, UserNotFoundException, NotAllowedException, RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException; + /** + * Re-executes the query of a given subset in the given database. + * + * @param database The database. + * @param query The subset. + * @param page The page number. Optional but requires size to be set too. + * @param size The page size. Optional but requires page to be set too. + * @param sortDirection The sort direction. + * @param sortColumn The column that is sorted. + * @return The query result. + * @throws TableMalformedException The table is malformed. + * @throws SQLException The connection to the database could not be established. + */ QueryResultDto reExecute(PrivilegedDatabaseDto database, QueryDto query, Long page, Long size, SortTypeDto sortDirection, String sortColumn) throws TableMalformedException, SQLException; + /** + * Counts the subset row count of a query of a given subset in the given database. + * + * @param database The database. + * @param query The subset. + * @return The row count. + * @throws TableMalformedException The table is malformed. + * @throws SQLException The connection to the database could not be established. + */ Long reExecuteCount(PrivilegedDatabaseDto database, QueryDto query) throws TableMalformedException, SQLException, QueryMalformedException; @@ -45,15 +89,45 @@ public interface SubsetService { * @param database The database. * @param filterPersisted Optional filter to only display persisted queries, or non-persisted queries. * @return The list of queries. + * @throws SQLException The connection to the database could not be established. + * @throws QueryNotFoundException The query was not found for re-execution. + * @throws RemoteUnavailableException The privileged database information could not be found in the Metadata Service. + * @throws DatabaseNotFoundException The database was not found in the Metadata Service. + * @throws MetadataServiceException The Metadata Service responded unexpected. */ List<QueryDto> findAll(PrivilegedDatabaseDto database, Boolean filterPersisted) throws SQLException, - QueryNotFoundException, NotAllowedException, RemoteUnavailableException, DatabaseNotFoundException, - MetadataServiceException; + QueryNotFoundException, RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException; + /** + * Exports a subset by re-executing the query in a given database with given timestamp to a given filename. + * + * @param database The database. + * @param query The query. + * @param timestamp The timestamp. + * @param filename The filename. + * @return The exported subset. + * @throws SQLException The connection to the database could not be established. + * @throws QueryMalformedException The mapped export query produced a database error. + * @throws SidecarExportException The sidecar of the given database failed to communicate. + * @throws StorageNotFoundException The exported subset was not found from the key provided by the sidecar in the Storage Service. + * @throws StorageUnavailableException The communication to the Storage Service failed. + * @throws RemoteUnavailableException The privileged database information could not be found in the Metadata Service. + */ ExportResourceDto export(PrivilegedDatabaseDto database, QueryDto query, Instant timestamp, String filename) throws SQLException, QueryMalformedException, SidecarExportException, StorageNotFoundException, - StorageUnavailableException, MetadataServiceException, RemoteUnavailableException; + StorageUnavailableException, RemoteUnavailableException; + /** + * Executes a subset query without saving it. + * + * @param database The database. + * @param statement The subset query. + * @param timestamp The timestamp. + * @return The row count. + * @throws SQLException The connection to the database could not be established. + * @throws QueryMalformedException The mapped query produced a database error. + * @throws TableMalformedException The database table is malformed. + */ Long executeCountNonPersistent(PrivilegedDatabaseDto database, String statement, Instant timestamp) throws SQLException, QueryMalformedException, TableMalformedException; @@ -63,11 +137,15 @@ public interface SubsetService { * @param database The database. * @param queryId The query id. * @return The query. - * @throws QueryNotFoundException The query store did not return a query + * @throws QueryNotFoundException The query store did not return a query. + * @throws SQLException The connection to the database could not be established. + * @throws RemoteUnavailableException The privileged database information could not be found in the Metadata Service. + * @throws UserNotFoundException The user that created the query was not found in the Metadata Service. + * @throws DatabaseNotFoundException The database metadata was not found in the Metadata Service. + * @throws MetadataServiceException Communication with the Metadata Service failed. */ QueryDto findById(PrivilegedDatabaseDto database, Long queryId) throws QueryNotFoundException, SQLException, - NotAllowedException, RemoteUnavailableException, UserNotFoundException, DatabaseNotFoundException, - MetadataServiceException; + RemoteUnavailableException, UserNotFoundException, DatabaseNotFoundException, MetadataServiceException; /** * Inserts a query and metadata to the query store of a given database id. @@ -75,7 +153,9 @@ public interface SubsetService { * @param database The database. * @param query The query statement. * @param userId The user id. - * @return The stored query on success + * @return The stored query id on success. + * @throws SQLException The connection to the database could not be established. + * @throws QueryStoreInsertException The query store failed to insert the query. */ Long storeQuery(PrivilegedDatabaseDto database, String query, Instant timestamp, UUID userId) throws SQLException, QueryStoreInsertException; @@ -83,15 +163,21 @@ public interface SubsetService { /** * Persists a query to be displayed in the frontend. * - * @param database The database id. + * @param database The database. * @param queryId The query id. * @param persist If true, the query is retained in the query store, ephemeral otherwise. + * @throws SQLException The connection to the database could not be established. + * @throws QueryStorePersistException The query store failed to persist/unpersist the query. */ void persist(PrivilegedDatabaseDto database, Long queryId, Boolean persist) throws SQLException, QueryStorePersistException; /** * Deletes the stale queries that have not been persisted within 24 hours. + * + * @param database The database. + * @throws SQLException The connection to the database could not be established. + * @throws QueryStoreGCException The query store failed to delete stale queries. */ void deleteStaleQueries(PrivilegedDatabaseDto database) throws SQLException, QueryStoreGCException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java index 53d93d35dfaa11d4ec40ef5e00b73050ebea91c5..7ebd403b6903737c07f2197593ab6229851c32e3 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java @@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.sql.*; import java.time.Instant; import java.util.LinkedList; diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java index 557d1e49aba58b799f3dfa65fea8c4d0a8f7cf4c..fe267fe35ab4c1b7f2cd7e658baa4b765d4d28e2 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java @@ -749,6 +749,8 @@ public interface MetadataMapper { }) UserDetailsDto userDtoToUserDetailsDto(UserDto data); + User userDtoToUser(at.tuwien.api.keycloak.UserDto data); + /* keep */ @Mappings({ @Mapping(target = "name", expression = "java(userToFullName(data))"), diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java index 4be54d5edd1ed39168b97a00177d87d09a7a87ee..b81a8142f727972c95ce0ea5573b99e3d1e59be9 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java @@ -68,13 +68,21 @@ public class UserEndpoint { mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = UserBriefDto.class)))}), }) - public ResponseEntity<List<UserBriefDto>> findAll() { - log.debug("endpoint find all users"); - final List<UserBriefDto> users = userService.findAll() - .stream() - .map(userMapper::userToUserBriefDto) - .toList(); - return ResponseEntity.ok(users); + public ResponseEntity<List<UserBriefDto>> findAll(@RequestParam(required = false) String username) { + log.debug("endpoint find all users, username={}", username); + if (username == null) { + return ResponseEntity.ok(userService.findAll() + .stream() + .map(userMapper::userToUserBriefDto) + .toList()); + } + try { + log.trace("filter by username: {}", username); + return ResponseEntity.ok(List.of(userMapper.userToUserBriefDto(userService.findByUsername(username)))); + } catch (UserNotFoundException e) { + log.trace("filter by username {} failed: return empty list", username); + return ResponseEntity.ok(List.of()); + } } @PostMapping diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java index f5e413e0e9de10192bca89d4fe823db02924712d..ad4cef5bbea6cb262607ebbad44f6ec8148b6f9b 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java @@ -45,18 +45,37 @@ public class UserEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void findAll_anonymous_succeeds() { + public void findAll_anonymous_succeeds() throws UserNotFoundException { /* test */ - findAll_generic(); + final List<UserBriefDto> response = findAll_generic(null, null); + assertEquals(2, response.size()); } @Test @WithMockUser(username = USER_1_USERNAME) - public void findAll_noRole_succeeds() { + public void findAll_noRole_succeeds() throws UserNotFoundException { /* test */ - findAll_generic(); + final List<UserBriefDto> response = findAll_generic(null, null); + assertEquals(2, response.size()); + } + + @Test + public void findAll_filterUsername_succeeds() throws UserNotFoundException { + + /* test */ + final List<UserBriefDto> response = findAll_generic(USER_2_USERNAME, USER_2); + assertEquals(1, response.size()); + assertEquals(USER_2_ID, response.get(0).getId()); + } + + @Test + public void findAll_filterUsername_fails() throws UserNotFoundException { + + /* test */ + final List<UserBriefDto> response = findAll_generic(USER_5_USERNAME, null); + assertEquals(0, response.size()); } @Test @@ -100,7 +119,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME) - public void find_self_succeeds() throws NotAllowedException, UserNotFoundException{ + public void find_self_succeeds() throws NotAllowedException, UserNotFoundException { /* test */ find_generic(USER_1_ID, USER_1, USER_1_PRINCIPAL); @@ -231,18 +250,29 @@ public class UserEndpointUnitTest extends AbstractUnitTest { /* ## GENERIC TEST CASES ## */ /* ################################################################################################### */ - protected void findAll_generic() { + protected List<UserBriefDto> findAll_generic(String username, User user) throws UserNotFoundException { /* mock */ - when(userService.findAll()) - .thenReturn(List.of(USER_1, USER_2)); + if (username != null) { + if (user != null) { + when(userService.findByUsername(username)) + .thenReturn(user); + } else { + doThrow(UserNotFoundException.class) + .when(userService) + .findByUsername(username); + } + } else { + when(userService.findAll()) + .thenReturn(List.of(USER_1, USER_2)); + } /* test */ - final ResponseEntity<List<UserBriefDto>> response = userEndpoint.findAll(); + final ResponseEntity<List<UserBriefDto>> response = userEndpoint.findAll(username); assertEquals(HttpStatus.OK, response.getStatusCode()); final List<UserBriefDto> body = response.getBody(); assertNotNull(body); - assertEquals(2, body.size()); + return response.getBody(); } protected void create_generic(SignupRequestDto data, User user, at.tuwien.api.keycloak.UserDto userDto, UUID id) @@ -265,7 +295,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest { } protected void find_generic(UUID id, User user, Principal principal) throws NotAllowedException, - UserNotFoundException{ + UserNotFoundException { /* mock */ if (user != null) { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java index 8183ea90808ea6c7e48e1530ea2a88a62a34df9b..698f7ed524c13bbb3d9fbe5fca37003419f51a4c 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java @@ -1103,7 +1103,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { } @Test - public void getTableStatistics_emptyBody_fails() { + public void getTableStatistics_emptyBody_fails() throws TableNotFoundException, DataServiceException, DataServiceConnectionException { /* mock */ when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableStatisticDto.class))) @@ -1111,9 +1111,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { .build()); /* test */ - assertThrows(DataServiceException.class, () -> { - dataServiceGateway.getTableStatistics(DATABASE_3_ID, TABLE_8_ID); - }); + dataServiceGateway.getTableStatistics(DATABASE_3_ID, TABLE_8_ID); } } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java index d3f75d70602ffb2dad2a6b2adc8797eb7636135c..0b804dbdeab1fe3777614fafc7e4b790249bf569 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -566,7 +566,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* mock */ try { - userEndpoint.findAll(); + userEndpoint.findAll(null); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java index 886911d9f4a770fe8c620d7cc882cd7b4da05c55..0c11de442957cbc6deee1d20d7f51b865017a90b 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java @@ -372,7 +372,8 @@ public class DataServiceGatewayImpl implements DataServiceGateway { } @Override - public TableStatisticDto getTableStatistics(Long databaseId, Long tableId) throws DataServiceConnectionException, DataServiceException, TableNotFoundException { + public TableStatisticDto getTableStatistics(Long databaseId, Long tableId) throws DataServiceConnectionException, + DataServiceException, TableNotFoundException { final ResponseEntity<TableStatisticDto> response; final String path = "/api/database/" + databaseId + "/table/" + tableId + "/statistic"; log.trace("get table statistics at endpoint {} with path {}", gatewayConfig.getDataEndpoint(), path); @@ -392,10 +393,6 @@ public class DataServiceGatewayImpl implements DataServiceGateway { log.error("Failed to analyse table statistic: wrong http code: {}", response.getStatusCode()); throw new DataServiceException("Failed to analyse table statistic: wrong http code: " + response.getStatusCode()); } - if (response.getBody() == null) { - log.error("Failed to analyse table statistic: empty body: {}", response.getStatusCode()); - throw new DataServiceException("Failed to analyse table statistic: empty body: " + response.getStatusCode()); - } return response.getBody(); } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/init/InitHandler.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/init/InitHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..1f7c8ced0a84bf4dc37f41b36b46408a68aa42df --- /dev/null +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/init/InitHandler.java @@ -0,0 +1,59 @@ +package at.tuwien.init; + +import at.tuwien.api.keycloak.UserDto; +import at.tuwien.config.GatewayConfig; +import at.tuwien.config.MetadataConfig; +import at.tuwien.entities.user.User; +import at.tuwien.exception.AuthServiceConnectionException; +import at.tuwien.exception.AuthServiceException; +import at.tuwien.exception.UserNotFoundException; +import at.tuwien.gateway.KeycloakGateway; +import at.tuwien.repository.UserRepository; +import at.tuwien.service.UserService; +import jakarta.annotation.PostConstruct; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +@Profile("!junit") +public class InitHandler { + + private final UserService userService; + private final GatewayConfig gatewayConfig; + private final MetadataConfig metadataConfig; + private final UserRepository userRepository; + private final KeycloakGateway keycloakGateway; + + @Autowired + public InitHandler(UserService userService, GatewayConfig gatewayConfig, MetadataConfig metadataConfig, + UserRepository userRepository, KeycloakGateway keycloakGateway) { + this.userService = userService; + this.gatewayConfig = gatewayConfig; + this.metadataConfig = metadataConfig; + this.userRepository = userRepository; + this.keycloakGateway = keycloakGateway; + } + + @PostConstruct + public void init() throws UserNotFoundException, AuthServiceException, AuthServiceConnectionException { + try { + userService.findByUsername(gatewayConfig.getSystemUsername()); + } catch (UserNotFoundException e) { + log.warn("Failed to find system user with username {} in metadata database", gatewayConfig.getSystemUsername()); + final UserDto user = keycloakGateway.findByUsername(gatewayConfig.getSystemUsername()); + final User entity = User.builder() + .id(user.getId()) + .username(user.getUsername()) + .email(metadataConfig.getAdminEmail()) + .theme("light") + .mariadbPassword(userService.getMariaDbPassword(gatewayConfig.getSystemPassword())) + .language("en") + .build(); + userRepository.save(entity); + log.info("Saved system user with username: {}", gatewayConfig.getSystemUsername()); + } + } +} diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java index 92e0bc37ee2b58eddc9faa86f08d4748caf662f5..8e7c715ad06d434bfdb947136546f686e91835ad 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java @@ -76,4 +76,6 @@ public interface UserService { * @throws EmailExistsException The user with this email already exists. */ void validateEmailNotExists(String email) throws EmailExistsException; + + String getMariaDbPassword(String password); } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index 7ca855974e86b23a5a2ec8abcbc124f97a0e8ab7..f88ba5c28f4d928757a501d8ed1a3f4effb018d8 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -270,6 +270,9 @@ public class TableServiceImpl implements TableService { DatabaseNotFoundException, SearchServiceConnectionException, MalformedException, TableNotFoundException, DataServiceException, DataServiceConnectionException { final TableStatisticDto statistic = dataServiceGateway.getTableStatistics(table.getTdbid(), table.getId()); + if (statistic == null) { + return; + } table.setNumRows(statistic.getRows()); table.setDataLength(statistic.getDataLength()); table.setAvgRowLength(statistic.getAvgRowLength()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java index 547a01c2fab73cf410673778747f0545c40907de..7e07b68d1c965ed42261ab508273770be50a6454 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java @@ -105,6 +105,7 @@ public class UserServiceImpl implements UserService { } } + @Override public String getMariaDbPassword(String password) { final byte[] utf8 = password.getBytes(StandardCharsets.UTF_8); return "*" + DigestUtils.sha1Hex(DigestUtils.sha1(utf8)).toUpperCase(); diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java index 156563a041c833376f850465032f28b69602e020..23ba450312b224d76e729613c005489fb7cc8826 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java @@ -83,6 +83,7 @@ public abstract class AbstractUnitTest extends BaseTest { TABLE_5.setColumns(new LinkedList<>(TABLE_5_COLUMNS)); TABLE_5.setConstraints(TABLE_5_CONSTRAINTS); TABLE_5_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_5_COLUMNS_DTO)); + TABLE_5_PRIVILEGED_DTO.setConstraints(TABLE_5_CONSTRAINTS_DTO); TABLE_5_PRIVILEGED_DTO.setDatabase(DATABASE_2_PRIVILEGED_DTO); TABLE_5_DTO.setColumns(TABLE_5_COLUMNS_DTO); TABLE_5_DTO.setConstraints(TABLE_5_CONSTRAINTS_DTO); @@ -113,6 +114,7 @@ public abstract class AbstractUnitTest extends BaseTest { TABLE_8_DTO.setConstraints(TABLE_8_CONSTRAINTS_DTO); TABLE_8_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_8_COLUMNS_DTO)); TABLE_8_PRIVILEGED_DTO.setConstraints(TABLE_8_CONSTRAINTS_DTO); + TABLE_8_PRIVILEGED_DTO.setDatabase(DATABASE_3_PRIVILEGED_DTO); VIEW_5.setDatabase(DATABASE_3); VIEW_5.setColumns(VIEW_5_COLUMNS); IDENTIFIER_6.setDatabase(DATABASE_3); diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java index 0341a7a5dfad8f3a218dd195631ff956289b6b15..dfaf58d995e8e57ef8fcc017c551ff0720bc8a4a 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java @@ -90,6 +90,8 @@ import java.math.BigInteger; import java.nio.charset.Charset; import java.security.Principal; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneOffset; import java.util.*; import java.util.stream.Collectors; @@ -380,6 +382,7 @@ public abstract class BaseTest { public final static String USER_BROKER_USERNAME = "guest"; public final static String USER_BROKER_PASSWORD = "guest"; + public final static UUID USER_LOCAL_ADMIN_ID = UUID.fromString("a54dcb2e-a644-4e82-87e7-05a96413983d"); public final static String USER_LOCAL_ADMIN_USERNAME = "admin"; public final static String USER_LOCAL_ADMIN_PASSWORD = "admin"; @@ -1679,6 +1682,40 @@ public abstract class BaseTest { .owner(USER_1_BRIEF_DTO) .build(); + public final static Long TABLE_1_DATA_COUNT = 3L; + public final static QueryResultDto TABLE_1_DATA_DTO = QueryResultDto.builder() + .headers(new LinkedList<>(List.of(new HashMap<>() {{ + put("id", 0); + put("date", 1); + put("location", 2); + put("mintemp", 3); + put("rainfall", 4); + }}))) + .result(new LinkedList<>(List.of( + new HashMap<>() {{ + put("id", BigInteger.valueOf(1L)); + put("date", LocalDate.of(2008, 12, 1).atStartOfDay().toInstant(ZoneOffset.UTC)); + put("location", "Albury"); + put("mintemp", 13.4); + put("rainfall", 0.6); + }}, + new HashMap<>() {{ + put("id", BigInteger.valueOf(2L)); + put("date", LocalDate.of(2008, 12, 2).atStartOfDay().toInstant(ZoneOffset.UTC)); + put("location", "Albury"); + put("mintemp", 7.4); + put("rainfall", 0); + }}, + new HashMap<>() {{ + put("id", BigInteger.valueOf(3L)); + put("date", LocalDate.of(2008, 12, 3).atStartOfDay().toInstant(ZoneOffset.UTC)); + put("location", "Albury"); + put("mintemp", 12.9); + put("rainfall", 0); + }} + ))) + .build(); + public final static Long TABLE_2_ID = 2L; public final static String TABLE_2_NAME = "Weather Location"; public final static String TABLE_2_INTERNALNAME = "weather_location"; @@ -1846,6 +1883,8 @@ public abstract class BaseTest { .build(); public final static ConstraintsCreateDto TABLE_3_CONSTRAINTS_INVALID_CREATE_DTO = ConstraintsCreateDto.builder() + .checks(new LinkedHashSet<>()) + .primaryKey(new LinkedHashSet<>()) // <<<< .uniques(new LinkedList<>()) .foreignKeys(List.of(ForeignKeyCreateDto.builder() .referencedTable("weather_location") @@ -2169,7 +2208,7 @@ public abstract class BaseTest { public final static ConstraintsCreateDto TABLE_4_CONSTRAINTS_CREATE_DTO = ConstraintsCreateDto.builder() .checks(new LinkedHashSet<>()) - .primaryKey(new LinkedHashSet<>()) + .primaryKey(new LinkedHashSet<>(Set.of("Timestamp"))) .foreignKeys(new LinkedList<>()) .uniques(List.of(List.of("Timestamp"))) .build(); @@ -2404,37 +2443,32 @@ public abstract class BaseTest { public final static Long COLUMN_8_1_ID = 72L; public final static Integer COLUMN_8_1_ORDINALPOS = 0; - public final static Boolean COLUMN_8_1_PRIMARY = true; public final static String COLUMN_8_1_NAME = "ID"; public final static String COLUMN_8_1_INTERNAL_NAME = "id"; public final static TableColumnType COLUMN_8_1_TYPE = TableColumnType.BIGINT; public final static ColumnTypeDto COLUMN_8_1_TYPE_DTO = ColumnTypeDto.BIGINT; - public final static Long COLUMN_8_1_DATE_FORMAT = null; public final static Boolean COLUMN_8_1_NULL = false; public final static Boolean COLUMN_8_1_AUTO_GENERATED = true; - public final static String COLUMN_8_1_FOREIGN_KEY = null; - public final static String COLUMN_8_1_CHECK = null; - public final static List<String> COLUMN_8_1_ENUM_VALUES = null; - public final static List<String> COLUMN_8_1_ENUM_VALUES_DTO = null; - public final static List<String> COLUMN_8_1_SET_VALUES = null; - public final static List<String> COLUMN_8_1_SET_VALUES_DTO = null; public final static Long COLUMN_8_2_ID = 73L; public final static Integer COLUMN_8_2_ORDINALPOS = 1; - public final static Boolean COLUMN_8_2_PRIMARY = true; public final static String COLUMN_8_2_NAME = "Value"; public final static String COLUMN_8_2_INTERNAL_NAME = "value"; - public final static TableColumnType COLUMN_8_2_TYPE = TableColumnType.INT; - public final static ColumnTypeDto COLUMN_8_2_TYPE_DTO = ColumnTypeDto.INT; - public final static Long COLUMN_8_2_DATE_FORMAT = null; - public final static Boolean COLUMN_8_2_NULL = true; + public final static TableColumnType COLUMN_8_2_TYPE = TableColumnType.DECIMAL; + public final static ColumnTypeDto COLUMN_8_2_TYPE_DTO = ColumnTypeDto.DECIMAL; + public final static Long COLUMN_8_2_SIZE = 10L; + public final static Long COLUMN_8_2_D = 10L; + public final static Boolean COLUMN_8_2_NULL = false; public final static Boolean COLUMN_8_2_AUTO_GENERATED = false; - public final static String COLUMN_8_2_FOREIGN_KEY = null; - public final static String COLUMN_8_2_CHECK = null; - public final static List<String> COLUMN_8_2_ENUM_VALUES = null; - public final static List<String> COLUMN_8_2_ENUM_VALUES_DTO = null; - public final static List<String> COLUMN_8_2_SET_VALUES = null; - public final static List<String> COLUMN_8_2_SET_VALUES_DTO = null; + + public final static Long COLUMN_8_3_ID = 74L; + public final static Integer COLUMN_8_3_ORDINALPOS = 2; + public final static String COLUMN_8_3_NAME = "raw"; + public final static String COLUMN_8_3_INTERNAL_NAME = "raw"; + public final static TableColumnType COLUMN_8_3_TYPE = TableColumnType.LONGBLOB; + public final static ColumnTypeDto COLUMN_8_3_TYPE_DTO = ColumnTypeDto.LONGBLOB; + public final static Boolean COLUMN_8_3_NULL = true; + public final static Boolean COLUMN_8_3_AUTO_GENERATED = false; public final static ColumnBriefDto TABLE_8_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() .id(COLUMN_8_1_ID) @@ -2462,6 +2496,18 @@ public abstract class BaseTest { .columnType(COLUMN_8_2_TYPE) .isNullAllowed(COLUMN_8_2_NULL) .autoGenerated(COLUMN_8_2_AUTO_GENERATED) + .size(COLUMN_8_2_SIZE) + .d(COLUMN_8_2_D) + .build(), + TableColumn.builder() + .id(COLUMN_8_3_ID) + .ordinalPosition(COLUMN_8_3_ORDINALPOS) + .table(TABLE_8) + .name(COLUMN_8_3_NAME) + .internalName(COLUMN_8_3_INTERNAL_NAME) + .columnType(COLUMN_8_3_TYPE) + .isNullAllowed(COLUMN_8_3_NULL) + .autoGenerated(COLUMN_8_3_AUTO_GENERATED) .build()); public final static List<ColumnDto> TABLE_8_COLUMNS_DTO = List.of(ColumnDto.builder() @@ -2483,6 +2529,16 @@ public abstract class BaseTest { .columnType(COLUMN_8_2_TYPE_DTO) .isNullAllowed(COLUMN_8_2_NULL) .autoGenerated(COLUMN_8_2_AUTO_GENERATED) + .build(), + ColumnDto.builder() + .id(COLUMN_8_3_ID) + .ordinalPosition(COLUMN_8_3_ORDINALPOS) + .table(TABLE_8_DTO) + .name(COLUMN_8_3_NAME) + .internalName(COLUMN_8_3_INTERNAL_NAME) + .columnType(COLUMN_8_3_TYPE_DTO) + .isNullAllowed(COLUMN_8_3_NULL) + .autoGenerated(COLUMN_8_3_AUTO_GENERATED) .build()); public final static Long TABLE_8_DATA_COUNT = 6L; @@ -2492,12 +2548,36 @@ public abstract class BaseTest { put(COLUMN_8_2_INTERNAL_NAME, 1); }}))) .result(new LinkedList<>(List.of( - Map.of(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(1L), COLUMN_8_2_INTERNAL_NAME, 11.2), - Map.of(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(2L), COLUMN_8_2_INTERNAL_NAME, 11.3), - Map.of(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(3L), COLUMN_8_2_INTERNAL_NAME, 11.4), - Map.of(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(4L), COLUMN_8_2_INTERNAL_NAME, 11.9), - Map.of(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(5L), COLUMN_8_2_INTERNAL_NAME, 12.3), - Map.of(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(6L), COLUMN_8_2_INTERNAL_NAME, 23.1) + new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(1L)); + put(COLUMN_8_2_INTERNAL_NAME, 11.2); + put(COLUMN_8_3_INTERNAL_NAME, null); + }}, + new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(2L)); + put(COLUMN_8_2_INTERNAL_NAME, 11.3); + put(COLUMN_8_3_INTERNAL_NAME, null); + }}, + new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(3L)); + put(COLUMN_8_2_INTERNAL_NAME, 11.4); + put(COLUMN_8_3_INTERNAL_NAME, null); + }}, + new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(4L)); + put(COLUMN_8_2_INTERNAL_NAME, 11.9); + put(COLUMN_8_3_INTERNAL_NAME, null); + }}, + new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(5L)); + put(COLUMN_8_2_INTERNAL_NAME, 12.3); + put(COLUMN_8_3_INTERNAL_NAME, null); + }}, + new HashMap<>() {{ + put(COLUMN_8_1_INTERNAL_NAME, BigInteger.valueOf(6L)); + put(COLUMN_8_2_INTERNAL_NAME, 23.1); + put(COLUMN_8_3_INTERNAL_NAME, null); + }} ))) .build(); @@ -2876,6 +2956,13 @@ public abstract class BaseTest { .uniques(List.of(List.of("date"))) .build(); + public final static ConstraintsCreateDto TABLE_1_CONSTRAINTS_CREATE_INVALID_DTO = ConstraintsCreateDto.builder() + .checks(new LinkedHashSet<>()) + .primaryKey(new LinkedHashSet<>()) + .foreignKeys(new LinkedList<>()) + .uniques(List.of(List.of("date"))) + .build(); + public final static TableCreateDto TABLE_1_CREATE_DTO = TableCreateDto.builder() .name(TABLE_1_NAME) .description(TABLE_1_DESCRIPTION) @@ -2890,6 +2977,13 @@ public abstract class BaseTest { .constraints(TABLE_1_CONSTRAINTS_CREATE_DTO) .build(); + public final static at.tuwien.api.database.table.internal.TableCreateDto TABLE_1_CREATE_INTERNAL_INVALID_DTO = at.tuwien.api.database.table.internal.TableCreateDto.builder() + .name(TABLE_1_NAME) + .description(TABLE_1_DESCRIPTION) + .columns(TABLE_1_COLUMNS_CREATE_DTO) + .constraints(TABLE_1_CONSTRAINTS_CREATE_INVALID_DTO) + .build(); + public final static List<TableColumn> TABLE_2_COLUMNS = List.of(TableColumn.builder() .id(6L) .ordinalPosition(0) @@ -5105,6 +5199,8 @@ public abstract class BaseTest { public final static String VIEW_3_QUERY = "select w.`mintemp`, w.`rainfall`, w.`location`, m.`date` from `weather_aus` w join `junit2` m on m.`location` = w.`location` and m.`date` = w.`date`"; public final static String VIEW_3_QUERY_HASH = "bbbaa56a5206b3dc3e6cf9301b0db9344eb6f19b100c7b88550ffb597a0bd255"; + public final static Long VIEW_3_DATA_COUNT = 3L; + public final static List<ViewColumnDto> VIEW_3_COLUMNS_DTO = List.of( ViewColumnDto.builder() .id(8L) @@ -7118,6 +7214,13 @@ public abstract class BaseTest { .user(USER_2) .build(); + public final static DatabaseAccessDto DATABASE_1_USER_2_READ_ACCESS_DTO = DatabaseAccessDto.builder() + .type(AccessTypeDto.READ) + .hdbid(DATABASE_1_ID) + .huserid(USER_2_ID) + .user(USER_2_DTO) + .build(); + public final static DatabaseAccess DATABASE_1_USER_2_WRITE_OWN_ACCESS = DatabaseAccess.builder() .type(AccessType.WRITE_OWN) .hdbid(DATABASE_1_ID) @@ -7141,6 +7244,13 @@ public abstract class BaseTest { .user(USER_2) .build(); + public final static DatabaseAccessDto DATABASE_1_USER_2_WRITE_ALL_ACCESS_DTO = DatabaseAccessDto.builder() + .type(AccessTypeDto.WRITE_ALL) + .hdbid(DATABASE_1_ID) + .huserid(USER_2_ID) + .user(USER_2_DTO) + .build(); + public final static DatabaseAccess DATABASE_1_USER_3_READ_ACCESS = DatabaseAccess.builder() .type(AccessType.READ) .hdbid(DATABASE_1_ID) diff --git a/dbrepo-ui/components/database/DatabaseToolbar.vue b/dbrepo-ui/components/database/DatabaseToolbar.vue index 0c87e5599c688b3c4feebfbadb09a8868071d54d..741252475c31ef841260266d3d4fcb459b35cd64 100644 --- a/dbrepo-ui/components/database/DatabaseToolbar.vue +++ b/dbrepo-ui/components/database/DatabaseToolbar.vue @@ -135,6 +135,12 @@ export default { } return this.access.type === 'write_all' || this.access.type === 'write_own' }, + hasReadAccess () { + if (!this.access) { + return false + } + return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own' + }, canImportCsv () { if (!this.user || !this.hasWriteAccess) { return false @@ -142,10 +148,13 @@ export default { return this.roles.includes('insert-table-data') }, canCreateSubset () { - if (!this.user) { + if (!this.database) { return false } - return this.roles.includes('execute-query') + if (this.database.is_public) { + return true + } + return this.hasReadAccess }, canCreateView () { if (!this.user || !this.isOwner) { diff --git a/dbrepo-ui/components/table/TableImport.vue b/dbrepo-ui/components/table/TableImport.vue index 2451a7c3acb18722e8e4b0ebdb9fe10effcd554d..e55db4130e893810d22bf93ea4308c6e97a2b060 100644 --- a/dbrepo-ui/components/table/TableImport.vue +++ b/dbrepo-ui/components/table/TableImport.vue @@ -149,7 +149,8 @@ <v-row v-if="step > 1 && suggestedAnalyseSeparator && providedSeparator !== analysedSeparator" dense> - <v-col> + <v-col + md="8"> <v-alert border="start" color="warning"> @@ -164,7 +165,8 @@ <v-row v-if="step > 1 && suggestedAnalyseLineTerminator && providedTerminator !== analysedTerminator" dense> - <v-col> + <v-col + md="8"> <v-alert border="start" color="warning"> @@ -261,23 +263,20 @@ direction="vertical"> <v-container> <v-row - v-if="rowCount" dense> <v-col md="8"> <v-alert border="start" color="success"> - <span v-text="$t(`pages.table.subpages.import.summary.prefix`)"/> - <strong> {{ rowCount }} </strong> - <span v-text="$t('pages.table.subpages.import.summary.suffix')"/> + <span v-text="$t(`pages.table.subpages.import.summary.text`)"/> </v-alert> </v-col> </v-row> <v-row> <v-col> <v-btn - v-if="rowCount !== null" + v-if="step === 3" color="secondary" :disabled="step !== 3 || disabled" size="small" @@ -457,10 +456,6 @@ export default { const toast = useToastInstance() toast.success(this.$t('success.import.dataset')) this.cacheStore.reloadDatabase() - tableService.getCount(this.$route.params.database_id, this.tableId, null) - .then((rowCount) => { - this.rowCount = rowCount - }) this.step = 3 this.validStep3 = true this.loadingImport = false @@ -542,7 +537,7 @@ export default { this.$emit('analyse', { columns: this.columns, filename, - line_termination, + line_termination: line_termination === '\\n' ? '\n' : JSON.stringify(line_termination).replaceAll('"', ''), separator: this.tableImport.separator, skip_lines: this.tableImport.skip_lines, quote: this.tableImport.quote, diff --git a/dbrepo-ui/components/table/TableSchema.vue b/dbrepo-ui/components/table/TableSchema.vue index 25c4f66cb55993ff6e5f482b4bf63749021ec155..da30905fa30a9327ca90266bd2ee4088fab46c70 100644 --- a/dbrepo-ui/components/table/TableSchema.vue +++ b/dbrepo-ui/components/table/TableSchema.vue @@ -176,7 +176,7 @@ variant="flat" size="small" :loading="loading" - :disabled="disabled || !valid || this.columns.length === 0" + :disabled="disabled || !valid || showPrimaryKeyWarning || this.columns.length === 0" :text="submitText" @click="submit" /> </v-col> diff --git a/dbrepo-ui/composables/query-service.ts b/dbrepo-ui/composables/query-service.ts index abcd928b66d0350fd855ae380b959a33818b8f3d..f5d805b958f27676f7f1031bfe56af16721b4378 100644 --- a/dbrepo-ui/composables/query-service.ts +++ b/dbrepo-ui/composables/query-service.ts @@ -206,15 +206,15 @@ export const useQueryService = (): any => { {value: 'char', text: 'CHAR(size)', defaultSize: 1, defaultD: null, quoted: true, isBuildable: true}, {value: 'date', text: 'DATE', defaultSize: null, defaultD: null, quoted: true, isBuildable: true}, {value: 'datetime', text: 'DATETIME(fsp)', defaultSize: null, defaultD: null, quoted: true, isBuildable: true}, - {value: 'decimal', text: 'DECIMAL(size, d)', defaultSize: 10, defaultD: 4, quoted: false, isBuildable: true}, - {value: 'double', text: 'DOUBLE(size, d)', defaultSize: 25, defaultD: 4, quoted: false, isBuildable: true}, + {value: 'decimal', text: 'DECIMAL(size, d)', defaultSize: 40, defaultD: 10, quoted: false, isBuildable: true}, + {value: 'double', text: 'DOUBLE(size, d)', defaultSize: 40, defaultD: 10, quoted: false, isBuildable: true}, {value: 'enum', text: 'ENUM(val1,val2,...)', defaultSize: null, defaultD: null, quoted: true, isBuildable: true}, {value: 'float', text: 'FLOAT(p)', defaultSize: 24, defaultD: null, quoted: false, isBuildable: true}, {value: 'int', text: 'INT(size)', defaultSize: 255, defaultD: null, quoted: false, isBuildable: true}, {value: 'longblob', text: 'LONGBLOB', defaultSize: null, defaultD: null, quoted: false, isBuildable: false}, {value: 'longtext', text: 'LONGTEXT', defaultSize: null, defaultD: null, quoted: true, isBuildable: true}, {value: 'mediumblob', text: 'MEDIUMBLOB', defaultSize: null, defaultD: null, quoted: false, isBuildable: false}, - {value: 'mediumint', text: 'MEDIUMINT(size)', defaultSize: 10, defaultD: null, quoted: false, isBuildable: true}, + {value: 'mediumint', text: 'MEDIUMINT(size)', defaultSize: 40, defaultD: null, quoted: false, isBuildable: true}, {value: 'mediumtext', text: 'MEDIUMTEXT', defaultSize: null, defaultD: null, quoted: true, isBuildable: true}, {value: 'set', text: 'SET(val1,val2,...)', defaultSize: null, defaultD: null, quoted: true, isBuildable: true}, {value: 'smallint', text: 'SMALLINT(size)', defaultSize: 10, defaultD: null, quoted: false, isBuildable: true}, diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index 41620761c58257affb380be4be741489c8f77ce8..fb4c40266b6dcc1f6f15a340cd400b8d4a0b3cd4 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -225,25 +225,21 @@ export default { }, }, watch: { - '$route.params.database_id': { - handler (newId, oldId) { - if (newId === oldId) { + '$route.params': { + handler (newObj, oldObj) { + if (!newObj.database_id) { return } - this.cacheStore.setRouteDatabase(newId) + /* load database and optional access */ + this.cacheStore.setRouteDatabase(newObj.database_id) if (this.user) { - this.userStore.setRouteAccess(newId) + this.userStore.setRouteAccess(newObj.database_id) } - }, - deep: true, - immediate: true - }, - '$route.params.table_id': { - handler (newId, oldId) { - if (newId === oldId) { + if (!newObj.table_id) { return } - this.cacheStore.setRouteTable(this.$route.params.database_id, newId) + /* load table */ + this.cacheStore.setRouteTable(newObj.database_id, newObj.table_id) }, deep: true, immediate: true diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index be8ddfcc2317d441d3a8b6edacd4b3f5a663c6af..c07ee7ac48b99bcd3ce0a9055fc1846c686c5789 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -332,7 +332,7 @@ "title": "Dataset Structure", "text": "the table schema manually.", "primary": { - "warn": "No primary key column(s) selected. Please select a column that uniquely identifies data entries." + "warn": "No primary key column(s) selected. Please select one or more column(s) that uniquely identify data entries." } }, "dataset": { @@ -403,8 +403,7 @@ }, "summary": { "title": "Summary", - "prefix": "Imported", - "suffix": "rows from dataset" + "text": "Successfully imported dataset" }, "analyse": { "text": "Upload & Analyse" @@ -448,7 +447,7 @@ } }, "schema": { - "title": "System Versioned", + "title": "Schema", "subtitle": "Table Constraints", "bullet": "●", "assign": "Assign", diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts index 454cadf9090d23f92198c63e033ba7061221a29c..dc65c7ac3aca83cc1493bf69d20447273219c677 100644 --- a/dbrepo-ui/nuxt.config.ts +++ b/dbrepo-ui/nuxt.config.ts @@ -2,6 +2,7 @@ import { transformAssetUrls } from 'vite-plugin-vuetify' const proxy : any = {} +/* proxies the backend calls, >>NOT<< the frontend calls (clicking) */ if (process.env.NODE_ENV === 'development') { const api = 'http://localhost' proxy['/api'] = api @@ -12,7 +13,7 @@ if (process.env.NODE_ENV === 'development') { '^/pid': '/pid' } } - process.env.NUXT_PUBLIC_API_SERVER = 'http://localhost' + process.env.NUXT_PUBLIC_API_SERVER = api } /** diff --git a/dbrepo-ui/pages/database/[database_id]/subset/create.vue b/dbrepo-ui/pages/database/[database_id]/subset/create.vue index 62241db5050a1aebe1924cf6154d585fb1eb85e9..5202bc633626e135e263056fa39b5022676c1575 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/create.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/create.vue @@ -1,5 +1,5 @@ <template> - <div v-if="canExecuteQuery"> + <div v-if="canCreateSubset"> <Builder /> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> @@ -8,6 +8,7 @@ <script> import { useUserStore } from '@/stores/user' import Builder from '@/components/subset/Builder.vue' +import {useCacheStore} from "~/stores/cache.js"; export default { components: { @@ -34,6 +35,7 @@ export default { disabled: true } ], + cacheStore: useCacheStore(), userStore: useUserStore() } }, @@ -44,14 +46,26 @@ export default { roles () { return this.userStore.getRoles }, + database () { + return this.cacheStore.getDatabase + }, access () { return this.userStore.getAccess }, - canExecuteQuery () { - if (!this.roles) { + hasReadAccess () { + if (!this.access) { return false } - return this.roles.includes('execute-query') + return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own' + }, + canCreateSubset () { + if (!this.database) { + return false + } + if (this.database.is_public) { + return true + } + return this.hasReadAccess } } } diff --git a/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue index b60de5df3045d3f51bcbb2c203afdff18be87699..6642e89bf41e31326a42b5bae1e1100e0f04f5fb 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue @@ -20,6 +20,7 @@ <v-stepper-header> <v-stepper-item :title="$t('pages.table.subpages.create.information.title')" + :complete="valid" :value="1" /> </v-stepper-header> <v-stepper-window diff --git a/dbrepo-ui/stores/cache.js b/dbrepo-ui/stores/cache.js index c733e8d48a4a3d77f4fce9eb9b1558db59d0306e..5004b1beb0170862f5062627656fcf6e8844dd7a 100644 --- a/dbrepo-ui/stores/cache.js +++ b/dbrepo-ui/stores/cache.js @@ -78,6 +78,7 @@ export const useCacheStore = defineStore('cache', { setRouteTable (databaseId, tableId) { if (!databaseId || !tableId) { this.table = null + console.error('Cannot set route table: missing database id', databaseId, 'or table id', tableId) return } const tableService = useTableService() diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py index fdae96e4904bab0a3b2142a4f45a9a5707cd37fc..7db044958a045c70568268b01eb7b1bccd901cd7 100644 --- a/lib/python/dbrepo/RestClient.py +++ b/lib/python/dbrepo/RestClient.py @@ -1588,7 +1588,7 @@ class RestClient: else: if timestamp is not None: url += f'?timestamp={timestamp.strftime("%Y-%m-%dT%H:%M:%SZ")}' - response = self._wrapper(method="post", url=url, force_auth=True, headers={"Accept": "application/json"}, + response = self._wrapper(method="post", url=url, headers={"Accept": "application/json"}, payload=ExecuteQuery(statement=query)) if response.status_code == 201: body = response.json() diff --git a/lib/python/tests/test_unit_query.py b/lib/python/tests/test_unit_query.py index d2de6f8278bc33a123cf38525493ec93532ce87a..e5448c8a3774e4625c9992ce9c09ff79a18b83a0 100644 --- a/lib/python/tests/test_unit_query.py +++ b/lib/python/tests/test_unit_query.py @@ -64,16 +64,19 @@ class QueryUnitTest(unittest.TestCase): except NotExistsError: pass - def test_create_subset_not_auth_fails(self): + def test_create_subset_not_auth_succeeds(self): with requests_mock.Mocker() as mock: + exp = Result(result=[{'id': 1, 'username': 'foo'}, {'id': 2, 'username': 'bar'}], + headers=[{'id': 0, 'username': 1}], + id=None) # mock - mock.post('/api/database/1/subset', status_code=417) + mock.post('/api/database/1/subset', json=exp.model_dump(), status_code=201) # test - try: - response = RestClient().create_subset(database_id=1, - query="SELECT id, username FROM some_table WHERE id IN (1,2)") - except AuthenticationError: - pass + + client = RestClient() + response = client.create_subset(database_id=1, page=0, size=10, + query="SELECT id, username FROM some_table WHERE id IN (1,2)") + self.assertEqual(exp, response) def test_find_query_succeeds(self): with requests_mock.Mocker() as mock: