diff --git a/.docker/dist.tar.gz b/.docker/dist.tar.gz index 41421af414e1f200cced2348ab00977bde12c8b1..411c92f64cd600841b98f1375c32b920895df2e0 100644 Binary files a/.docker/dist.tar.gz and b/.docker/dist.tar.gz differ diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml index ce5680133fcfe2690220850359b9f0dc5c28f3c8..54b7b8edbbe0652379fc74c5a81a58ada8a57e3a 100644 --- a/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -275,6 +275,10 @@ services: container_name: dbrepo-ui hostname: ui image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.4.5 + environment: + NUXT_PUBLIC_API_CLIENT: "${BASE_URL:-http://localhost}" + NUXT_PUBLIC_API_SERVER: "${BASE_URL:-http://localhost}" + NUXT_PUBLIC_UPLOAD_CLIENT: "${BASE_URL:-http://localhost}/api/upload/files" depends_on: dbrepo-search-service: condition: service_started @@ -397,8 +401,8 @@ services: - "-s3-endpoint=${STORAGE_ENDPOINT:-http://storage-service:9000}" - "-s3-bucket=dbrepo" environment: - AWS_ACCESS_KEY_ID: "${STORAGE_USERNAME:-seaweedfsadmin}" - AWS_SECRET_ACCESS_KEY: "${STORAGE_PASSWORD:-seaweedfsadmin}" + AWS_ACCESS_KEY_ID: "${S3_ACCESS_KEY_ID:-seaweedfsadmin}" + AWS_SECRET_ACCESS_KEY: "${S3_SECRET_ACCESS_KEY:-seaweedfsadmin}" AWS_REGION: "${STORAGE_REGION_NAME:-default}" depends_on: dbrepo-storage-service: diff --git a/.docs/api/analyse-service.md b/.docs/api/analyse-service.md index 3f5921968c7d1ebfd7cc4563c4b3fc4dccf7b23f..3529f0f3df3482ecefcadd17951c641c60c3422a 100644 --- a/.docs/api/analyse-service.md +++ b/.docs/api/analyse-service.md @@ -31,8 +31,11 @@ the [Storage Service](../storage-service), analysis for data types and primary k with [`csv.Sniff().sniff(...)`](https://docs.python.org/3/library/csv.html#csv.Sniffer). This step is optional when the separator was provided via HTTP-payload: `{"separator": ";", ...}` 3. With the separator known (either from step 2 or via HTTP-payload), the [`Pandas`](https://pypi.org/project/pandas/) - guesses the headers and column types and enums, if the HTTP-payload contains `{"enum": true, ...}`. The data type - is guessed by a combination of Pandas and heuristics. + guesses the headers and column types and enums by analysing the first 10.000 rows, if the HTTP-payload contains + `{"enum": true, ...}`. The data type is guessed by a combination of Pandas and heuristics. + +If your datasets are larger than 10.000 rows, increase the number of lines analysed by setting the `ANALYSE_NROWS` +variable to the desired integer. ### Examples diff --git a/.docs/api/gateway-service.md b/.docs/api/gateway-service.md index 923b95a9f30ac9af06bb029682bd67bc7f2f0961..26ad76f092c599fbb865648577eba7b1a283ba72 100644 --- a/.docs/api/gateway-service.md +++ b/.docs/api/gateway-service.md @@ -39,6 +39,12 @@ services: If your TLS private key as a password, you need to specify it in the `dbrepo.conf` file. +### Connection Timeouts + +The reverse proxy has a defined timeout of 90 seconds on all requests (these are also enforced in the +[User Interface](../ui) using the [`axios`](https://www.npmjs.com/package/axios) module). For large databases these +timeouts may need to be increased, e.g. the timeout for creating subsets is by default already increased to 600 seconds. + ### User Interface To serve the [User Interface](../ui/) under different port than `80`, change the port mapping in diff --git a/.docs/api/metadata-service.md b/.docs/api/metadata-service.md index 039f2fa097ac3ba89d56ff6d735fc453bc3bc505..9d9c39ee25e62e14f610f5d84a1867af9a94e07f 100644 --- a/.docs/api/metadata-service.md +++ b/.docs/api/metadata-service.md @@ -23,9 +23,21 @@ types), semantic concepts (i.e. ontologies) and relational metadata (databases, ## Generation -Most of the metadata available in DBRepo is generated automatically, leveraging the available information and taking -the burden away from researchers, data stewards, etc. For example, the schema (names, constraints, data length) of -generated tables and views is obtained from the `information_schema` database maintained by MariaDB internally. +DBRepo generates metadata for managed tables automatically by querying MariaDB's internal structures +(e.g. `information_schema`). + +!!! info "Managed Tables" + + DBRepo only manages system-versioned tables, other tables are not supported. These other tables are ignored by + DBRepo and thus can co-exist in the same database. If you want a non-system-versioned table `my_table` to be managed + by DBRepo, make it system-versioned: + + ```sql + ALTER TABLE `my_table` ADD SYSTEM VERSIONING; + ``` + + Then, refresh the managed table index by navigating to your database > Settings > Schema > Refresh. This action can + only be performed by the database owner. ## Identifiers diff --git a/.docs/examples/health.md b/.docs/examples/health.md new file mode 100644 index 0000000000000000000000000000000000000000..ebd2673c4423f604e0aa74d5a552e09212d00d4b --- /dev/null +++ b/.docs/examples/health.md @@ -0,0 +1,25 @@ +--- +author: Martin Weise +--- + +## tl;dr + +[:fontawesome-solid-database: Dataset](https://dbrepo1.ec.tuwien.ac.at/database/32/info){ .md-button .md-button--primary target="_blank" } + +## Description + +This dataset contains anonymous behavioural and ocular recordings from healthy participants during performance of a +decision-making task between action movements in which we studied the influence of social facilitation and social +pressure. The original dataset is available on [Kaggle](https://doi.org/10.34740/kaggle/ds/4292829). + +## Solution + +Importing the original dataset from Kaggle into DBRepo allows for exploratory analysis and increased value for secondary +use, e.g. with our [`pandas`](https://pypi.org/project/pandas/)-compatible [Python Library](../../api/python). + +## DBRepo Features + +- [x] System versioning +- [x] Subset exploration +- [x] Large dataset (3 tables, around 6GB) +- [x] External data access for analysis diff --git a/.docs/examples/theater.md b/.docs/examples/theater.md new file mode 100644 index 0000000000000000000000000000000000000000..2ea453cfc9077f07490eada2c3d7c38f401053cf --- /dev/null +++ b/.docs/examples/theater.md @@ -0,0 +1,27 @@ +--- +author: Martin Weise +--- + +## tl;dr + +[:fontawesome-solid-database: Dataset](https://dbrepo1.ec.tuwien.ac.at/pid/52){ .md-button .md-button--primary target="_blank" } + +## Description + +This section will be expanded soon. + +## Solution + +We host a mirror from the [public data](https://www.stadttheater.uni-hamburg.de/) provided by UHH. + +## DBRepo Features + +- [x] System versioning +- [x] Subset exploration +- [x] External data access for analysis + +## Acknowledgement + +This work was part of a cooperation with the [University of Hamburg](https://www.uni-hamburg.de/en.html). + +<img src="../../images/logos/uhh.png" width=100 /> \ No newline at end of file diff --git a/.docs/images/logos/uhh.png b/.docs/images/logos/uhh.png new file mode 100644 index 0000000000000000000000000000000000000000..dc3553771316f67d6912835b3b3d9763e35b114d Binary files /dev/null and b/.docs/images/logos/uhh.png differ diff --git a/.docs/installation.md b/.docs/installation.md index 0404133900b41c28679e7a9562b8989783022742..9c864ead4686a1b4c417147ad564740aeb5b8c8e 100644 --- a/.docs/installation.md +++ b/.docs/installation.md @@ -47,10 +47,6 @@ curl -sSL https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-service Call the helper script to regenerate the client secret of the `dbrepo-client` and set it as value of the `AUTH_SERVICE_CLIENT_SECRET` variable in the `.env` file. -```bash -curl -sSL "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-1.4.5/.scripts/reg-client-secret.sh" | bash -``` - Update the rest of the default secrets in the `.env` file to secure passwords. You can use `openssl` for that, e.g. `openssl rand -hex 16`. Set `auth_ldap.dn_lookup_bind.password` in `dist/rabbitmq.conf` to the value of `SYSTEM_PASSWORD`. @@ -69,7 +65,7 @@ Log into the Auth Service with the default credentials `admin` and the value of :material-numeric-3-circle-outline:. <figure markdown> -{ .img-border } +{ .img-border } <figcaption>Figure 1: Select the Identity Service provider.</figcaption> </figure> @@ -78,9 +74,15 @@ but this is optional. Change the Bind credentials to the desired password :mater the variable `IDENTITY_SERVICE_ADMIN_PASSWORD` in `.env`. <figure markdown> -{ .img-border } +{ .img-border } <figcaption>Figure 2: Update the Identity Service admin user credentials.</figcaption> </figure> + +Update the client secret of the `dbrepo-client`: + +```bash +curl -sSL "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-1.4.5/.scripts/reg-client-secret.sh" | bash +``` Also, update the JWT key according to the [Keycloak documentation](https://www.keycloak.org/docs/24.0.1/server_admin/index.html#rotating-keys). To secure your diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8322e26eb1201fe2c4429c6eca6cee85d74654b1..0bf1bc925538e7a74c85f2b6f12e290eaf8d10a0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -387,6 +387,7 @@ release-helm: - "helm package ./helm/dbrepo --sign --key 'Martin Weise' --keyring ~/.gnupg/secring.gpg --destination ./build" - "helm plugin install https://github.com/sigstore/helm-sigstore" script: + - "helm push ./build/dbrepo-${CHART_VERSION}.tgz oci://${CI_REGISTRY2_URL}/helm" - "helm sigstore upload ./build/dbrepo-${CHART_VERSION}.tgz" release-docs: diff --git a/dbrepo-analyse-service/app.py b/dbrepo-analyse-service/app.py index 2dc9161746fbc52fb523b2bcfe934a2dac8ffbb4..def401c0e28e5c16c264d5fd280e5435fc44c45e 100644 --- a/dbrepo-analyse-service/app.py +++ b/dbrepo-analyse-service/app.py @@ -225,6 +225,7 @@ app.config["S3_ACCESS_KEY_ID"] = os.getenv('S3_ACCESS_KEY_ID', 'seaweedfsadmin') app.config["S3_BUCKET"] = os.getenv('S3_BUCKET', 'dbrepo') app.config["S3_ENDPOINT"] = os.getenv('S3_ENDPOINT', 'http://localhost:9000') app.config["S3_SECRET_ACCESS_KEY"] = os.getenv('S3_SECRET_ACCESS_KEY', 'seaweedfsadmin') +app.config["ANALYSE_NROWS"] = int(os.getenv('ANALYSE_NROWS', '10000')) app.json_encoder = LazyJSONEncoder diff --git a/dbrepo-analyse-service/determine_dt.py b/dbrepo-analyse-service/determine_dt.py index a0890c2b7a9cd5a9e53649464cfa19ec47f0e45d..462f8b652b1e5c40c3a7cf0717c554c864052059 100644 --- a/dbrepo-analyse-service/determine_dt.py +++ b/dbrepo-analyse-service/determine_dt.py @@ -10,7 +10,7 @@ import pandas from numpy import dtype, max, min from flask import current_app from pandas import DataFrame -from pandas.errors import EmptyDataError +from pandas.errors import EmptyDataError, ParserError from api.dto import ColumnAnalysisDto, DataTypeDto, AnalysisDto from clients.s3_client import S3Client @@ -45,12 +45,14 @@ def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=',') -> for encoding in ['utf-8', 'cp1252', 'latin1', 'iso-8859-1']: try: logging.debug(f"attempt parsing .csv using encoding {encoding}") - df = pandas.read_csv(fh, delimiter=separator, nrows=100, lineterminator=line_terminator, - index_col=False, encoding=encoding) + df = pandas.read_csv(fh, delimiter=separator, nrows=current_app.config['ANALYSE_NROWS'], + lineterminator=line_terminator, index_col=False, encoding=encoding) logging.debug(f"parsing .csv using encoding {encoding} was successful") break + except ParserError as error: + raise IOError(f"Failed to parse .csv using separator {separator}: {error}") except (UnicodeDecodeError, EmptyDataError) as error: - logging.warning(f"Failed to parse .csv using encoding {encoding}: {error}") + raise IOError(f"Failed to parse .csv using encoding {encoding}: {error}") if df is None: raise IOError( f"Failed to parse .csv: no supported encoding found (one of: utf-8, cp1252, latin1, iso-8859-1)") 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 8f5f4a821484d23a51dd5c60a6bba658278fdb14..a7c74946923a36f639ae692e6d153881bcc2401f 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 @@ -86,19 +86,11 @@ public class SubsetEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<List<QueryDto>> list(@NotNull @PathVariable("databaseId") Long databaseId, - @RequestParam(name = "persisted", required = false) Boolean filterPersisted, - Principal principal) + @RequestParam(name = "persisted", required = false) Boolean filterPersisted) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, QueryNotFoundException, NotAllowedException, MetadataServiceException { log.debug("endpoint find subsets in database, databaseId={}, filterPersisted={}", databaseId, filterPersisted); final PrivilegedDatabaseDto database = metadataServiceGateway.getDatabaseById(databaseId); - if (!database.getIsPublic()) { - if (principal == null) { - log.error("Failed to find subsets in database: no access"); - throw new NotAllowedException("Failed to find subsets in database: no access"); - } - metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); - } final List<QueryDto> queries; try { queries = subsetService.findAll(database, filterPersisted); @@ -151,8 +143,7 @@ public class SubsetEndpoint { public ResponseEntity<?> findById(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("subsetId") Long subsetId, @NotNull HttpServletRequest httpServletRequest, - @RequestParam(required = false) Instant timestamp, - Principal principal) + @RequestParam(required = false) Instant timestamp) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, QueryNotFoundException, FormatNotAvailableException, StorageUnavailableException, QueryMalformedException, SidecarExportException, StorageNotFoundException, NotAllowedException, UserNotFoundException, @@ -161,13 +152,6 @@ public class SubsetEndpoint { log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId, subsetId, accept, timestamp); final PrivilegedDatabaseDto database = metadataServiceGateway.getDatabaseById(databaseId); - if (!database.getIsPublic()) { - if (principal == null) { - log.error("Failed to find subsets in database: no access"); - throw new NotAllowedException("Failed to find subsets in database: no access"); - } - metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); - } final QueryDto query; try { query = subsetService.findById(database, subsetId); @@ -262,7 +246,7 @@ public class SubsetEndpoint { 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, + "page={}, size={}, timestamp={}", databaseId, data.getStatement(), principal.getName(), page, size, timestamp); /* check */ endpointValidator.validateDataParams(page, size); @@ -360,21 +344,21 @@ public class SubsetEndpoint { } try { final QueryDto query = subsetService.findById(database, subsetId); - final Long count = subsetService.reExecuteCount(database, query); final HttpHeaders headers = new HttpHeaders(); - headers.set("X-Count", "" + count); headers.set("Access-Control-Expose-Headers", "X-Count"); - if (request.getMethod().equals("GET")) { - final QueryResultDto result = subsetService.reExecute(database, query, page, size, null, null); - result.setId(subsetId); - log.trace("re-execute query resulted in result {}", result); + if (request.getMethod().equals("HEAD")) { + final Long count = subsetService.reExecuteCount(database, query); + headers.set("X-Count", "" + count); return ResponseEntity.ok() .headers(headers) - .body(result); + .build(); } + final QueryResultDto result = subsetService.reExecute(database, query, page, size, null, null); + result.setId(subsetId); + log.trace("re-execute query resulted in result {}", result); return ResponseEntity.ok() .headers(headers) - .build(); + .body(result); } 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); 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 f4d31bbc216b333656d1ac70d0e3f28ddd49be0f..e26a37b43aaa1b5307cf87d294e1dedc5cef87c4 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 @@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -32,6 +33,7 @@ import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -189,6 +191,7 @@ public class TableEndpoint { @RequestParam(required = false) Instant timestamp, @RequestParam(required = false) Long page, @RequestParam(required = false) Long size, + @NotNull HttpServletRequest request, Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, TableMalformedException, PaginationException, QueryMalformedException, MetadataServiceException, @@ -221,7 +224,12 @@ public class TableEndpoint { final HttpHeaders headers = new HttpHeaders(); headers.set("Access-Control-Expose-Headers", "X-Count"); try { - headers.set("X-Count", "" + tableService.getCount(table, timestamp)); + if (request.getMethod().equals("HEAD")) { + headers.set("X-Count", "" + tableService.getCount(table, timestamp)); + return ResponseEntity.ok() + .headers(headers) + .build(); + } final QueryResultDto dto = tableService.getData(table, timestamp, page, size); return ResponseEntity.ok() .headers(headers) 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 e9dbfd5c3a7f3d59088f048398cd2c4f9d9d8f45..e4ddab603685103027dcb6dfad936b882c987294 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 @@ -92,8 +92,7 @@ public class ViewEndpoint { }) public ResponseEntity<List<ViewDto>> getSchema(@NotBlank @PathVariable("databaseId") Long databaseId) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, - ViewMalformedException, ViewNotFoundException, DatabaseMalformedException, ViewSchemaException, - MetadataServiceException { + ViewNotFoundException, DatabaseMalformedException, MetadataServiceException { log.debug("endpoint inspect view schemas, databaseId={}", databaseId); final PrivilegedDatabaseDto database = metadataServiceGateway.getDatabaseById(databaseId); try { @@ -265,20 +264,19 @@ public class ViewEndpoint { metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); } try { - final Long count = viewService.count(view, timestamp); final HttpHeaders headers = new HttpHeaders(); - headers.set("X-Count", "" + count); headers.set("Access-Control-Expose-Headers", "X-Count"); - if (request.getMethod().equals("GET")) { - final QueryResultDto result = viewService.data(view, timestamp, page, size); - log.trace("get view data resulted in result {}", result); + if (request.getMethod().equals("HEAD")) { + headers.set("X-Count", "" + viewService.count(view, timestamp)); return ResponseEntity.ok() .headers(headers) - .body(result); + .build(); } + final QueryResultDto result = viewService.data(view, timestamp, page, size); + log.trace("get view data resulted in result {}", result); return ResponseEntity.ok() .headers(headers) - .build(); + .body(result); } 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); 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 d554667467009b396caee8f2800b522f21f8ace6..6a0015b6f61e567fbf65f2e8f97f6c568fe142fa 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 @@ -29,7 +29,6 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.InputStream; -import java.security.Principal; import java.sql.SQLException; import java.time.Instant; import java.util.List; @@ -69,7 +68,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { DatabaseNotFoundException, RemoteUnavailableException, SQLException, MetadataServiceException { /* test */ - final List<QueryDto> response = generic_findAllById(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, null); + final List<QueryDto> response = generic_findAllById(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO); assertEquals(6, response.size()); } @@ -79,17 +78,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_findAllById(null, null, null); - }); - } - - @Test - @WithAnonymousUser - public void findAllById_privateNoAccess_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_findAllById(DATABASE_1_ID, DATABASE_1_PRIVILEGED_DTO, null); + generic_findAllById(null, null); }); } @@ -105,7 +94,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_PRIVILEGED_DTO); /* test */ - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.APPLICATION_JSON, null, null); + generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.APPLICATION_JSON, null); } @Test @@ -126,7 +115,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(mock); /* test */ - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.parseMediaType("text/csv"), null, null); + generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.parseMediaType("text/csv"), null); } @Test @@ -147,7 +136,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(mock); /* test */ - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.parseMediaType("text/csv"), Instant.now(), null); + generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.parseMediaType("text/csv"), Instant.now()); } @Test @@ -161,7 +150,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.APPLICATION_JSON, null, null); + generic_findById(QUERY_5_ID, QUERY_5_DTO, MediaType.APPLICATION_JSON, null); }); } @@ -272,9 +261,6 @@ 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("X-Count")); - assertEquals(1, response.getHeaders().get("X-Count").size()); - assertEquals(QUERY_5_RESULT_NUMBER, 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)); @@ -325,9 +311,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ final ResponseEntity<QueryResultDto> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null); assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getHeaders().get("X-Count")); - assertEquals(1, response.getHeaders().get("X-Count").size()); - assertEquals(QUERY_1_RESULT_NUMBER, Long.parseLong(response.getHeaders().get("X-Count").get(0))); assertNotNull(response.getBody()); } @@ -464,7 +447,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { }); } - protected List<QueryDto> generic_findAllById(Long databaseId, PrivilegedDatabaseDto database, Principal principal) + protected List<QueryDto> generic_findAllById(Long databaseId, PrivilegedDatabaseDto database) throws DatabaseUnavailableException, NotAllowedException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, SQLException, MetadataServiceException { @@ -481,16 +464,16 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { } /* test */ - final ResponseEntity<List<QueryDto>> response = subsetEndpoint.list(databaseId, null, principal); + final ResponseEntity<List<QueryDto>> response = subsetEndpoint.list(databaseId, null); assertEquals(HttpStatus.OK, response.getStatusCode()); return response.getBody(); } - protected void generic_findById(Long subsetId, QueryDto subset, MediaType accept, Instant timestamp, - Principal principal) throws UserNotFoundException, DatabaseUnavailableException, - StorageUnavailableException, NotAllowedException, QueryMalformedException, QueryNotFoundException, - DatabaseNotFoundException, SidecarExportException, RemoteUnavailableException, FormatNotAvailableException, - StorageNotFoundException, SQLException, MetadataServiceException { + 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 { /* mock */ when(queryService.findById(DATABASE_3_PRIVILEGED_DTO, subsetId)) @@ -499,7 +482,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(accept.toString()); /* test */ - final ResponseEntity<?> response = subsetEndpoint.findById(DATABASE_3_ID, subsetId, mockHttpServletRequest, timestamp, principal); + final ResponseEntity<?> response = subsetEndpoint.findById(DATABASE_3_ID, subsetId, mockHttpServletRequest, timestamp); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } 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 29a33d1d9cbb3dd95a5640610d7620a36d1800db..76c34ef47cd2d13f698f3d552b9aebbbb4175492 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 @@ -9,6 +9,7 @@ import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.TableService; import at.tuwien.test.AbstractUnitTest; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,6 +41,9 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Autowired private TableEndpoint tableEndpoint; + @Autowired + private HttpServletRequest httpServletRequest; + @MockBean private TableService tableService; @@ -152,11 +156,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_8_DATA_DTO); /* test */ - final ResponseEntity<QueryResultDto> response = tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, null); + final ResponseEntity<QueryResultDto> response = tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, httpServletRequest, null); assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getHeaders().get("X-Count")); - assertEquals(1, response.getHeaders().get("X-Count").size()); - assertEquals(QUERY_5_RESULT_NUMBER, 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)); @@ -175,7 +176,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, null); + tableEndpoint.getData(DATABASE_3_ID, TABLE_8_ID, null, null, null, httpServletRequest, null); }); } 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 b9b814378e9bd537d42c421c3e3fab81d381443f..543b4a5cf299d48a8a4594f95e8586752d4278cc 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 @@ -161,17 +161,12 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); - when(viewService.count(eq(VIEW_1_PRIVILEGED_DTO), any(Instant.class))) - .thenReturn(VIEW_1_DATA_COUNT); when(viewService.data(eq(VIEW_1_PRIVILEGED_DTO), any(Instant.class), eq(0L), eq(10L))) .thenReturn(VIEW_1_DATA_DTO); /* 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("X-Count")); - assertEquals(1, response.getHeaders().get("X-Count").size()); - assertEquals(VIEW_1_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)); 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 9d42c1a54df551749416b0b1fcc5c6c56abed3f8..5898372633b0d63c5b256f6b567f8a8869583ab7 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 @@ -126,12 +126,12 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { } @Test - @WithMockUser(username = USER_1_USERNAME, authorities = {"dbrepo_subset_list", "execute-query", "persist-query"}) + @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query", "persist-query"}) public void prometheusSubsetEndpoint_succeeds() { /* mock */ try { - subsetEndpoint.list(DATABASE_1_ID, null, USER_1_PRINCIPAL); + subsetEndpoint.list(DATABASE_1_ID, null); } catch (Exception e) { /* ignore */ } @@ -151,7 +151,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, new MockHttpServletRequest(), null, USER_1_PRINCIPAL); + subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, new MockHttpServletRequest(), null); } catch (Exception e) { /* ignore */ } @@ -171,7 +171,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* mock */ try { - tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, null); + tableEndpoint.getData(DATABASE_1_ID, TABLE_1_ID, null, null, null, httpServletRequest, null); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java index be1f6b5dae37db17ec5e90ad001a54ff77869886..540e17850a535b8776daf4a6a7bf53a5499f1838 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java @@ -332,12 +332,39 @@ public class SchemaServiceIntegrationTest extends AbstractUnitTest { assertEquals("other_id", pk1.getColumn().getName()); assertEquals("other_id", pk1.getColumn().getInternalName()); assertEquals(ColumnTypeDto.BIGINT, pk1.getColumn().getColumnType()); + } + + @Test + public void inspectTable_exoticBoolean_succeeds() throws TableNotFoundException, SQLException { + /* test */ + final TableDto response = schemaService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "exotic_boolean"); + final ConstraintsDto constraints = response.getConstraints(); + final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); + assertEquals(1, primaryKey.size()); + final PrimaryKeyDto pk0 = primaryKey.get(0); + assertNull(pk0.getId()); + assertNotNull(pk0.getTable()); + assertNull(pk0.getTable().getId()); + assertEquals("exotic_boolean", pk0.getTable().getName()); + assertEquals("exotic_boolean", pk0.getTable().getInternalName()); + assertNotNull(pk0.getColumn()); + assertNull(pk0.getColumn().getId()); + assertNull(pk0.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, pk0.getColumn().getDatabaseId()); + assertNull(pk0.getColumn().getAlias()); + assertEquals("bool_default", pk0.getColumn().getName()); + assertEquals("bool_default", pk0.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.BOOL, pk0.getColumn().getColumnType()); + final List<ColumnDto> columns = response.getColumns(); + assertEquals(3, columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "bool_default", "bool_default", ColumnTypeDto.BOOL, null, 0L, false, null, null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "bool_tinyint", "bool_tinyint", ColumnTypeDto.BOOL, null, 0L, false, null, null); + assertColumn(columns.get(2), null, null, DATABASE_1_ID, "bool_tinyint_unsigned", "bool_tinyint_unsigned", ColumnTypeDto.BOOL, null, 0L, false, null, null); } @Test - public void inspectView_succeeds() throws ViewMalformedException, SQLException, ViewNotFoundException, - ViewSchemaException { + public void inspectView_succeeds() throws SQLException, ViewNotFoundException { /* test */ final ViewDto response = schemaService.inspectView(DATABASE_1_PRIVILEGED_DTO, "not_in_metadata_db2"); 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 41fa4963fb26158f11060db383c4cb973e66e1c0..b8267e959e32f00545ec6670af98b420efb17c13 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 @@ -334,7 +334,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* test */ final List<TableDto> response = tableService.getSchemas(DATABASE_1_PRIVILEGED_DTO); - assertEquals(3, response.size()); + assertEquals(4, response.size()); final TableDto table0 = response.get(0); Assertions.assertEquals("complex_foreign_keys", table0.getInternalName()); Assertions.assertEquals("complex_foreign_keys", table0.getName()); @@ -411,32 +411,53 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { assertEquals(0, constraints1.getUniques().size()); /* table 2 */ final TableDto table2 = response.get(2); - Assertions.assertEquals("not_in_metadata_db", table2.getInternalName()); - Assertions.assertEquals("not_in_metadata_db", table2.getName()); + Assertions.assertEquals("exotic_boolean", table2.getInternalName()); + Assertions.assertEquals("exotic_boolean", table2.getName()); Assertions.assertEquals(DATABASE_1_ID, table2.getTdbid()); assertTrue(table2.getIsVersioned()); Assertions.assertEquals(DATABASE_1_PUBLIC, table2.getIsPublic()); final List<ColumnDto> columns2 = table2.getColumns(); assertNotNull(columns2); - Assertions.assertEquals(5, columns2.size()); - assertColumn(columns2.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null, null); - assertColumn(columns2.get(1), null, null, DATABASE_1_ID, "given_name", "given_name", ColumnTypeDto.VARCHAR, 255L, null, false, null, null); - assertColumn(columns2.get(2), null, null, DATABASE_1_ID, "middle_name", "middle_name", ColumnTypeDto.VARCHAR, 255L, null, true, null, null); - assertColumn(columns2.get(3), null, null, DATABASE_1_ID, "family_name", "family_name", ColumnTypeDto.VARCHAR, 255L, null, false, null, null); - assertColumn(columns2.get(4), null, null, DATABASE_1_ID, "age", "age", ColumnTypeDto.INT, 10L, 0L, false, null, null); + Assertions.assertEquals(3, columns2.size()); + assertColumn(columns2.get(0), null, null, DATABASE_1_ID, "bool_default", "bool_default", ColumnTypeDto.BOOL, null, 0L, false, null, null); + assertColumn(columns2.get(1), null, null, DATABASE_1_ID, "bool_tinyint", "bool_tinyint", ColumnTypeDto.BOOL, null, 0L, false, null, null); + assertColumn(columns2.get(2), null, null, DATABASE_1_ID, "bool_tinyint_unsigned", "bool_tinyint_unsigned", ColumnTypeDto.BOOL, null, 0L, false, null, null); final ConstraintsDto constraints2 = table2.getConstraints(); assertNotNull(constraints2); final Set<PrimaryKeyDto> primaryKey2 = constraints2.getPrimaryKey(); Assertions.assertEquals(1, primaryKey2.size()); final Set<String> checks2 = constraints2.getChecks(); - Assertions.assertEquals(1, checks2.size()); - Assertions.assertEquals(Set.of("`age` > 0 and `age` < 120"), checks2); + Assertions.assertEquals(0, checks2.size()); final List<UniqueDto> uniques2 = constraints2.getUniques(); - Assertions.assertEquals(1, uniques2.size()); - Assertions.assertEquals(2, uniques2.get(0).getColumns().size()); - Assertions.assertEquals("not_in_metadata_db", uniques2.get(0).getTable().getInternalName()); - Assertions.assertEquals("given_name", uniques2.get(0).getColumns().get(0).getInternalName()); - Assertions.assertEquals("family_name", uniques2.get(0).getColumns().get(1).getInternalName()); + Assertions.assertEquals(0, uniques2.size()); + /* table 3 */ + final TableDto table3 = response.get(3); + Assertions.assertEquals("not_in_metadata_db", table3.getInternalName()); + Assertions.assertEquals("not_in_metadata_db", table3.getName()); + Assertions.assertEquals(DATABASE_1_ID, table3.getTdbid()); + assertTrue(table3.getIsVersioned()); + Assertions.assertEquals(DATABASE_1_PUBLIC, table3.getIsPublic()); + final List<ColumnDto> columns3 = table3.getColumns(); + assertNotNull(columns3); + Assertions.assertEquals(5, columns3.size()); + assertColumn(columns3.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null, null); + assertColumn(columns3.get(1), null, null, DATABASE_1_ID, "given_name", "given_name", ColumnTypeDto.VARCHAR, 255L, null, false, null, null); + assertColumn(columns3.get(2), null, null, DATABASE_1_ID, "middle_name", "middle_name", ColumnTypeDto.VARCHAR, 255L, null, true, null, null); + assertColumn(columns3.get(3), null, null, DATABASE_1_ID, "family_name", "family_name", ColumnTypeDto.VARCHAR, 255L, null, false, null, null); + assertColumn(columns3.get(4), null, null, DATABASE_1_ID, "age", "age", ColumnTypeDto.INT, 10L, 0L, false, null, null); + final ConstraintsDto constraints3 = table3.getConstraints(); + assertNotNull(constraints3); + final Set<PrimaryKeyDto> primaryKey3 = constraints3.getPrimaryKey(); + Assertions.assertEquals(1, primaryKey3.size()); + final Set<String> checks3 = constraints3.getChecks(); + Assertions.assertEquals(1, checks3.size()); + Assertions.assertEquals(Set.of("`age` > 0 and `age` < 120"), checks3); + final List<UniqueDto> uniques3 = constraints3.getUniques(); + Assertions.assertEquals(1, uniques3.size()); + Assertions.assertEquals(2, uniques3.get(0).getColumns().size()); + Assertions.assertEquals("not_in_metadata_db", uniques3.get(0).getTable().getInternalName()); + Assertions.assertEquals("given_name", uniques3.get(0).getColumns().get(0).getInternalName()); + Assertions.assertEquals("family_name", uniques3.get(0).getColumns().get(1).getInternalName()); } @Test diff --git a/dbrepo-data-service/rest-service/src/test/resources/init/weather.sql b/dbrepo-data-service/rest-service/src/test/resources/init/weather.sql index 79755c97bf68fd822869277e3ad867c582f4e68c..7c3ca99ce39f61755752d2ecbb607ad6ed86e386 100644 --- a/dbrepo-data-service/rest-service/src/test/resources/init/weather.sql +++ b/dbrepo-data-service/rest-service/src/test/resources/init/weather.sql @@ -32,7 +32,7 @@ CREATE TABLE complex_foreign_keys ( id BIGINT NOT NULL PRIMARY KEY, weather_id BIGINT NOT NULL, - other_id BIGINT NOT NULL, + other_id BIGINT NOT NULL, FOREIGN KEY (weather_id, other_id) REFERENCES complex_primary_key (id, `other_id`) ) WITH SYSTEM VERSIONING; @@ -42,6 +42,13 @@ CREATE TABLE sensor `value` DECIMAL ) WITH SYSTEM VERSIONING; +CREATE TABLE exotic_boolean +( + `bool_default` BOOLEAN NOT NULL PRIMARY KEY, + `bool_tinyint` TINYINT(1) NOT NULL, + `bool_tinyint_unsigned` TINYINT(1) UNSIGNED NOT NULL +) WITH SYSTEM VERSIONING; + INSERT INTO weather_location (location, lat, lng) VALUES ('Albury', -36.0653583, 146.9112214), ('Sydney', -33.847927, 150.6517942), diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java index 6e233395d92ae03785ba820055cb7d421cfc7b38..1d73f6219a77e05f33166e23f75e16cafebc3e6f 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java @@ -58,20 +58,20 @@ public class KeycloakGatewayImpl implements KeycloakGateway { .exchange(url, HttpMethod.POST, new HttpEntity<>(payload, headers), TokenDto.class); } catch (HttpServerErrorException e) { log.error("Failed to obtain user token: {}", e.getMessage()); - throw new AuthServiceConnectionException("Service unavailable", e); + throw new AuthServiceConnectionException("Failed to obtain user token: " + e.getMessage(), e); } catch (HttpClientErrorException.BadRequest e) { if (e.getResponseBodyAsByteArray() != null && e.getResponseBodyAsByteArray().length > 0) { final KeycloakErrorDto error = e.getResponseBodyAs(KeycloakErrorDto.class); if (error != null && error.getError().equals("invalid_grant")) { log.error("Failed to obtain user token: {}", error.getErrorDescription()); - throw new AccountNotSetupException(error.getErrorDescription()); + throw new AccountNotSetupException("Failed to obtain user token: " + error.getErrorDescription(), e); } } log.error("Failed to obtain user token: bad request"); throw new CredentialsInvalidException("Bad request", e); } catch (HttpClientErrorException.Unauthorized e) { log.error("Failed to obtain user token: invalid credentials"); - throw new CredentialsInvalidException("Invalid credentials", e); + throw new CredentialsInvalidException("Invalid credentials: " + e.getMessage(), e); } return response.getBody(); } 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 06641b738b91c4798e9a57b26eb011fbc6c3c4cd..b4cb2ff5043c46e1e5ece49a45690ef04782bd3c 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 @@ -7,7 +7,6 @@ import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.internal.PrivilegedDatabaseDto; import at.tuwien.api.database.internal.PrivilegedViewDto; import at.tuwien.api.database.table.TableDto; -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; @@ -56,7 +55,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new RemoteUnavailableException("Failed to find container: " + e.getMessage(), e); } catch (HttpClientErrorException.NotFound e) { log.error("Failed to find container with id {}: {}", containerId, e.getMessage()); - throw new ContainerNotFoundException("Failed to find container: " + e.getMessage()); + throw new ContainerNotFoundException("Failed to find container: " + e.getMessage(), e); } if (response.getStatusCode() != HttpStatus.OK) { log.error("Failed to find container with id {}: service responded unsuccessful: {}", containerId, response.getStatusCode()); @@ -88,7 +87,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new RemoteUnavailableException("Failed to find database: " + e.getMessage(), e); } catch (HttpClientErrorException.NotFound e) { log.error("Failed to find database with id {}: body is null", id); - throw new DatabaseNotFoundException("Failed to find database: body is null", e); + throw new DatabaseNotFoundException("Failed to find database: body is null: " + e.getMessage(), e); } if (response.getStatusCode() != HttpStatus.OK) { log.error("Failed to find database with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); @@ -141,7 +140,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new RemoteUnavailableException("Failed to find table: " + e.getMessage(), e); } catch (HttpClientErrorException.NotFound e) { log.error("Failed to find table with id {}: not found: {}", id, e.getMessage()); - throw new TableNotFoundException("Failed to find table: " + e.getMessage()); + throw new TableNotFoundException("Failed to find table: " + e.getMessage(), e); } if (!response.getStatusCode().equals(HttpStatus.OK)) { log.error("Failed to find table with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); @@ -179,7 +178,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new RemoteUnavailableException("Failed to find view: " + e.getMessage(), e); } catch (HttpClientErrorException.NotFound e) { log.error("Failed to find view with id {}: not found: {}", id, e.getMessage()); - throw new ViewNotFoundException("Failed to find view: " + e.getMessage()); + throw new ViewNotFoundException("Failed to find view: " + e.getMessage(), e); } if (!response.getStatusCode().equals(HttpStatus.OK)) { log.error("Failed to find view with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); @@ -214,7 +213,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new RemoteUnavailableException("Failed to find user: " + e.getMessage(), e); } catch (HttpClientErrorException.NotFound e) { log.error("Failed to find user with id {}: not found: {}", userId, e.getMessage()); - throw new UserNotFoundException("Failed to find user: " + e.getMessage()); + throw new UserNotFoundException("Failed to find user: " + e.getMessage(), e); } if (!response.getStatusCode().equals(HttpStatus.OK)) { log.error("Failed to find user with id {}: service responded unsuccessful: {}", userId, response.getStatusCode()); @@ -238,7 +237,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new RemoteUnavailableException("Failed to find user: " + e.getMessage(), e); } catch (HttpClientErrorException.NotFound e) { log.error("Failed to find user with id {}: not found: {}", userId, e.getMessage()); - throw new UserNotFoundException("Failed to find user: " + e.getMessage()); + throw new UserNotFoundException("Failed to find user: " + e.getMessage(), e); } if (!response.getStatusCode().equals(HttpStatus.OK)) { log.error("Failed to find user with id {}: service responded unsuccessful: {}", userId, response.getStatusCode()); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java index 796ef3edebc452471569dd6f04c81bb4731891cd..b34053ec88a47a1f02ab99048fffc7e7f0cf35dd 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java @@ -21,6 +21,7 @@ import at.tuwien.api.database.table.constraints.foreign.ForeignKeyReferenceDto; import at.tuwien.api.database.table.constraints.foreign.ReferenceTypeDto; import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; import at.tuwien.api.database.table.constraints.unique.UniqueDto; +import at.tuwien.api.user.UserDto; import at.tuwien.config.QueryConfig; import at.tuwien.exception.QueryNotFoundException; import at.tuwien.exception.TableNotFoundException; @@ -97,7 +98,8 @@ public interface DataMapper { /** * Map the inspected schema to either an existing view/table and append e.g. column or (if not existing) create a new view/table. - * @param database The database. + * + * @param database The database. * @param resultSet The inspected schema. * @return The database containing the updated view/table. * @throws SQLException @@ -152,31 +154,23 @@ public interface DataMapper { .databaseId(table.getTdbid()) .description(resultSet.getString(11)) .build(); + final String dataType = resultSet.getString(8); if (column.getColumnType().equals(ColumnTypeDto.ENUM)) { - column.setEnums(Arrays.stream(resultSet.getString(8) - .substring(0, resultSet.getString(8).length() - 1) + column.setEnums(Arrays.stream(dataType.substring(0, resultSet.getString(8).length() - 1) .replace("enum(", "") .split(",")) .map(value -> value.replace("'", "")) .toList()); } if (column.getColumnType().equals(ColumnTypeDto.SET)) { - column.setSets(Arrays.stream(resultSet.getString(8) - .substring(0, resultSet.getString(8).length() - 1) + column.setSets(Arrays.stream(dataType.substring(0, dataType.length() - 1) .replace("set(", "") .split(",")) .map(value -> value.replace("'", "")) .toList()); } - /* constraints */ - if (resultSet.getString(9) != null && resultSet.getString(9).equals("PRI")) { - table.getConstraints().getPrimaryKey().add(PrimaryKeyDto.builder() - .table(tableDtoToTableBriefDto(table)) - .column(columnDtoToColumnBriefDto(column)) - .build()); - } /* fix boolean and set size for others */ - if (resultSet.getString(8).equalsIgnoreCase("tinyint(1)")) { + if (dataType.startsWith("tinyint(1)")) { column.setColumnType(ColumnTypeDto.BOOL); } else if (resultSet.getString(5) != null) { column.setSize(resultSet.getLong(5)); @@ -196,6 +190,13 @@ public interface DataMapper { .id(queryConfig.getDefaultTimeFormatId()) .build()); } + /* constraints */ + if (resultSet.getString(9) != null && resultSet.getString(9).equals("PRI")) { + table.getConstraints().getPrimaryKey().add(PrimaryKeyDto.builder() + .table(tableDtoToTableBriefDto(table)) + .column(columnDtoToColumnBriefDto(column)) + .build()); + } table.getColumns() .add(column); return table; @@ -241,8 +242,9 @@ public interface DataMapper { /** * Parse columns from a SQL statement of a known database. + * * @param database The database. - * @param query The SQL statement. + * @param query The SQL statement. * @return The list of columns. * @throws JSQLParserException The table/view or column was not found in the database. */ @@ -401,6 +403,9 @@ public interface DataMapper { .created(LocalDateTime.parse(data.getString(2), mariaDbFormatter) .atZone(ZoneId.of("UTC")) .toInstant()) + .creator(UserDto.builder() + .id(UUID.fromString(data.getString(3))) + .build()) .createdBy(UUID.fromString(data.getString(3))) .query(data.getString(4)) .queryHash(data.getString(5)) 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 74e9ffe66d7428d5fdfb7d94909794f034cfd045..a88cdb5078b9a6fe348b082ff089de01a444bc61 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 @@ -73,6 +73,30 @@ public interface MariaDbMapper { return statement.toString(); } + default String databaseRevokePrivilegesQuery(String username) { + final StringBuilder statement = new StringBuilder("REVOKE ALL PRIVILEGES ON *.* FROM `") + .append(username) + .append("`@`%`;"); + log.trace("mapped revoke privileges statement: {}", statement); + return statement.toString(); + } + + default String databaseGrantProcedureQuery(String username, String procedure) { + final StringBuilder statement = new StringBuilder("GRANT EXECUTE ON PROCEDURE `") + .append(procedure) + .append("` TO `") + .append(username) + .append("`@`%`;"); + log.trace("mapped revoke privileges statement: {}", statement); + return statement.toString(); + } + + default String databaseFlushPrivilegesQuery() { + final String statement = "FLUSH PRIVILEGES;"; + log.trace("mapped flush privileges statement: {}", statement); + return statement; + } + @Named("createDatabase") default String databaseCreateDatabaseQuery(String database) { final StringBuilder statement = new StringBuilder("CREATE DATABASE `") @@ -82,6 +106,60 @@ public interface MariaDbMapper { return statement.toString(); } + default String queryStoreCreateSequenceRawQuery() { + final String statement = "CREATE SEQUENCE `qs_queries_seq` NOCACHE;"; + log.trace("mapped create query store sequence statement: {}", statement); + return statement; + } + + default String queryStoreCreateTableRawQuery() { + final String statement = "CREATE TABLE `qs_queries` ( `id` bigint not null primary key default nextval(`qs_queries_seq`), `created` datetime not null default now(), `executed` datetime not null default now(), `created_by` varchar(36) not null, `query` text not null, `query_normalized` text not null, `is_persisted` boolean not null, `query_hash` varchar(255) not null, `result_hash` varchar(255), `result_number` bigint);"; + log.trace("mapped create query store table statement: {}", statement); + return statement; + } + + default String queryStoreCreateHashTableProcedureRawQuery() { + final String statement = "CREATE PROCEDURE hash_table(IN name VARCHAR(255), OUT hash VARCHAR(255), OUT count BIGINT) BEGIN DECLARE _sql TEXT; SELECT CONCAT('SELECT SHA2(GROUP_CONCAT(CONCAT_WS(\\'\\',', GROUP_CONCAT(CONCAT('`', column_name, '`') ORDER BY column_name), ') SEPARATOR \\',\\'), 256) AS hash, COUNT(*) AS count FROM `', name, '` INTO @hash, @count;') FROM `information_schema`.`columns` WHERE `table_schema` = DATABASE() AND `table_name` = name INTO _sql; PREPARE stmt FROM _sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET hash = @hash; SET count = @count; END;"; + log.trace("mapped create query store hash_table procedure statement: {}", statement); + return statement; + } + + default String queryStoreCreateStoreQueryProcedureRawQuery() { + final String statement = "CREATE PROCEDURE store_query(IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _username varchar(255) DEFAULT REGEXP_REPLACE(current_user(), '@.*', ''); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;"; + log.trace("mapped create query store store_query procedure statement: {}", statement); + return statement; + } + + default String queryStoreCreateInternalStoreQueryProcedureRawQuery() { + final String statement = "CREATE DEFINER = 'root' PROCEDURE _store_query(IN _username VARCHAR(255), IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;"; + log.trace("mapped create query store _store_query procedure statement: {}", statement); + return statement; + } + + default String queryStoreStoreQueryRawQuery() { + final String statement = "{call _store_query(?, ?, ?, ?)}"; + log.trace("mapped store query statement: {}", statement); + return statement; + } + + default String queryStoreUpdateQueryRawQuery() { + final String statement = "UPDATE `qs_queries` SET `is_persisted` = ? WHERE `id` = ?"; + log.trace("mapped update query statement: {}", statement); + return statement; + } + + default String queryStoreDeleteStaleQueriesRawQuery() { + final String statement = "DELETE FROM `qs_queries` WHERE `is_persisted` = false AND ABS(DATEDIFF(`created`, NOW())) >= 1"; + log.trace("mapped delete stale queries statement: {}", statement); + return statement; + } + + default String queryStoreFindQueryRawQuery() { + final String statement = "SELECT `id`, `created`, `created_by`, `query`, `query_hash`, `result_hash`, `result_number`, `is_persisted`, `executed` FROM `qs_queries` q WHERE q.`id` = ?"; + log.trace("mapped find query statement: {}", statement); + return statement; + } + default String databaseTablesSelectRawQuery() { final String statement = "SELECT DISTINCT t.`TABLE_NAME` FROM information_schema.TABLES t WHERE t.`TABLE_SCHEMA` = ? AND t.`TABLE_TYPE` = 'SYSTEM VERSIONED' AND t.`TABLE_NAME` != 'qs_queries' ORDER BY t.`TABLE_NAME` ASC"; log.trace("mapped select tables statement: {}", statement); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java index 5acca0018de6bc3e29de49aea9f1da4ae95ff492..f4bef4f067706688dc30234a3a375678214cdb8f 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java @@ -20,12 +20,10 @@ public interface ViewService { * @return The list of view metadata. * @throws SQLException * @throws DatabaseMalformedException - * @throws ViewMalformedException * @throws ViewNotFoundException - * @throws ViewSchemaException */ List<ViewDto> getSchemas(PrivilegedDatabaseDto database) throws SQLException, DatabaseMalformedException, - ViewMalformedException, ViewNotFoundException, ViewSchemaException; + ViewNotFoundException; /** * Creates a view in the given data database. diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java index 8c52e020103db210744884e1f5973f67a8e44861..4fdb8c32a69eac23dc05b1982f1abbbc4893d985 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java @@ -40,17 +40,25 @@ public class AccessServiceMariaDbImpl extends HibernateConnector implements Acce final Connection connection = dataSource.getConnection(); try { /* create user if not exists */ + long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.databaseCreateUserQuery(user.getUsername(), user.getPassword())) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); /* grant access */ final String grants = access != AccessTypeDto.READ ? grantDefaultWrite : grantDefaultRead; + start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.databaseGrantPrivilegesQuery(user.getUsername(), grants)) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); /* grant query store */ - connection.prepareStatement("GRANT EXECUTE ON PROCEDURE `store_query` TO `" + user.getUsername() + "`@`%`;") + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.databaseGrantProcedureQuery(user.getUsername(), "store_query")) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); /* apply access rights */ - connection.prepareStatement("FLUSH PRIVILEGES;"); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.databaseFlushPrivilegesQuery()); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -71,10 +79,12 @@ public class AccessServiceMariaDbImpl extends HibernateConnector implements Acce try { /* grant access */ final String grants = access != AccessTypeDto.READ ? grantDefaultWrite : grantDefaultRead; + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.databaseGrantPrivilegesQuery(user.getUsername(), grants)) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); /* apply access rights */ - connection.prepareStatement("FLUSH PRIVILEGES;"); + connection.prepareStatement(mariaDbMapper.databaseFlushPrivilegesQuery()); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -93,10 +103,15 @@ public class AccessServiceMariaDbImpl extends HibernateConnector implements Acce final Connection connection = dataSource.getConnection(); try { /* revoke access */ - connection.prepareStatement("REVOKE ALL PRIVILEGES ON *.* FROM `" + user.getUsername() + "`@`%`;") + long start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.databaseRevokePrivilegesQuery(user.getUsername())) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); /* apply access rights */ - connection.prepareStatement("FLUSH PRIVILEGES;"); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.databaseFlushPrivilegesQuery()) + .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java index 4ee483ad5e11b4bbc3499e0d98f255e666bec1ea..2d58744a21e9c06fa6fee111ba06240d87e44bd8 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java @@ -43,8 +43,10 @@ public class DatabaseServiceMariaDbImpl extends HibernateConnector implements Da final Connection connection = dataSource.getConnection(); try { /* create database if not exists */ + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.databaseCreateDatabaseQuery(data.getInternalName())) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -77,8 +79,10 @@ public class DatabaseServiceMariaDbImpl extends HibernateConnector implements Da final Connection connection = dataSource.getConnection(); try { /* update user password */ + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.databaseSetPasswordQuery(data.getUsername(), data.getPassword())) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java index 6a583b274affc20d6871e7358e842d77a88f2c4c..22bb599c6090cfe75f152d7f0a2a8f26e03c71b4 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java @@ -12,6 +12,7 @@ import org.springframework.stereotype.Service; public abstract class HibernateConnector { public static ComboPooledDataSource getPrivilegedDataSource(PrivilegedContainerDto container, String databaseName) { + final long start = System.currentTimeMillis(); final ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setJdbcUrl(url(container, databaseName)); dataSource.setUser(container.getUsername()); @@ -21,7 +22,7 @@ public abstract class HibernateConnector { dataSource.setAcquireIncrement(5); dataSource.setMaxPoolSize(20); dataSource.setMaxStatements(100); - log.trace("created pooled data source {} (user={}, password=(hidden))", url(container, databaseName), container.getUsername()); + log.trace("created pooled data source {} in {} ms (user={}, password=(hidden))", url(container, databaseName), System.currentTimeMillis() - start, container.getUsername()); return dataSource; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java index f2675d4e5b537d751743ae70ea7c366e19a05351..0b1dc7caa1cd7f530d6d8590ffb09225e8048ff4 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java @@ -46,7 +46,9 @@ public class QueueServiceRabbitMqImpl extends HibernateConnector implements Queu dataMapper.prepareStatementWithColumnTypeObject(preparedStatement, optional.get().getColumnType(), idx[0]++, entry.getValue()); } + final long start = System.currentTimeMillis(); preparedStatement.executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); log.trace("successfully inserted tuple"); } finally { dataSource.close(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java index cc5840080b21ae8549632db7123e12ec72b1ee30..a0fbd1434c22be6a58d945d0fe669b5cc5be558f 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java @@ -49,26 +49,32 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche final Connection connection = dataSource.getConnection(); try { /* obtain only table metadata */ + long start = System.currentTimeMillis(); final PreparedStatement statement1 = connection.prepareStatement(mariaDbMapper.databaseTableSelectRawQuery()); statement1.setString(1, database.getInternalName()); statement1.setString(2, tableName); log.trace("1={}, 2={}", database.getInternalName(), tableName); TableDto table = dataMapper.schemaResultSetToTable(metadataMapper.privilegedDatabaseDtoToDatabaseDto(database), statement1.executeQuery()); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); /* obtain columns metadata */ + start = System.currentTimeMillis(); final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); statement2.setString(1, database.getInternalName()); statement2.setString(2, tableName); log.trace("1={}, 2={}", database.getInternalName(), tableName); final ResultSet resultSet2 = statement2.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); while (resultSet2.next()) { table = dataMapper.resultSetToTable(resultSet2, table, queryConfig); } /* obtain check constraints metadata */ + start = System.currentTimeMillis(); final PreparedStatement statement3 = connection.prepareStatement(mariaDbMapper.columnsCheckConstraintSelectRawQuery()); statement3.setString(1, database.getInternalName()); statement3.setString(2, tableName); log.trace("1={}, 2={}", database.getInternalName(), tableName); final ResultSet resultSet3 = statement3.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); while (resultSet3.next()) { final String clause = resultSet3.getString(1); table.getConstraints() @@ -77,11 +83,13 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche log.trace("found check clause: {}", clause); } /* obtain column constraints metadata */ + start = System.currentTimeMillis(); final PreparedStatement statement4 = connection.prepareStatement(mariaDbMapper.databaseTableConstraintsSelectRawQuery()); statement4.setString(1, database.getInternalName()); statement4.setString(2, tableName); log.trace("1={}, 2={}", database.getInternalName(), tableName); final ResultSet resultSet4 = statement4.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); while (resultSet4.next()) { table = dataMapper.resultSetToConstraint(resultSet4, table); for (UniqueDto uk : table.getConstraints().getUniques()) { @@ -122,11 +130,13 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche final DatabaseDto database = metadataMapper.privilegedDatabaseDtoToDatabaseDto(privilegedDatabase); try { /* obtain only view metadata */ + long start = System.currentTimeMillis(); final PreparedStatement statement1 = connection.prepareStatement(mariaDbMapper.databaseViewSelectRawQuery()); statement1.setString(1, database.getInternalName()); statement1.setString(2, viewName); log.trace("1={}, 2={}", database.getInternalName(), viewName); final ResultSet resultSet1 = statement1.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); if (!resultSet1.next()) { throw new ViewNotFoundException("Failed to find view in the information schema"); } @@ -136,11 +146,13 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche view.setCreator(database.getCreator()); view.setCreatedBy(database.getCreator().getId()); /* obtain view columns */ + start = System.currentTimeMillis(); final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); statement2.setString(1, database.getInternalName()); statement2.setString(2, viewName); log.trace("1={}, 2={}", database.getInternalName(), viewName); final ResultSet resultSet2 = statement2.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); TableDto tmp = TableDto.builder() .columns(new LinkedList<>()) .build(); 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 3d19276196895efc00834b60383ddfa226a46bc3..53d93d35dfaa11d4ec40ef5e00b73050ebea91c5 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 @@ -68,16 +68,26 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final Connection connection = dataSource.getConnection(); try { /* create query store */ - connection.prepareStatement("CREATE SEQUENCE `qs_queries_seq` NOCACHE;") + long start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateSequenceRawQuery()) .execute(); - connection.prepareStatement("CREATE TABLE `qs_queries` ( `id` bigint not null primary key default nextval(`qs_queries_seq`), `created` datetime not null default now(), `executed` datetime not null default now(), `created_by` varchar(36) not null, `query` text not null, `query_normalized` text not null, `is_persisted` boolean not null, `query_hash` varchar(255) not null, `result_hash` varchar(255), `result_number` bigint );") + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateTableRawQuery()) .execute(); - connection.prepareStatement("CREATE PROCEDURE hash_table(IN name VARCHAR(255), OUT hash VARCHAR(255), OUT count BIGINT) BEGIN DECLARE _sql TEXT; SELECT CONCAT('SELECT SHA2(GROUP_CONCAT(CONCAT_WS(\\'\\',', GROUP_CONCAT(CONCAT('`', column_name, '`') ORDER BY column_name), ') SEPARATOR \\',\\'), 256) AS hash, COUNT(*) AS count FROM `', name, '` INTO @hash, @count;') FROM `information_schema`.`columns` WHERE `table_schema` = DATABASE() AND `table_name` = name INTO _sql; PREPARE stmt FROM _sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET hash = @hash; SET count = @count; END;") + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateHashTableProcedureRawQuery()) .execute(); - connection.prepareStatement("CREATE PROCEDURE store_query(IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _username varchar(255) DEFAULT REGEXP_REPLACE(current_user(), '@.*', ''); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;") + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateStoreQueryProcedureRawQuery()) .execute(); - connection.prepareStatement("CREATE DEFINER = 'root' PROCEDURE _store_query(IN _username VARCHAR(255), IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;") + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateInternalStoreQueryProcedureRawQuery()) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -130,12 +140,14 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); try { + final long start = System.currentTimeMillis(); final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.filterToGetQueriesRawQuery(filterPersisted)); if (filterPersisted != null) { statement.setBoolean(1, filterPersisted); log.trace("filter persisted only {}", filterPersisted); } final ResultSet resultSet = statement.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); final List<QueryDto> queries = new LinkedList<>(); while (resultSet.next()) { final QueryDto query = dataMapper.resultSetToQueryDto(resultSet); @@ -168,12 +180,18 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final Connection connection = dataSource.getConnection(); try { /* export to data database sidecar */ + long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.subsetToRawTemporaryViewQuery(viewName, query.getQuery())) .executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.subsetToRawExportQuery(viewName, timestamp, filePath)) .executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.dropViewRawQuery(viewName)) .executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -191,8 +209,10 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); try { + final long start = System.currentTimeMillis(); final PreparedStatement preparedStatement = connection.prepareStatement(statement); final ResultSet resultSet = preparedStatement.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); return dataMapper.resultListToQueryResultDto(columns, resultSet); } catch (SQLException e) { log.error("Failed to execute and map time-versioned query: {}", e.getMessage()); @@ -208,8 +228,10 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); try { + final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.countRawSelectQuery(statement, timestamp)) .executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); return mariaDbMapper.resultSetToNumber(resultSet); } catch (SQLException e) { log.error("Failed to map object: {}", e.getMessage()); @@ -225,9 +247,11 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); try { - final PreparedStatement preparedStatement = connection.prepareStatement("SELECT `id`, `created`, `created_by`, `query`, `query_hash`, `result_hash`, `result_number`, `is_persisted`, `executed` FROM `qs_queries` q WHERE q.`id` = ?"); + final long start = System.currentTimeMillis(); + final PreparedStatement preparedStatement = connection.prepareStatement(mariaDbMapper.queryStoreFindQueryRawQuery()); preparedStatement.setLong(1, queryId); final ResultSet resultSet = preparedStatement.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); if (!resultSet.next()) { throw new QueryNotFoundException("Failed to find query"); } @@ -255,12 +279,14 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final Connection connection = dataSource.getConnection(); try { /* insert query into query store */ - final CallableStatement callableStatement = connection.prepareCall("{call _store_query(?, ?, ?, ?)}"); + final long start = System.currentTimeMillis(); + final CallableStatement callableStatement = connection.prepareCall(mariaDbMapper.queryStoreStoreQueryRawQuery()); callableStatement.setString(1, String.valueOf(userId)); callableStatement.setString(2, query); callableStatement.setTimestamp(3, Timestamp.from(timestamp)); callableStatement.registerOutParameter(4, Types.BIGINT); callableStatement.executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); queryId = callableStatement.getLong(4); callableStatement.close(); log.info("Stored query with id {} in database with name {}", queryId, database.getInternalName()); @@ -282,10 +308,12 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final Connection connection = dataSource.getConnection(); try { /* update query */ - final PreparedStatement preparedStatement = connection.prepareStatement("UPDATE `qs_queries` SET `is_persisted` = ? WHERE `id` = ?"); + final long start = System.currentTimeMillis(); + final PreparedStatement preparedStatement = connection.prepareStatement(mariaDbMapper.queryStoreUpdateQueryRawQuery()); preparedStatement.setBoolean(1, persist); preparedStatement.setLong(2, queryId); preparedStatement.executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); } catch (SQLException e) { log.error("Failed to (un-)persist query: {}", e.getMessage()); throw new QueryStorePersistException("Failed to (un-)persist query", e); @@ -300,8 +328,10 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); try { - connection.prepareStatement("DELETE FROM `qs_queries` WHERE `is_persisted` = false AND ABS(DATEDIFF(`created`, NOW())) >= 1") + final long start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreDeleteStaleQueriesRawQuery()) .executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); } catch (SQLException e) { log.error("Failed to delete stale queries: {}", e.getMessage()); throw new QueryStoreGCException("Failed to delete stale queries: " + e.getMessage(), e); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java index 4dacc1e094f0c0f11e6d9949ee4cbf2fbc9e8621..97778e9e11679bc349ef6037fe8c7a9150cf530a 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java @@ -25,6 +25,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.io.File; import java.sql.*; import java.time.Instant; import java.util.*; @@ -60,9 +61,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final List<TableDto> tables = new LinkedList<>(); try { /* inspect tables before views */ + final long start = System.currentTimeMillis(); final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.databaseTablesSelectRawQuery()); statement.setString(1, database.getInternalName()); final ResultSet resultSet1 = statement.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); while (resultSet1.next()) { final String tableName = resultSet1.getString(1); if (database.getTables().stream().anyMatch(t -> t.getInternalName().equals(tableName))) { @@ -92,8 +95,10 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final TableStatisticDto statistic; try { /* obtain statistic */ + final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.tableColumnStatisticsSelectRawQuery(table.getColumns(), table.getInternalName())) .executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); statistic = dataMapper.resultSetToTableStatistic(resultSet); final TableDto tmpTable = schemaService.inspectTable(table.getDatabase(), table.getInternalName()); statistic.setAvgRowLength(tmpTable.getAvgRowLength()); @@ -129,8 +134,10 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final Connection connection = dataSource.getConnection(); try { /* create table if not exists */ + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.tableCreateDtoToCreateTableRawQuery(data)) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -156,8 +163,10 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final Connection connection = dataSource.getConnection(); try { /* create table if not exists */ + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.dropTableRawQuery(tableName)) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -177,12 +186,16 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final QueryResultDto queryResult; try { /* find table data */ + long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectDatasetRawQuery( table.getDatabase().getInternalName(), table.getInternalName(), table.getColumns(), timestamp, size, page)) .executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); connection.commit(); queryResult = dataMapper.resultListToQueryResultDto(table.getColumns(), resultSet); + log.debug("mapped result in {} ms", System.currentTimeMillis() - start); } catch (SQLException e) { connection.rollback(); log.error("Failed to find data from table {}.{}: {}", table.getDatabase().getInternalName(), table.getInternalName(), e.getMessage()); @@ -203,9 +216,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final List<TableHistoryDto> history; try { /* find table data */ + final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectHistoryRawQuery( table.getDatabase().getInternalName(), table.getInternalName(), size)) .executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); history = dataMapper.resultSetToTableHistory(resultSet); connection.commit(); } catch (SQLException e) { @@ -227,9 +242,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final Long queryResult; try { /* find table data */ + final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectCountRawQuery( table.getDatabase().getInternalName(), table.getInternalName(), timestamp)) .executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); queryResult = mariaDbMapper.resultSetToNumber(resultSet); connection.commit(); } catch (SQLException e) { @@ -253,9 +270,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final Connection connection = dataSource.getConnection(); try { /* import tuple */ - data.setLocation(s3Config.getS3FilePath() + "/" + data.getLocation()); + data.setLocation(s3Config.getS3FilePath() + File.separator + data.getLocation()); + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.datasetToRawInsertQuery(table.getDatabase().getInternalName(), table, data)) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -283,7 +302,9 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table getColumnType(table.getColumns(), column), idx[0], column, data.getKeys().get(column)); idx[0]++; } + final long start = System.currentTimeMillis(); statement.executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -325,7 +346,9 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table getColumnType(table.getColumns(), entry.getKey()), idx[0], entry.getKey(), entry.getValue()); idx[0]++; } + final long start = System.currentTimeMillis(); statement.executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -359,7 +382,9 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table getColumnType(table.getColumns(), entry.getKey()), idx[0], entry.getKey(), entry.getValue()); idx[0]++; } + final long start = System.currentTimeMillis(); statement.executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -392,9 +417,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final Connection connection = dataSource.getConnection(); try { /* export to data database sidecar */ + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.tableOrViewToRawExportQuery(table.getDatabase().getInternalName(), table.getInternalName(), table.getColumns(), timestamp, filePath)) .executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java index 3cdba35f085491334300cc3fb5a3ab3ee30801fd..06cf42ae6ed0b49f56f63648cd3286b4226f727a 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.io.File; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.PreparedStatement; @@ -63,7 +64,7 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe @Override public List<ViewDto> getSchemas(PrivilegedDatabaseDto database) throws SQLException, DatabaseMalformedException, - ViewMalformedException, ViewNotFoundException, ViewSchemaException { + ViewNotFoundException { final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); final List<ViewDto> views = new LinkedList<>(); @@ -71,7 +72,9 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe /* inspect tables before views */ final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.databaseViewsSelectRawQuery()); statement.setString(1, database.getInternalName()); + final long start = System.currentTimeMillis(); final ResultSet resultSet1 = statement.executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); while (resultSet1.next()) { final String viewName = resultSet1.getString(1); if (database.getViews().stream().anyMatch(v -> v.getInternalName().equals(viewName))) { @@ -117,8 +120,10 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe .build(); try { /* create view if not exists */ + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.viewCreateRawQuery(view.getInternalName(), data.getQuery())) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); /* select view columns */ final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); statement2.setString(1, database.getInternalName()); @@ -151,10 +156,12 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe .stream() .map(metadataMapper::viewColumnDtoToColumnDto) .toList(); + final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement( mariaDbMapper.selectDatasetRawQuery(view.getDatabase().getInternalName(), view.getInternalName(), mappedColumns, timestamp, size, page)) .executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); queryResult = dataMapper.resultListToQueryResultDto(mappedColumns, resultSet); queryResult.setId(view.getId()); connection.commit(); @@ -174,8 +181,10 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe final Connection connection = dataSource.getConnection(); try { /* drop view if exists */ + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.dropViewRawQuery(view.getInternalName())) .execute(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -196,9 +205,11 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe final Long queryResult; try { /* find view data */ + final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectCountRawQuery( view.getDatabase().getInternalName(), view.getInternalName(), timestamp)) .executeQuery(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); queryResult = mariaDbMapper.resultSetToNumber(resultSet); connection.commit(); } catch (SQLException e) { @@ -217,7 +228,7 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe throws SQLException, QueryMalformedException, StorageNotFoundException, StorageUnavailableException, RemoteUnavailableException, SidecarExportException { final String fileName = RandomStringUtils.randomAlphabetic(40) + ".csv"; - final String filePath = s3Config.getS3FilePath() + "/" + fileName; + final String filePath = s3Config.getS3FilePath() + File.separator + fileName; final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); try { @@ -226,9 +237,11 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe .stream() .map(metadataMapper::viewColumnDtoToColumnDto) .toList(); + final long start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.tableOrViewToRawExportQuery(database.getInternalName(), view.getInternalName(), columns, timestamp, filePath)) .executeUpdate(); + log.debug("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); diff --git a/dbrepo-gateway-service/dbrepo.conf b/dbrepo-gateway-service/dbrepo.conf index f9c0001cebfbba5bbb842984377fb01197617d96..de8eaa417bbe422999436e83dc24f8fb66f9b1d5 100644 --- a/dbrepo-gateway-service/dbrepo.conf +++ b/dbrepo-gateway-service/dbrepo.conf @@ -134,7 +134,7 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://data; - proxy_read_timeout 90; + proxy_read_timeout 600; } location ~ /api/(database|concept|container|identifier|image|message|license|oai|ontology|unit|user) { diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java index 307c166febb3634a08991c7b5d571bcf7bf072e1..da1c28918576b0a0bf606b23c15e5d0a02a63820 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java @@ -38,9 +38,9 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway { response = restTemplate.exchange(path, HttpMethod.PUT, new HttpEntity<>(data), Void.class); } catch (HttpServerErrorException e) { log.error("Failed to grant topic permissions: {}", e.getMessage()); - throw new BrokerServiceConnectionException("Failed to grant topic permissions: " + e.getMessage()); + throw new BrokerServiceConnectionException("Failed to grant topic permissions: " + e.getMessage(), e); } catch (Exception e) { - log.error("Failed to grant topic permissions: unexpected response: {}", e.getMessage()); + log.error("Failed to grant topic permissions: unexpected response: {}", e.getMessage(), e); throw new BrokerServiceException("Failed to grant topic permissions: unexpected response: " + e.getMessage(), e); } if (!response.getStatusCode().equals(HttpStatus.CREATED) && !response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { @@ -59,7 +59,7 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway { response = restTemplate.exchange(path, HttpMethod.PUT, new HttpEntity<>(data), Void.class); } catch (HttpServerErrorException e) { log.error("Failed to grant virtual host permissions: {}", e.getMessage()); - throw new BrokerServiceConnectionException("Failed to grant virtual host permissions: " + e.getMessage()); + throw new BrokerServiceConnectionException("Failed to grant virtual host permissions: " + e.getMessage(), e); } catch (Exception e) { log.error("Failed to grant virtual host permissions: unexpected response: {}", e.getMessage()); throw new BrokerServiceException("Failed to grant virtual host permissions: unexpected response: " + e.getMessage(), e); @@ -80,7 +80,7 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway { response = restTemplate.exchange(path, HttpMethod.PUT, new HttpEntity<>(data), Void.class); } catch (HttpServerErrorException e) { log.error("Failed to grant exchange permissions: {}", e.getMessage()); - throw new BrokerServiceConnectionException("Failed to grant exchange permissions: " + e.getMessage()); + throw new BrokerServiceConnectionException("Failed to grant exchange permissions: " + e.getMessage(), e); } catch (Exception e) { log.error("Failed to grant exchange permissions: unexpected response: {}", e.getMessage()); throw new BrokerServiceException("Failed to grant exchange permissions: unexpected response: " + e.getMessage(), e); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/CrossrefGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/CrossrefGatewayImpl.java index 542d9c981d82f6b8a300808ed94ca40034c95851..8fd957f330a947793f9bfc4e4fcbb08616bbf90f 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/CrossrefGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/CrossrefGatewayImpl.java @@ -37,7 +37,7 @@ public class CrossrefGatewayImpl implements CrossrefGateway { response = restTemplate.exchange(gatewayConfig.getCrossRefEndpoint() + path, HttpMethod.GET, HttpEntity.EMPTY, CrossrefDto.class); } catch (HttpServerErrorException e) { log.error("Failed to retrieve crossref metadata: {}", e.getMessage()); - throw new DoiNotFoundException("Failed to retrieve crossref metadata: " + e.getMessage()); + throw new DoiNotFoundException("Failed to retrieve crossref metadata: " + e.getMessage(), e); } return response.getBody(); } diff --git a/dbrepo-ui/components/database/DatabaseCard.vue b/dbrepo-ui/components/database/DatabaseCard.vue index 9485fdee101584250d10bc1b8b959565a6699541..48aefa7493ce5beb52d8ec62cf794d64368b8489 100644 --- a/dbrepo-ui/components/database/DatabaseCard.vue +++ b/dbrepo-ui/components/database/DatabaseCard.vue @@ -4,11 +4,20 @@ :to="`/database/${database.id}/info`" variant="flat" rounded="0" - :href="`/database/${database.id}`"> + :href="`/database/${database.id}`" + @click="loading = true"> <v-divider class="mx-4" /> - <v-card-title - class="text-primary text-decoration-underline" - v-text="formatTitle(database)" /> + <v-card-title> + <span + class="text-primary text-decoration-underline" + v-text="formatTitle(database)" /> + <v-progress-circular + v-if="loading" + color="primary" + size="24" + class="ml-1" + indeterminate /> + </v-card-title> <v-card-subtitle v-text="formatCreators(database)" /> <v-card-text> @@ -68,6 +77,11 @@ import { formatLanguage } from '@/utils' export default { + data() { + return { + loading: false + } + }, props: { database: { default: () => { diff --git a/dbrepo-ui/components/database/DatabaseToolbar.vue b/dbrepo-ui/components/database/DatabaseToolbar.vue index 5a86bbaa01410fb068533930af6636fc9f20d1d7..0c87e5599c688b3c4feebfbadb09a8868071d54d 100644 --- a/dbrepo-ui/components/database/DatabaseToolbar.vue +++ b/dbrepo-ui/components/database/DatabaseToolbar.vue @@ -10,21 +10,21 @@ <span v-if="database && $vuetify.display.lgAndUp" v-text="database.name" /> - <v-tooltip - v-if="database" - bottom> - <template v-slot:activator="{ props }"> - <v-icon - class="mr-2" - size="small" - right - :color="database.is_public ? 'success' : 'chip'" - v-bind="props"> - {{ database.is_public ? 'mdi-lock-open-outline' : 'mdi-lock-outline' }} - </v-icon> - </template> - <span>{{ $t('toolbars.database.' + (database.is_public ? 'public' : 'private')) }}</span> - </v-tooltip> + <v-chip + v-if="database && database.is_public" + size="small" + class="ml-2" + color="success" + :text="$t('toolbars.database.public')" + variant="outlined" /> + <v-chip + v-if="database && !database.is_public" + size="small" + class="ml-2" + :color="colorVariant" + variant="outlined" + :text="$t('toolbars.database.private')" + flat /> </v-toolbar-title> <v-spacer /> <v-btn @@ -33,6 +33,14 @@ color="tertiary" :variant="buttonVariant" :text="$t('toolbars.database.dashboard.permanent') + ($vuetify.display.lgAndUp ? ' ' + $t('toolbars.database.dashboard.xl') : '')" /> + <v-btn + v-if="canCreateTable" + :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-table-large-plus' : null" + color="secondary" + variant="flat" + :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-table.xl') + ' ' : '') + $t('toolbars.database.create-table.permanent')" + class="mr-2" + :to="`/database/${$route.params.database_id}/table/create/dataset`" /> <v-btn v-if="canCreateSubset" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-wrench' : null" @@ -49,14 +57,6 @@ :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')" class="mr-2 white--text" :to="`/database/${$route.params.database_id}/view/create`" /> - <v-btn - v-if="canCreateTable" - :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-table-large-plus' : null" - color="secondary" - variant="flat" - :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-table.xl') + ' ' : '') + $t('toolbars.database.create-table.permanent')" - class="mr-2" - :to="`/database/${$route.params.database_id}/table/create/dataset`" /> <v-btn v-if="canCreateIdentifier" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-identifier' : null" @@ -117,6 +117,9 @@ export default { roles () { return this.userStore.getRoles }, + colorVariant () { + return this.isContrastTheme ? '' : (this.isDarkTheme ? 'tertiary' : 'secondary') + }, canCreateIdentifier () { if (!this.roles) { return false diff --git a/dbrepo-ui/components/identifier/Citation.vue b/dbrepo-ui/components/identifier/Citation.vue index 8cd96902d48009da22cd761849be1962ba07c248..6f35ac915e255610965015ccd16adcf1dad03597 100644 --- a/dbrepo-ui/components/identifier/Citation.vue +++ b/dbrepo-ui/components/identifier/Citation.vue @@ -10,8 +10,8 @@ <v-select v-model="style" :items="styles" - item-title="style" - item-value="accept" + item-title="title" + item-value="value" dense variant="outlined" single-line /> @@ -33,39 +33,38 @@ export default { return { loading: false, styles: [ - { style: 'APA', accept: 'text/bibliography;style=apa' }, - { style: 'IEEE', accept: 'text/bibliography;style=ieee' }, - { style: 'BibTeX', accept: 'text/bibliography;style=bibtex' } + { title: 'APA', value: 'text/bibliography;style=apa' }, + { title: 'IEEE', value: 'text/bibliography;style=ieee' }, + { title: 'BibTeX', value: 'text/bibliography;style=bibtex' } ], - style: null, + style: 'text/bibliography;style=apa', citation: null } }, watch: { style () { - this.loadCitation(this.style) + this.loadCitation() }, pid () { - this.loadCitation(this.style) + this.loadCitation() } }, mounted () { - this.style = this.styles[0].accept - this.loadCitation(null) + this.loadCitation() }, methods: { - loadCitation (accept) { - if (!this.identifier || !accept) { + loadCitation () { + if (!this.identifier || !this.style) { return } this.loading = true const identifierService = useIdentifierService() - identifierService.findOne(this.identifier.id, accept) + identifierService.findOne(this.identifier.id, this.style) .then((citation) => { this.citation = citation this.loading = false }) - .error(({code, message}) => { + .catch(({code, message}) => { const toast = useToastInstance() toast.error(this.$t(`${code}: ${message}`)) this.loading = false diff --git a/dbrepo-ui/components/subset/SubsetList.vue b/dbrepo-ui/components/subset/SubsetList.vue index 21d4e7263eedd942f8abfec1470dd3a13af9d53e..b977daffa5aaf71dbe2980b98761244e8a3627b0 100644 --- a/dbrepo-ui/components/subset/SubsetList.vue +++ b/dbrepo-ui/components/subset/SubsetList.vue @@ -1,7 +1,7 @@ <template> <div> <v-card - v-if="!loadingSubsets && queries.length === 0" + v-if="!loadingSubsets && subsets.length === 0" variant="flat" rounded="0" :text="$t('pages.database.subpages.subsets.empty')" /> @@ -14,7 +14,7 @@ <Loading /> </v-list-item> <div - v-for="(item, i) in queries" + v-for="(item, i) in subsets" :key="`q-${i}`"> <v-divider v-if="i !== 0" class="mx-4" /> <v-list> @@ -54,9 +54,7 @@ export default { return { loadingSubsets: false, loadingIdentifiers: false, - queries: [], - identifiers: [], - isAuthorizationError: false, + subsets: [], cacheStore: useCacheStore(), userStore: useUserStore() } @@ -77,8 +75,9 @@ export default { this.loadingSubsets = true const queryService = useQueryService() queryService.findAll(this.$route.params.database_id, true) - .then((queries) => { - this.queries = queries + .then((subsets) => { + this.loadingSubsets = false + this.subsets = subsets }) .catch(({code}) => { this.loadingSubsets = false @@ -88,9 +87,6 @@ export default { } toast.error(this.$t(code)) }) - .finally(() => { - this.loadingSubsets = false - }) }, title (query) { if (query.identifiers.length === 0) { diff --git a/dbrepo-ui/components/subset/SubsetToolbar.vue b/dbrepo-ui/components/subset/SubsetToolbar.vue index b51cc1d089085b657cb72d689fbf9ef2c6776b36..5c5081a2f8f5c3944928bbd59cd76dddc2c79d89 100644 --- a/dbrepo-ui/components/subset/SubsetToolbar.vue +++ b/dbrepo-ui/components/subset/SubsetToolbar.vue @@ -1,6 +1,7 @@ <template> <div> - <v-toolbar flat> + <v-toolbar + flat> <v-btn class="mr-2" variant="plain" @@ -27,16 +28,16 @@ color="secondary" variant="flat" class="mb-1 ml-2" - :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null" + :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-star' : null" :text="$t('toolbars.subset.save.permanent')" @click.stop="save" /> <v-btn v-if="canForgetQuery" :loading="loadingSave" - color="error" + color="warning" variant="flat" class="mb-1 ml-2" - :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-trash-can-outline' : null" + :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-star-off' : null" :text="$t('toolbars.subset.unsave.permanent')" @click.stop="forget" /> <v-btn @@ -116,7 +117,10 @@ export default { if (!this.database) { return false } - return this.database.is_public + if (this.database.is_public) { + return true + } + return this.access }, identifier () { /* mount pid */ @@ -154,7 +158,7 @@ export default { return formatTimestampUTCLabel(this.subset.created) }, result_visibility () { - if (!this.database) { + if (!this.database || !this.subset) { return false } if (this.database.is_public) { diff --git a/dbrepo-ui/components/view/ViewToolbar.vue b/dbrepo-ui/components/view/ViewToolbar.vue index c107c3c0ef006c2d5456f6db187908e092664c5a..c43b730397a977559f85b55f92e62b486b15aa6f 100644 --- a/dbrepo-ui/components/view/ViewToolbar.vue +++ b/dbrepo-ui/components/view/ViewToolbar.vue @@ -11,16 +11,17 @@ <v-spacer /> <v-btn v-if="canDeleteView" - prepend-icon="mdi-delete" class="mr-2" variant="flat" - color="error" - :text="$vuetify.display.lgAndUp ? $t('navigation.delete') : ''" + :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-delete' : null" :loading="loadingDelete" + color="error" + :text="$t('navigation.delete')" @click="deleteView" /> <v-btn v-if="canCreatePid" - prepend-icon="mdi-content-save-outline" + class="mr-2" + :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null" variant="flat" color="primary" :text="($vuetify.display.lgAndUp ? $t('toolbars.view.pid.xl') + ' ' : '') + $t('toolbars.view.pid.permanent')" @@ -63,6 +64,10 @@ export default { database () { return this.cacheStore.getDatabase }, + buttonVariant () { + const runtimeConfig = useRuntimeConfig() + return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal + }, view () { if (!this.database) { return null diff --git a/dbrepo-ui/composables/axios-instance.ts b/dbrepo-ui/composables/axios-instance.ts index a2b6ca4f6ef5b138473b7812b460981a6f7c7f2f..d4f274b717e938c3305bc20463c93d9e9474e079 100644 --- a/dbrepo-ui/composables/axios-instance.ts +++ b/dbrepo-ui/composables/axios-instance.ts @@ -8,7 +8,7 @@ export const useAxiosInstance = () => { const userStore = useUserStore() if (!instance) { instance = axios.create({ - timeout: 10_000, + timeout: 90_000, params: {}, headers: { Accept: 'application/json', diff --git a/dbrepo-ui/composables/identifier-service.ts b/dbrepo-ui/composables/identifier-service.ts index a85f05a45c5d4211ca9374b874ccc367d5b56372..96e5610c8c11161b0a4c1687e630204f80e53554 100644 --- a/dbrepo-ui/composables/identifier-service.ts +++ b/dbrepo-ui/composables/identifier-service.ts @@ -2,7 +2,7 @@ import type {AxiosError, AxiosRequestConfig} from 'axios' import {axiosErrorToApiError} from '@/utils' export const useIdentifierService = (): any => { - async function findOne(id: number, accept: string | null): Promise<IdentifierDto> { + async function findOne(id: number, accept: string): Promise<IdentifierDto> { const axios = useAxiosInstance() console.debug('find identifier with id', id) const config: AxiosRequestConfig = { diff --git a/dbrepo-ui/composables/query-service.ts b/dbrepo-ui/composables/query-service.ts index fa18a55ec053bad22339a14bcb053b9a405f3716..abcd928b66d0350fd855ae380b959a33818b8f3d 100644 --- a/dbrepo-ui/composables/query-service.ts +++ b/dbrepo-ui/composables/query-service.ts @@ -13,6 +13,10 @@ export const useQueryService = (): any => { resolve(response.data) }) .catch((error) => { + if (error.response.status === 403) { + /* ignore */ + resolve([]) + } console.error('Failed to find queries', error) reject(axiosErrorToApiError(error)) }) @@ -77,7 +81,7 @@ export const useQueryService = (): any => { const axios = useAxiosInstance() console.debug('execute query in database with id', databaseId) return new Promise<QueryResultDto>((resolve, reject) => { - axios.post<QueryResultDto>(`/api/database/${databaseId}/subset`, data, {params: mapFilter(timestamp, page, size)}) + axios.post<QueryResultDto>(`/api/database/${databaseId}/subset`, data, {params: mapFilter(timestamp, page, size), timeout: 600_000}) .then((response) => { console.info('Executed query with id', response.data.id, ' in database with id', databaseId) resolve(response.data) @@ -93,7 +97,7 @@ export const useQueryService = (): any => { const axios = useAxiosInstance() console.debug('re-execute query in database with id', databaseId) return new Promise<QueryResultDto>((resolve, reject) => { - axios.get<QueryResultDto>(`/api/database/${databaseId}/subset/${queryId}/data`, { params: mapFilter(null, page, size), timeout: 30_000 }) + axios.get<QueryResultDto>(`/api/database/${databaseId}/subset/${queryId}/data`, { params: mapFilter(null, page, size) }) .then((response) => { console.info('Re-executed query in database with id', databaseId) resolve(response.data) @@ -109,7 +113,7 @@ export const useQueryService = (): any => { const axios = useAxiosInstance() console.debug('re-execute query in database with id', databaseId) return new Promise<number>((resolve, reject) => { - axios.head<void>(`/api/database/${databaseId}/subset/${queryId}/data`, { timeout: 30_000 }) + axios.head<void>(`/api/database/${databaseId}/subset/${queryId}/data`) .then((response) => { const count: number = Number(response.headers['x-count']) console.info('Found', count, 'tuples for query', queryId, 'in database with id', databaseId) diff --git a/dbrepo-ui/composables/table-service.ts b/dbrepo-ui/composables/table-service.ts index 88ab27b39ee93d632d4492ed4a2d2d776cc08e8c..ffd7ebcd6074cc23d9bfa9412e19e48ef87f8f37 100644 --- a/dbrepo-ui/composables/table-service.ts +++ b/dbrepo-ui/composables/table-service.ts @@ -71,7 +71,7 @@ export const useTableService = (): any => { const axios = useAxiosInstance() console.debug('get data for table with id', tableId, 'in database with id', databaseId); return new Promise<QueryResultDto>((resolve, reject) => { - axios.get<QueryResultDto>(`/api/database/${databaseId}/table/${tableId}/data`, { params: mapFilter(timestamp, page, size), timeout: 30_000 }) + axios.get<QueryResultDto>(`/api/database/${databaseId}/table/${tableId}/data`, { params: mapFilter(timestamp, page, size) }) .then((response) => { console.info('Got data for table with id', tableId, 'in database with id', databaseId) resolve(response.data) @@ -87,7 +87,7 @@ export const useTableService = (): any => { const axios = useAxiosInstance() console.debug('get data count for table with id', tableId, 'in database with id', databaseId); return new Promise<number>((resolve, reject) => { - axios.head<void>(`/api/database/${databaseId}/table/${tableId}/data`, { params: mapFilter(timestamp, null, null), timeout: 30_000 }) + axios.head<void>(`/api/database/${databaseId}/table/${tableId}/data`, { params: mapFilter(timestamp, null, null) }) .then((response: AxiosResponse<void>) => { const count: number = Number(response.headers['x-count']) console.info('Found' + count + 'in table with id', tableId, 'in database with id', databaseId) @@ -191,7 +191,7 @@ export const useTableService = (): any => { const axios = useAxiosInstance() console.debug('suggest semantic entities for table column with id', columnId, 'of table with id', tableId, 'of database with id', databaseId) return new Promise<TableColumnEntityDto[]>((resolve, reject) => { - axios.get<TableColumnEntityDto[]>(`/api/database/${databaseId}/table/${tableId}/column/${columnId}/suggest`, {timeout: 10000}) + axios.get<TableColumnEntityDto[]>(`/api/database/${databaseId}/table/${tableId}/column/${columnId}/suggest`) .then((response) => { console.info('Suggested semantic entities for table column with id', columnId, 'of table with id', tableId, 'of database with id', databaseId) resolve(response.data) diff --git a/dbrepo-ui/composables/view-service.ts b/dbrepo-ui/composables/view-service.ts index adc76e32a1407f7e893cbe92692ec0b2f597591b..642a7c6e51ca7344dae72dd3ee12550c84c893a3 100644 --- a/dbrepo-ui/composables/view-service.ts +++ b/dbrepo-ui/composables/view-service.ts @@ -37,7 +37,7 @@ export const useViewService = (): any => { const axios = useAxiosInstance() console.debug('re-execute view with id', viewId, 'in database with id', databaseId) return new Promise<QueryResultDto>((resolve, reject) => { - axios.get<QueryResultDto>(`/api/database/${databaseId}/view/${viewId}/data`, { params: {page, size}, timeout: 30_000 }) + axios.get<QueryResultDto>(`/api/database/${databaseId}/view/${viewId}/data`, { params: {page, size} }) .then((response) => { console.info('Re-executed view with id', viewId, 'in database with id', databaseId) resolve(response.data) @@ -53,7 +53,7 @@ export const useViewService = (): any => { const axios = useAxiosInstance() console.debug('re-execute view with id', viewId, 'in database with id', databaseId) return new Promise<number>((resolve, reject) => { - axios.head<number>(`/api/database/${databaseId}/view/${viewId}/data`, { timeout: 30_000 }) + axios.head<number>(`/api/database/${databaseId}/view/${viewId}/data`) .then((response) => { const count: number = Number(response.headers['x-count']) console.info('Found', count, 'tuples for view with id', viewId, 'in database with id', databaseId) diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index cf8dcfd03e8df91fd4ba737c4c3662dec41402f0..be8ddfcc2317d441d3a8b6edacd4b3f5a663c6af 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -1382,7 +1382,7 @@ }, "subset": { "save": { - "permanent": "Save" + "permanent": "Star" }, "export": { "data": { @@ -1399,7 +1399,7 @@ "permanent": "PID" }, "unsave": { - "permanent": "Unsave" + "permanent": "Unstar" } }, "view": { diff --git a/dbrepo-ui/pages/database/[database_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/settings.vue index 61e9dc62449b48cba17d77b4bb717e8164c9f257..a79ad48c4193c9b4f0fbc83f84b3a2c0db3a6193 100644 --- a/dbrepo-ui/pages/database/[database_id]/settings.vue +++ b/dbrepo-ui/pages/database/[database_id]/settings.vue @@ -422,7 +422,7 @@ export default { databaseService.updateVisibility(this.$route.params.database_id, this.modifyVisibility) .then((database) => { const toast = useToastInstance() - toast.success('success.database.visibility') + toast.success(this.$t('success.database.visibility')) this.cacheStore.setDatabase(database) }) .catch(() => { diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue index f740416faaf70387ed73935754c777435f58fa67..4902e2c54f243c7a2e4f3df90f3aa16cf7c26753 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue @@ -14,6 +14,15 @@ v-else v-text="executionUTC" /> </v-toolbar-title> + <v-spacer /> + <v-btn + :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-refresh' : null" + variant="flat" + :text="$t('toolbars.table.data.refresh')" + class="mr-2" + :disabled="loadingSubset" + :loading="loadingSubset" + @click="loadSubset" /> </v-toolbar> <v-card tile> <QueryResults diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue index d52ac91642901fe4b012345c1309235ea4f66420..1d9101fbf240d8d577026e53361f2cedb8f6486e 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue @@ -103,7 +103,12 @@ <script setup> const config = useRuntimeConfig() const { database_id, subset_id } = useRoute().params -const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}/subset/${subset_id}`) +const requestConfig = { timeout: 90_000, headers: { Accept: 'application/json', 'Content-Type': 'application/json' } } +const userStore = useUserStore() +if (userStore.getToken) { + requestConfig.headers.Authorization = `Bearer ${userStore.getToken}` +} +const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}/subset/${subset_id}`, requestConfig) if (data.value) { const identifierService = useIdentifierService() useServerHead(identifierService.subsetToServerHead(data.value)) diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue index 2d7195fbcdebf9645fc2362f867c4a946e81c2d1..bcab9b60be8812f270836644e5f521553a22040d 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue @@ -72,7 +72,7 @@ :headers="headers" :items="rows" :items-length="total" - :loading="loadingData" + :loading="loadingData || loadingCount" :options.sync="options" :footer-props="footerProps" @update:options="loadData"> @@ -192,9 +192,6 @@ export default { } }, computed: { - loadingColor () { - return this.error ? 'error' : 'primary' - }, roles () { return this.userStore.getRoles }, @@ -283,6 +280,7 @@ export default { }, watch: { version () { + this.loadCount() this.reload() }, table (newTable, oldTable) { @@ -292,8 +290,8 @@ export default { } }, mounted () { - this.reload() this.loadProperties() + this.loadCount() }, methods: { addTuple () { @@ -429,9 +427,20 @@ export default { reload () { this.lastReload = new Date() this.loadData({ page: this.options.page, itemsPerPage: this.options.itemsPerPage, sortBy: null}) - this.loadCount() }, - loadData ({ page, itemsPerPage, sortBy }) { + loadCount() { + this.loadingCount = true + const tableService = useTableService() + tableService.getCount(this.$route.params.database_id, this.$route.params.table_id, (this.versionISO || this.lastReload.toISOString())) + .then((count) => { + this.total = count + this.loadingCount = false + }) + .catch((error) => { + this.loadingCount = false + }) + }, + loadData({ page, itemsPerPage, sortBy }) { this.options.page = page this.options.itemsPerPage = itemsPerPage const tableService = useTableService() @@ -464,23 +473,6 @@ export default { toast.error(this.$t(code) + ": " + message) }) }, - loadCount () { - const tableService = useTableService() - this.loadingCount = true - tableService.getCount(this.$route.params.database_id, this.$route.params.table_id, (this.versionISO || this.lastReload.toISOString())) - .then((count) => { - this.total = count - this.loadingCount = false - }) - .catch(({code, message}) => { - this.loadingCount = false - const toast = useToastInstance() - if (typeof code !== 'string' || typeof message !== 'string') { - return - } - toast.error(this.$t(code) + ": " + message) - }) - }, isFileField (column) { return ['blob', 'longblob', 'mediumblob', 'tinyblob'].includes(column.column_type) }, diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue index 03464f5dbba66d89312f7b1c4a55332c79af161a..838ef2f0f1adf0a90f722ffd73d43457a5b92186 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue @@ -10,7 +10,7 @@ :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-refresh' : null" variant="flat" :text="$t('toolbars.table.data.refresh')" - class="mb-1 ml-2" + class="mb-1 mr-2" :loading="loadingData" @click="reload" /> </v-toolbar> diff --git a/dbrepo-ui/utils/index.ts b/dbrepo-ui/utils/index.ts index d940d076ae2b2d4fafac31c6dca8ad833342f64a..995c03a82795545858c477326a57ef93296c4d5b 100644 --- a/dbrepo-ui/utils/index.ts +++ b/dbrepo-ui/utils/index.ts @@ -1082,28 +1082,30 @@ export function timestampsToHumanDifference(date1: string, date2: string) { return moment.duration(other.diff(date)).humanize(true) } -export function sizeToHumanLabel(num: number) { +export function sizeToHumanLabel(num: number): string { let number = Number(num) if (!number) { return '0 B' } if (number < 1000) { - return `${Math.floor(number)} B` + return `${roundTwoDecimals(number)} B` } number = number / 1000 if (number < 1000) { - return `${Math.floor(number)} kB` + return `${roundTwoDecimals(number)} kB` } number = number / 1000 if (number < 1000) { - return `${Math.floor(number)} MB` + return `${roundTwoDecimals(number)} MB` } number = number / 1000 if (number < 1000) { - return `${number} GB` + return `${roundTwoDecimals(number)} GB` } number = number / 1000 - if (number < 1000) { - return `${number} TB` - } + return `${roundTwoDecimals(number)} TB` +} + +export function roundTwoDecimals(num: number): number { + return Math.round((num + Number.EPSILON) * 100) / 100 } diff --git a/docker-compose.yml b/docker-compose.yml index 3ffd0e04a33d4e09e73b4cb8b8a56dfa487283a3..6408cec90431dde9cf7ed97d57d6b30040d9cbf0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -310,6 +310,9 @@ services: APP_VERSION: ${APP_VERSION:-latest} COMMIT: ${CI_COMMIT_SHA:-} network: host + environment: + NUXT_PUBLIC_API_CLIENT: "${BASE_URL:-http://localhost}" + NUXT_PUBLIC_UPLOAD_CLIENT: "${BASE_URL:-http://localhost}/api/upload/files" depends_on: dbrepo-search-service: condition: service_started diff --git a/helm/dbrepo/templates/auth-configmap.yaml b/helm/dbrepo/templates/auth-configmap.yaml index 0ef8e90bf95eb5575f2f4fd1628fcf6b8c16e120..269d18c99d7800101afc8ac1c3528104fad7243c 100644 --- a/helm/dbrepo/templates/auth-configmap.yaml +++ b/helm/dbrepo/templates/auth-configmap.yaml @@ -2184,7 +2184,7 @@ data: } ], "org.keycloak.storage.UserStorageProvider" : [ { "id" : "c109d473-5ce1-4032-af7b-02e5442f5c07", - "name" : "openldap", + "name" : "Identity Service", "providerId" : "ldap", "subComponents" : { "org.keycloak.storage.ldap.mappers.LDAPStorageMapper" : [ { @@ -2944,4 +2944,4 @@ data: "policies" : [ ] } } -{{- end }} \ No newline at end of file +{{- end }} diff --git a/helm/dbrepo/templates/metadata-secret.yaml b/helm/dbrepo/templates/metadata-secret.yaml index ac9fbb32fe149c8d64942ea08424bf7344bd4bf4..0718b02ea1f7ed10d4a53d9d93e04ce985104bb1 100644 --- a/helm/dbrepo/templates/metadata-secret.yaml +++ b/helm/dbrepo/templates/metadata-secret.yaml @@ -36,7 +36,7 @@ stringData: METADATA_HOST: "{{ .Values.metadatadb.host }}" METADATA_JDBC_EXTRA_ARGS: "{{ .Values.metadatadb.jdbcExtraArgs }}" METADATA_USERNAME: "{{ .Values.metadatadb.rootUser.user }}" - METADATA_PASSWORD: "{{ .Values.metadatadb.rootUser.password }}" + METADATA_DB_PASSWORD: "{{ .Values.metadatadb.rootUser.password }}" PID_BASE: "{{ $pidBase }}" REPOSITORY_NAME: "{{ .Values.metadataservice.repositoryName }}" ROR_ENDPOINT: "{{ .Values.metadataservice.ror.endpoint}}" diff --git a/helm/dbrepo/templates/search-db-secret.yaml b/helm/dbrepo/templates/search-db-secret.yaml deleted file mode 100644 index 81cc79db7c68449bd4b416863db000c818ec59a7..0000000000000000000000000000000000000000 --- a/helm/dbrepo/templates/search-db-secret.yaml +++ /dev/null @@ -1,79 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/tls -metadata: - name: search-db-secret - namespace: {{ .Values.namespace }} -data: - tls.crt: | - LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM0akNDQWNxZ0F3SUJBZ0lSQVAvcFJoaFQ5 - SFVWaUFzYitybmJjdkV3RFFZSktvWklodmNOQVFFTEJRQXcKRkRFU01CQUdBMVVFQXhNSmMyVmhj - bU5vTFdSaU1CNFhEVEkwTURRd056RTRORFEwT0ZvWERUSTBNRGN3TmpFNApORFEwT0Zvd0ZERVNN - QkFHQTFVRUF4TUpjMlZoY21Ob0xXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DCkFROEFN - SUlCQ2dLQ0FRRUFzb3lTWTduV3J0MTRVQjNST0kwOWRtMVNSU2lDZ2hQYVhwRlJTMjhjalpNSUFz - TUoKR2ZwREZ5VktOQ3pTV0VZN0J2M1JpOHlrRnlZNkpFb2p0S3oxdk9GNnNyQ2JYZnhsY1NiZDk3 - UVYwdU9IYTZKRApsWGN2aUJEKzN2ZTB0K0MzRGFPSFVMY1liVWkzS2xOS3FwTDU1Q2ZNeTYzdU4z - a21zekRwTjVycWhOYnBlVTAxCnd2NFZNaldNZ3RlU1VpWDNqeU1EcUFOa1B3UXFiYnZHN0hBUm54 - Q0QvMHJFeEVvRjNqRCtGV01XbEVjdXR1VGkKbFJ5QnN3L1FLTWd0aVJVSFJXYUJGK2ZES0wxSUoz - YVhmcDR5bmNhL2tCR3pxVGpqb3dJb2R0MEdOZjBFa1QyQgpTWG9hZGtwdVptT2JiVDF2UmN1T1BH - UEZjWVd5Qm1ucS9adEorUUlEQVFBQm95OHdMVEFkQmdOVkhTVUVGakFVCkJnZ3JCZ0VGQlFjREFR - WUlLd1lCQlFVSEF3SXdEQVlEVlIwVEFRSC9CQUl3QURBTkJna3Foa2lHOXcwQkFRc0YKQUFPQ0FR - RUFlUU4vaUsvRzhHbGt5R0w1NjlrZnBiWEE2bE8vRHFObGlXRkgrY2ZIZ0NzYWxKMWVSSjliY1RZ - dgo0S3Y0MDlWUWpCbVg0WTRqMUt6R1ZnYkZaZkh1Ry9Nb0dzWVVnQ1VjTm94ZThtM0ZUcjRwYnZT - MXNUV0V4cGFNCkpSMURQQmNMV0o3MndTQzBkRFpISC9hVVNSMUs4UGpnMWtaMVRINTdvZDJoNWpJ - RUFhZkd1ZGhzejVpWlZQcVkKR1lrakZhRklVeXpjWkxUbjFBNXRwSlpTRmhxZHZGQmFndURUYkp4 - NmROVWZVc0sxZXFuaThSQVN6L3dPbHQwcQpSckExbVdCTEI1NW9XRzh4ZXZicmtNNUNuSWVvL2hS - SG83cE1pUFQxWE5uT2cvNjhmZEc4T0lXMFFhNjdMVEZnCnU2dTkxQ1BmVk5KVHQ5bmlZWHJ4N1hl - SEJ2dW1iUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - ca.crt: | - LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM0akNDQWNxZ0F3SUJBZ0lSQVAvcFJoaFQ5 - SFVWaUFzYitybmJjdkV3RFFZSktvWklodmNOQVFFTEJRQXcKRkRFU01CQUdBMVVFQXhNSmMyVmhj - bU5vTFdSaU1CNFhEVEkwTURRd056RTRORFEwT0ZvWERUSTBNRGN3TmpFNApORFEwT0Zvd0ZERVNN - QkFHQTFVRUF4TUpjMlZoY21Ob0xXUmlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DCkFROEFN - SUlCQ2dLQ0FRRUFzb3lTWTduV3J0MTRVQjNST0kwOWRtMVNSU2lDZ2hQYVhwRlJTMjhjalpNSUFz - TUoKR2ZwREZ5VktOQ3pTV0VZN0J2M1JpOHlrRnlZNkpFb2p0S3oxdk9GNnNyQ2JYZnhsY1NiZDk3 - UVYwdU9IYTZKRApsWGN2aUJEKzN2ZTB0K0MzRGFPSFVMY1liVWkzS2xOS3FwTDU1Q2ZNeTYzdU4z - a21zekRwTjVycWhOYnBlVTAxCnd2NFZNaldNZ3RlU1VpWDNqeU1EcUFOa1B3UXFiYnZHN0hBUm54 - Q0QvMHJFeEVvRjNqRCtGV01XbEVjdXR1VGkKbFJ5QnN3L1FLTWd0aVJVSFJXYUJGK2ZES0wxSUoz - YVhmcDR5bmNhL2tCR3pxVGpqb3dJb2R0MEdOZjBFa1QyQgpTWG9hZGtwdVptT2JiVDF2UmN1T1BH - UEZjWVd5Qm1ucS9adEorUUlEQVFBQm95OHdMVEFkQmdOVkhTVUVGakFVCkJnZ3JCZ0VGQlFjREFR - WUlLd1lCQlFVSEF3SXdEQVlEVlIwVEFRSC9CQUl3QURBTkJna3Foa2lHOXcwQkFRc0YKQUFPQ0FR - RUFlUU4vaUsvRzhHbGt5R0w1NjlrZnBiWEE2bE8vRHFObGlXRkgrY2ZIZ0NzYWxKMWVSSjliY1RZ - dgo0S3Y0MDlWUWpCbVg0WTRqMUt6R1ZnYkZaZkh1Ry9Nb0dzWVVnQ1VjTm94ZThtM0ZUcjRwYnZT - MXNUV0V4cGFNCkpSMURQQmNMV0o3MndTQzBkRFpISC9hVVNSMUs4UGpnMWtaMVRINTdvZDJoNWpJ - RUFhZkd1ZGhzejVpWlZQcVkKR1lrakZhRklVeXpjWkxUbjFBNXRwSlpTRmhxZHZGQmFndURUYkp4 - NmROVWZVc0sxZXFuaThSQVN6L3dPbHQwcQpSckExbVdCTEI1NW9XRzh4ZXZicmtNNUNuSWVvL2hS - SG83cE1pUFQxWE5uT2cvNjhmZEc4T0lXMFFhNjdMVEZnCnU2dTkxQ1BmVk5KVHQ5bmlZWHJ4N1hl - SEJ2dW1iUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - tls.key: | - LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZB - QVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3lqSkpqdWRhdTNYaFEKSGRFNGpUMTJiVkpGS0lLQ0U5cGVr - VkZMYnh5Tmt3Z0N3d2taK2tNWEpVbzBMTkpZUmpzRy9kR0x6S1FYSmpvawpTaU8wclBXODRYcXlz - SnRkL0dWeEp0MzN0QlhTNDRkcm9rT1ZkeStJRVA3ZTk3UzM0TGNObzRkUXR4aHRTTGNxClUwcXFr - dm5rSjh6THJlNDNlU2F6TU9rM211cUUxdWw1VFRYQy9oVXlOWXlDMTVKU0pmZVBJd09vQTJRL0JD - cHQKdThic2NCR2ZFSVAvU3NURVNnWGVNUDRWWXhhVVJ5NjI1T0tWSElHekQ5QW95QzJKRlFkRlpv - RVg1OE1vdlVnbgpkcGQrbmpLZHhyK1FFYk9wT09PakFpaDIzUVkxL1FTUlBZRkplaHAyU201bVk1 - dHRQVzlGeTQ0OFk4VnhoYklHCmFlcjltMG41QWdNQkFBRUNnZ0VCQUtPZ3A5ZTB5OFhkT1JGVEFo - WXRlaEk2QlpkVGxLYll3dHEvbWh6amF1dGoKdjRlb2JZTGRFdmIzT1pXdkxlV3dGeEJGTS9CR1Rt - cllvWmY0U2RpZVdXWUx6WUpNejFYR3BNQ1p1Zm56azd4OAp2L0luOW4vWGhqdlFONExteHp0c09O - WEs4NHRKQUozR2NmWGI5eVZ6SklldTRjUVhWYVNJNXFwNVBJRzArdzlZCnk2NTFWZkZJQUd3SmRI - QlpId1lmQUdxbU5oVlo3MDc4TVUxQWU2Y2VkZjJ0RnlWYW5ScXBLUFZ1Z0tGQy9kRG8KVXJIMHRJ - ajFkU3RKRGxucHJ3YVYrMDRkUDBvZnlBc09ablp3VXRzZE8vM1ZMMWR4bCtIT1dGeUpvSjI1dkF2 - eAp5ZW5qc0dzd1pJRW1oUzM0NVRVTlFNbTJzYnJtYklMS1dpWEp5SmlEeWdFQ2dZRUF4QUNEbWxG - ZmsxZDlkSmJTCi90NDRGTzFiUVQ2b1Z2VWJ2NGMwcnRLVVhwTnFlajNmbXl4bUJINm82SkhoYjNO - eXdWa3U0QW9YTXBFTkhISDAKNlN2TzBYM2U1MU8xOXppQ25hMGdoZExSS3JIS3ZqbzRNdmdiV3Ey - Z3NJNmpJQkxTcU0zNXNxTjhGRXRVZko4TQpKRUZIMThJTThTRjEyQ2hWYjVWaThTTDgvY2tDZ1lF - QTZUUmhVQi9nQVM1RmN5V1NHNGR4UWtHWG03R1lSamFrCjJVWUlOUUIyV2d2Qk9vN2tvV21nR1M0 - eE9YanJsZ2NrMmhsTEhZSXAycnRoSWdNMUdBQUdqa2lYeXJPVE9kaGQKeUJ0RjBMS0kvVjlBZUxK - eExkRDBYalB0WThIYXdTcW8weGdxUml2RzlJUFBlSGZ6SmlraXV6enNOT3pjVlk1aApkZktqZy9J - eVFyRUNnWUJ1c0dlaDk4Q0ZBa3pNVWZ6NGlHQ2RtT29ITDY1NzVWSjFXSkx0QStsY2U5NFBDUEJG - CnZzNGlUYkZ3SGlwMCtYcmVMRkpubmVzNTJHYlNJSjBTTFhaUUlzaUdWV1VYSjZmRUNpaXF5c0xy - WEpyRjBUVTUKdTVvZkhKejUrS094RWxBN21vOGdUbWxkUUttRzgzODAzbFVIU1FSc0ROeHpaVnZT - ZDBmNExDMDUyUUtCZ0h0YQpzNmJZVlhzS2FMNFJ2NGxFU1lxTWU0OWxqM0NFY3dwaTJ2QitRQnc5 - WDRhRUV6ZTJVWE5BVmRWYXV2THU4SFZWCkw4QjZHMzJSNUQxRGlSQWE0MXpiMVQ3cFloVVU5L1pq - UnJpdjEzcCtxZkd1SWVQa1JYNlc1UmtCYjU4QjI2OWQKZHU4TE5RQWR3TjZ1UkRXSlNNL1YxL1Bl - M21WN0hONXc3RUZkR1d6aEFvR0FiN29HOWhoTGxTL212bEhHVE11egp6SXhtSTBaTisyZGFqNEYy - MDZHRGJYaUkwYUtFTHlteVJkcEZYcStzaWpaTzFqdXU4WDhMS0FCOUVaeEtXRzRICjNnQWoxWGFF - QXFjNjFkaFkveHRHUDR2OXN3UElmVjR3NGtTUHBWYlFFd3ZIcko4K1V2NjhuOTZTU3dkK1BBNisK - OFFwUEVYSXlscXB4UlFyMnN5amhoOTQ9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K \ No newline at end of file diff --git a/helm/dbrepo/templates/search-secret.yaml b/helm/dbrepo/templates/search-secret.yaml index 41665ac2bc1614653262f93cf28882a55638e4ec..251da00248f77afa295483890064ed6a3bc73211 100644 --- a/helm/dbrepo/templates/search-secret.yaml +++ b/helm/dbrepo/templates/search-secret.yaml @@ -13,7 +13,7 @@ stringData: AUTH_SERVICE_CLIENT: "{{ .Values.authservice.client.id }}" AUTH_SERVICE_CLIENT_SECRET: "{{ .Values.authservice.client.secret }}" AUTH_SERVICE_ENDPOINT: "{{ .Values.authservice.endpoint }}" - GATEWAY_SERVICE_ENDPOINT: "{{ .Values.gateway }}" + METADATA_SERVICE_ENDPOINT: "{{ .Values.metadataservice.endpoint }}" JWT_PUBKEY: "{{ .Values.authservice.jwt.pubkey }}" LOG_LEVEL: "{{ ternary "DEBUG" "INFO" .Values.searchservice.image.debug }}" OPENSEARCH_HOST: "{{ .Values.searchdb.host }}" diff --git a/helm/dbrepo/values.yaml b/helm/dbrepo/values.yaml index 2b18ed14222517593b1d9a31ec3bf67633fd2e91..af810436af63b6ca3b87a08c767a28959852ec35 100644 --- a/helm/dbrepo/values.yaml +++ b/helm/dbrepo/values.yaml @@ -56,8 +56,8 @@ metadatadb: enabled: false ## @skip metadatadb.initdbScriptsConfigMap The initial database scripts. initdbScriptsConfigMap: metadata-db-setup - ## @param metadatadb.initdbScripts Additional init.db scripts that are executed on the first start. - initdbScripts: { } + ## @param metadatadb.extraInitDbScripts Additional init.db scripts that are executed on the first start. + extraInitDbScripts: { } # 03-additional-data.sql: | # BEGIN; # INSERT INTO `mdb_containers` (name, internal_name, image_id, host, port, sidecar_host, sidecar_port, privileged_username, privileged_password) @@ -81,11 +81,6 @@ authservice: debug: false ## @param authservice.endpoint The hostname for the microservices. endpoint: http://auth-service - auth: - ## @param authservice.auth.adminUser The admin username. - adminUser: fda - ## @param authservice.auth.adminPassword The admin user password. - adminPassword: fda ## @skip authservice.postgresql postgresql: enabled: true @@ -222,8 +217,6 @@ searchdb: ## @skip searchdb.security security: enabled: false - adminUsername: admin - adminPassword: admin ## @param searchdb.clusterName The cluster name. clusterName: search-db @@ -422,7 +415,7 @@ metadataservice: ## @param metadataservice.podSecurityContext.fsGroup Set RabbitMQ pod's Security Context fsGroup fsGroup: 1001 containerSecurityContext: - ## @param metadataservice.containerSecurityContext.enabled Enabled containers' Security Context + ## @param metadataservice.containerSecurityContext.enabled Enable containers' Security Context enabled: true ## @param metadataservice.containerSecurityContext.seLinuxOptions Set SELinux options in container seLinuxOptions: { } @@ -812,14 +805,14 @@ ui: host: example.com port: ## @param ui.public.broker.port.5671 Enable display of the broker 5671 port and mark it as secure (SSL/TLS). - 5671: true + 5671: false ## @param ui.public.broker.port.5672 Enable display of the broker 5672 port and mark it as insecure (no SSL/TLS). - 5672: false + 5672: true ## @param ui.public.broker.extra Extra metadata displayed. extra: "" database: ## @param ui.public.database.extra Extra metadata displayed. - extra: "128.130.0.0/15" + extra: "" ## @skip ui.public.links links: rabbitmq: diff --git a/install.sh b/install.sh index a9fcf6d10d374f048d14289d20fc89d01ef82813..5e367f4d53fefc633fb3131dad7360569ea971ac 100644 --- a/install.sh +++ b/install.sh @@ -69,15 +69,15 @@ fi echo "[📦] Pulling images for version ${VERSION} ..." docker compose pull -echo "[✨] Starting DBRepo ..." -docker compose up -d - -if [ $? -eq 0 ]; then - echo "[🎉] Successfully started!" - echo "" - echo "You can now inspect the logs with:" - echo "" - echo " docker compose logs -f" - echo "" - echo "Read about next steps online: https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/${VERSION}/installation/#next-steps" -fi +echo "[🎉] Success!" +echo "" +echo "You can now:" +echo "" +echo " 1) Either start the deployment running on http://localhost, or" +echo " 2) Edit the BASE_URL variable in .env to set your hostname" +echo "" +echo "Then start the local deployment with:" +echo "" +echo " docker compose up -d" +echo "" +echo "Read about next steps online: https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/${VERSION}/installation/#next-steps" diff --git a/mkdocs.yml b/mkdocs.yml index 9bb4588f27727a8e7926caf585094989553957fa..5285953568a544a7589bb091e82c13138f6b84cb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,10 +47,12 @@ nav: - Air Quality Data: examples/air.md - COVID-19 Data: examples/covid-19.md - Hazard Data: examples/hazard.md + - Health Data: examples/health.md - Industry 4.0 Power Data: examples/power.md - Survey Data: examples/survey.md - Lute Data: examples/lute-data.md - Music-ML Data: examples/music.md + - Theater Data: examples/theater.md - Transportation Data: examples/transportation.md - XPS Data: examples/xps-data.md - publications.md