From 53e094520dcc7e746efd4fec5c6a846b4c4c6455 Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Fri, 13 Dec 2024 14:41:52 +0100
Subject: [PATCH] Hotfix the UI

---
 .../java/at/tuwien/mapper/DataMapper.java     |  16 ++-
 .../java/at/tuwien/mapper/MariaDbMapper.java  |   2 +-
 .../impl/SubsetServiceMariaDbImpl.java        |   2 -
 .../at/tuwien/endpoints/AccessEndpoint.java   |   9 +-
 dbrepo-ui/components/subset/Results.vue       |  47 ++++++-
 .../[database_id]/subset/[subset_id]/info.vue |   3 +-
 .../[database_id]/table/[table_id]/data.vue   | 125 +++---------------
 7 files changed, 79 insertions(+), 125 deletions(-)

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 ccb49288c5..3e569d2f12 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,7 +21,6 @@ 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;
 import org.apache.hadoop.shaded.com.google.common.hash.Hashing;
 import org.apache.hadoop.shaded.org.apache.commons.io.FileUtils;
@@ -199,17 +198,13 @@ public interface DataMapper {
         return view;
     }
 
-    default QueryDto resultSetToQueryDto(@NotNull ResultSet data) throws SQLException, QueryNotFoundException {
+    default QueryDto resultSetToQueryDto(@NotNull ResultSet data) throws SQLException {
         /* note that next() is called outside this mapping function */
-        return QueryDto.builder()
+        final QueryDto subset = QueryDto.builder()
                 .id(data.getLong(1))
                 .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))
                 .resultHash(data.getString(6))
@@ -219,6 +214,13 @@ public interface DataMapper {
                         .atZone(ZoneId.of("UTC"))
                         .toInstant())
                 .build();
+        if (data.getString(3) != null) {
+            subset.setCreator(UserDto.builder()
+                    .id(UUID.fromString(data.getString(3)))
+                    .build());
+            subset.setCreatedBy(UUID.fromString(data.getString(3)));
+        }
+        return subset;
     }
 
     default List<TableHistoryDto> resultSetToTableHistory(ResultSet resultSet) throws SQLException {
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 99480719fa..4e91dd7221 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
@@ -119,7 +119,7 @@ public interface MariaDbMapper {
     }
 
     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) WITH SYSTEM VERSIONING;";
+        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), `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) WITH SYSTEM VERSIONING;";
         log.trace("mapped create query store table statement: {}", statement);
         return statement;
     }
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 1e1e78603b..7e821af01c 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
@@ -180,8 +180,6 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs
             }
             final QueryDto query = dataMapper.resultSetToQueryDto(resultSet);
             query.setIdentifiers(metadataServiceGateway.getIdentifiers(database.getId(), queryId));
-            query.setCreator(database.getOwner());
-            query.setDatabaseId(database.getId());
             return query;
         } catch (SQLException e) {
             log.error("Failed to find query with id {}: {}", queryId, e.getMessage());
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
index 3b39857ee6..812ec7bc21 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
@@ -20,7 +20,6 @@ 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.validation.Valid;
-import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -92,7 +91,7 @@ public class AccessEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<DatabaseAccessDto> create(@NotNull @PathVariable("databaseId") Long databaseId,
-                                                    @org.hibernate.validator.constraints.UUID @PathVariable("userId") UUID userId,
+                                                    @PathVariable("userId") UUID userId,
                                                     @Valid @RequestBody UpdateDatabaseAccessDto data,
                                                     @NotNull Principal principal) throws NotAllowedException, DataServiceException,
             DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException,
@@ -155,7 +154,7 @@ public class AccessEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") Long databaseId,
-                                       @org.hibernate.validator.constraints.UUID @PathVariable("userId") UUID userId,
+                                       @PathVariable("userId") UUID userId,
                                        @Valid @RequestBody UpdateDatabaseAccessDto data,
                                        @NotNull Principal principal) throws NotAllowedException,
             DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException,
@@ -200,7 +199,7 @@ public class AccessEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<DatabaseAccessDto> find(@NotNull @PathVariable("databaseId") Long databaseId,
-                                                  @org.hibernate.validator.constraints.UUID @PathVariable("userId") UUID userId,
+                                                  @PathVariable("userId") UUID userId,
                                                   @NotNull Principal principal) throws DatabaseNotFoundException,
             UserNotFoundException, AccessNotFoundException, NotAllowedException {
         log.debug("endpoint get database access, databaseId={}, userId={}, principal.name={}", databaseId, userId,
@@ -257,7 +256,7 @@ public class AccessEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<Void> revoke(@NotNull @PathVariable("databaseId") Long databaseId,
-                                       @org.hibernate.validator.constraints.UUID @PathVariable("userId") UUID userId,
+                                       @PathVariable("userId") UUID userId,
                                        @NotNull Principal principal) throws NotAllowedException, DataServiceException,
             DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException,
             SearchServiceException, SearchServiceConnectionException {
diff --git a/dbrepo-ui/components/subset/Results.vue b/dbrepo-ui/components/subset/Results.vue
index ba063dcf5d..e9e3c0deb5 100644
--- a/dbrepo-ui/components/subset/Results.vue
+++ b/dbrepo-ui/components/subset/Results.vue
@@ -18,13 +18,15 @@ export default {
   props: {
     type: {
       type: String,
-      default: () => 'query' /* query or view */
+      default: () => 'query' /* query, view or table */
     },
     loading: {
       type: Boolean,
-      default: () => {
-        return false
-      }
+      default: () => false
+    },
+    timestamp: {
+      type: String,
+      default: () => new Date().toISOString()
     }
   },
   data () {
@@ -106,6 +108,25 @@ export default {
           .finally(() => {
             this.loadingExecute = false
           })
+      } else if (this.type === 'table') {
+        const tableService = useTableService()
+        tableService.getData(this.$route.params.database_id, id, (this.options.page - 1), this.options.itemsPerPage, this.timestamp)
+          .then((result) => {
+            this.mapResults(result)
+            this.id = id
+            this.loadingExecute = false
+          })
+          .catch(({code}) => {
+            this.loadingExecute = false
+            const toast = useToastInstance()
+            if (typeof code !== 'string') {
+              return
+            }
+            toast.error(this.$t(code))
+          })
+          .finally(() => {
+            this.loadingExecute = false
+          })
       } else {
         const viewService = useViewService()
         viewService.reExecuteData(this.$route.params.database_id, id, this.options.page - 1, this.options.itemsPerPage)
@@ -150,6 +171,24 @@ export default {
           .finally(() => {
             this.loadingCount = false
           })
+      } else if (this.type === 'table') {
+        const tableService = useTableService()
+        tableService.getCount(this.$route.params.database_id, id, this.timestamp)
+          .then((count) => {
+            this.total = count
+            this.loadingCount = false
+          })
+          .catch(({code}) => {
+            this.loadingCount = false
+            const toast = useToastInstance()
+            if (typeof code !== 'string') {
+              return
+            }
+            toast.error(this.$t(code))
+          })
+          .finally(() => {
+            this.loadingCount = false
+          })
       } else {
         const viewService = useViewService()
         viewService.reExecuteCount(this.$route.params.database_id, id)
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 b9ed74a042..764d1b55ff 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
@@ -30,7 +30,7 @@
             width="50%" />
         </v-list>
         <v-list
-          v-else
+          v-else-if="subset"
           lines="two"
           dense>
           <v-list-item
@@ -40,6 +40,7 @@
             {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }}
           </v-list-item>
           <v-list-item
+            v-if="subset.creator"
             :title="$t('pages.subset.creator.title')"
             density="compact">
             <UserBadge :user="subset.creator" :other-user="user" />
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 1be05e4bf1..1a29f918bf 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
@@ -55,36 +55,19 @@
     </v-toolbar>
     <TimeDrift />
     <v-card
-      elevation="0"
-      tile>
-      <v-card
-        v-if="error"
-        variant="flat">
-        <v-card-text>
-          {{ $t('error.table.connection') }}
-        </v-card-text>
-      </v-card>
-      <v-data-table-server
-        v-if="!error"
-        v-model="selection"
-        flat
-        :show-select="canModify"
-        return-object
-        :headers="headers"
-        :items="rows"
-        :items-length="total"
-        :loading="loadingData || loadingCount"
-        :options.sync="options"
-        :footer-props="footerProps"
-        :items-per-page-options="footerProps.itemsPerPageOptions"
-        @update:options="loadData">
-        <template
-          v-for="(blobColumn, idx) in blobColumns"
-          v-slot:[blobColumn]="{ item }">
-          <BlobDownload
-            :blob="item[blobColumn.substring(5)]" />
-        </template>
-      </v-data-table-server>
+      v-if="error"
+      variant="flat">
+      <v-card-text>
+        {{ $t('error.table.connection') }}
+      </v-card-text>
+    </v-card>
+    <v-card tile>
+      <QueryResults
+        id="query-results"
+        ref="queryResults"
+        type="table"
+        :timestamp="versionISO || lastReload.toISOString()"
+        class="mt-0 mb-0" />
     </v-card>
     <v-dialog
       v-model="pickVersionDialog"
@@ -122,14 +105,16 @@
 import TableHistory from '@/components/table/TableHistory.vue'
 import TimeDrift from '@/components/TimeDrift.vue'
 import TableToolbar from '@/components/table/TableToolbar.vue'
-import {formatTimestampUTC, formatDateUTC, formatTimestamp} from '@/utils'
+import { formatTimestamp } from '@/utils'
 import { useUserStore } from '@/stores/user'
 import { useCacheStore } from '@/stores/cache'
 import EditTuple from '@/components/dialogs/EditTuple.vue'
 import BlobDownload from '@/components/table/BlobDownload.vue'
+import QueryResults from '@/components/subset/Results.vue'
 
 export default {
   components: {
+    QueryResults,
     BlobDownload,
     EditTuple,
     TableHistory,
@@ -142,6 +127,7 @@ export default {
       loadingData: false,
       loadingCount: false,
       loadingDelete: false,
+      loadingTable: false,
       addTupleDialog: false,
       editTupleDialog: false,
       total: 0,
@@ -282,18 +268,11 @@ export default {
   },
   watch: {
     version () {
-      this.loadCount()
       this.reload()
-    },
-    table (newTable, oldTable) {
-      if (newTable !== oldTable && oldTable === null) {
-        this.loadProperties()
-      }
     }
   },
   mounted () {
-    this.loadProperties()
-    this.loadCount()
+    this.reload()
   },
   methods: {
     addTuple () {
@@ -402,74 +381,10 @@ export default {
       }
       this.pickVersionDialog = false
     },
-    loadProperties () {
-      if (!this.table || this.headers.length > 0) {
-        return
-      }
-      try {
-        this.headers = []
-        this.table.columns.map((c) => {
-          return {
-            value: c.internal_name,
-            title: c.internal_name,
-            sortable: false
-          }
-        }).forEach(header => this.headers.push(header))
-        this.dateColumns = this.table.columns.filter(c => (c.column_type === 'date' || c.column_type === 'timestamp'))
-        console.debug('date columns are', this.dateColumns)
-      } catch ({code}) {
-        const toast = useToastInstance()
-        toast.error(this.$t(code))
-      }
-      this.loading = false
-    },
     reload () {
       this.lastReload = new Date()
-      this.loadData({ page: this.options.page, itemsPerPage: this.options.itemsPerPage, sortBy: null})
-    },
-    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(({code}) => {
-          this.loadingCount = false
-          const toast = useToastInstance()
-          toast.error(this.$t(code))
-        })
-    },
-    loadData({ page, itemsPerPage, sortBy }) {
-      this.options.page = page
-      this.options.itemsPerPage = itemsPerPage
-      const tableService = useTableService()
-      this.loadingData = true
-      tableService.getData(this.$route.params.database_id, this.$route.params.table_id, (page - 1), itemsPerPage, (this.versionISO || this.lastReload.toISOString()))
-        .then((data) => {
-          this.rows = data.result.map((row) => {
-            for (const col in row) {
-              const column = this.table.columns.filter(c => c.internal_name === col)[0]
-              const columnDefinition = this.dateColumns.filter(c => c.internal_name === col)
-              if (columnDefinition.length > 0) {
-                if (columnDefinition[0].column_type === 'date') {
-                  row[col] = formatDateUTC(row[col])
-                } else if (columnDefinition[0].column_type === 'timestamp') {
-                  row[col] = formatTimestampUTC(row[col])
-                }
-              }
-            }
-            return row
-          })
-          this.loadingData = false
-        })
-        .catch(({code, message}) => {
-          this.error = true
-          this.loadingData = false
-          const toast = useToastInstance()
-          toast.error(this.$t(code) + ": " + message)
-        })
+      this.$refs.queryResults.reExecute(Number(this.$route.params.table_id))
+      this.$refs.queryResults.reExecuteCount(Number(this.$route.params.table_id))
     },
     isFileField (column) {
       return ['blob', 'longblob', 'mediumblob', 'tinyblob'].includes(column.column_type)
-- 
GitLab