From b5ca0db529a8921c1f88ad6cd09e2f30fe98c553 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Tue, 8 Feb 2022 14:50:44 +0100 Subject: [PATCH] Last commit, deploy to the public instance Former-commit-id: 67804dd934f65b796501e186b753a194ce075995 --- .../tuwien/endpoints/IdentifierEndpoint.java | 1 + .../tuwien/repository/jpa/UserRepository.java | 14 +++++ .../at/tuwien/service/IdentifierService.java | 6 ++- .../service/impl/IdentifierServiceImpl.java | 2 + .../at/tuwien/endpoint/TableDataEndpoint.java | 25 +-------- .../java/at/tuwien/mapper/QueryMapper.java | 2 +- fda-ui/components/dialogs/PersistQuery.vue | 28 +++++++++- fda-ui/components/dialogs/TimeTravel.vue | 31 ++++------- fda-ui/components/query/Builder.vue | 49 +++++++++++------ .../_database_id/query/_query_id/index.vue | 54 +++++++++++++------ .../_database_id/table/_table_id/index.vue | 44 ++++++++------- fda-ui/pages/container/index.vue | 21 ++++---- fda-ui/pages/pid/_pid_id/index.vue | 20 ++++++- 13 files changed, 190 insertions(+), 107 deletions(-) create mode 100644 fda-identifier-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java diff --git a/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java index 18f795121b..40858946ca 100644 --- a/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java +++ b/fda-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java @@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotNull; +import java.security.Principal; import java.util.List; import java.util.stream.Collectors; diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java b/fda-identifier-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java new file mode 100644 index 0000000000..08f813da41 --- /dev/null +++ b/fda-identifier-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java @@ -0,0 +1,14 @@ +package at.tuwien.repository.jpa; + +import at.tuwien.entities.user.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserRepository extends JpaRepository<User, Long> { + + Optional<User> findByUsername(String username); + + Optional<User> findByEmail(String email); + +} diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java index 7c0b73a5d7..4fd7bc0393 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java @@ -4,9 +4,11 @@ import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.identifier.VisibilityTypeDto; import at.tuwien.entities.identifier.Identifier; import at.tuwien.exception.*; +import org.bouncycastle.pqc.math.linearalgebra.PolynomialRingGF2; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.security.Principal; import java.util.List; @Service @@ -40,7 +42,9 @@ public interface IdentifierService { * @return The created identifier from the metadata database if successful. * @throws IdentifierPublishingNotAllowedException When the visibility is not self. */ - Identifier create(Long containerId, Long databaseId, IdentifierDto data) throws IdentifierPublishingNotAllowedException, QueryNotFoundException, RemoteUnavailableException, IdentifierAlreadyExistsException; + Identifier create(Long containerId, Long databaseId, IdentifierDto data) + throws IdentifierPublishingNotAllowedException, QueryNotFoundException, RemoteUnavailableException, + IdentifierAlreadyExistsException; /** * Finds an identifier by given id in the metadata database. diff --git a/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java index 3595be08de..b16e034374 100644 --- a/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java +++ b/fda-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java @@ -5,6 +5,7 @@ import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.identifier.VisibilityTypeDto; import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.identifier.VisibilityType; +import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.QueryServiceGateway; import at.tuwien.mapper.IdentifierMapper; @@ -17,6 +18,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.security.Principal; import java.util.List; import java.util.Optional; diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java index 121bc46c04..d322f13acd 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java @@ -79,7 +79,7 @@ public class TableDataEndpoint { .body(queryService.insert(id, databaseId, tableId, data)); } - @GetMapping + @RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD}) @Transactional(readOnly = true) @ApiOperation(value = "Get values", notes = "Get Data from a Table in the database.") @ApiResponses({ @@ -116,28 +116,5 @@ public class TableDataEndpoint { .body(response); } - @RequestMapping(method = RequestMethod.HEAD) - @Transactional(readOnly = true) - @ApiOperation(value = "Get values", notes = "Get Data Count from a Table in the database.") - @ApiResponses({ - @ApiResponse(code = 200, message = "Get data from the table."), - @ApiResponse(code = 401, message = "Not authorized to update tables."), - @ApiResponse(code = 404, message = "The table is not found in database."), - @ApiResponse(code = 405, message = "The connection to the database was unsuccessful."), - }) - public ResponseEntity<QueryResultDto> getCount(@NotNull @PathVariable("id") Long id, - @NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @PathVariable("tableId") Long tableId, - @RequestParam(required = false) Instant timestamp) - throws TableNotFoundException, DatabaseNotFoundException, ImageNotSupportedException, - TableMalformedException, ContainerNotFoundException { - final BigInteger count = queryService.count(id, databaseId, tableId, timestamp); - final HttpHeaders headers = new HttpHeaders(); - headers.set("FDA-COUNT", count.toString()); - return ResponseEntity.ok() - .headers(headers) - .build(); - } - } diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java index e6b3e69579..1efc7caacd 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java +++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java @@ -225,7 +225,7 @@ public interface QueryMapper { if (data == null) { return null; } - log.debug("map data {} to table column {}", data, column); + log.trace("map data {} to table column {}", data, column); switch (column.getColumnType()) { case BLOB: log.trace("mapping {} to blob", data); diff --git a/fda-ui/components/dialogs/PersistQuery.vue b/fda-ui/components/dialogs/PersistQuery.vue index 5bb38f294f..fb3b827801 100644 --- a/fda-ui/components/dialogs/PersistQuery.vue +++ b/fda-ui/components/dialogs/PersistQuery.vue @@ -93,9 +93,19 @@ export default { computed: { loadingColor () { return this.error ? 'red lighten-2' : 'primary' + }, + token () { + return this.$store.state.token + }, + headers () { + if (this.token === null) { + return null + } + return { Authorization: `Bearer ${this.token}` } } }, beforeMount () { + this.loadUser() }, methods: { cancel () { @@ -107,11 +117,12 @@ export default { }) }, async persist () { - console.debug('identifier data', this.identifier) this.loading = true let res try { - res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/identifier`, this.identifier) + res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/identifier`, this.identifier, { + headers: this.headers + }) console.debug('persist', res.data) } catch (err) { this.$toast.error('Failed to persist query') @@ -119,6 +130,19 @@ export default { } this.$toast.success('Query persisted.') this.$emit('close') + }, + async loadUser () { + this.loading = true + let res + try { + res = await this.$axios.put('/api/auth', null, { + headers: this.headers + }) + console.debug('user data', res.data) + } catch (err) { + this.$toast.error('Failed load user data') + console.error('load user data failed', err) + } } } } diff --git a/fda-ui/components/dialogs/TimeTravel.vue b/fda-ui/components/dialogs/TimeTravel.vue index 45258f63f9..49993afbe0 100644 --- a/fda-ui/components/dialogs/TimeTravel.vue +++ b/fda-ui/components/dialogs/TimeTravel.vue @@ -10,7 +10,7 @@ </v-card-subtitle> <v-card-text> <v-date-picker - v-model="picker" + v-model="date" no-title /> <v-time-picker v-model="time" @@ -33,7 +33,7 @@ <v-btn id="version" class="mb-2" - :disabled="version === null || version === undefined" + :disabled="date === null || time === null" color="primary" @click="pick"> Pick @@ -50,8 +50,8 @@ export default { formValid: false, loading: false, error: false, - version: null, - versions: [] + date: null, + time: null } }, computed: { @@ -59,9 +59,6 @@ export default { return this.error ? 'red lighten-2' : 'primary' } }, - mounted () { - this.loadVersions() - }, methods: { cancel () { this.$parent.$parent.$parent.$parent.pickVersionDialog = false @@ -72,25 +69,19 @@ export default { }) }, reset () { - this.$parent.$parent.$parent.$parent.version = { id: null, created: null } + this.$parent.$parent.$parent.$parent.version = null this.cancel() }, pick () { - this.$parent.$parent.$parent.$parent.version = this.versions[this.version] + this.$parent.$parent.$parent.$parent.version = this.formatDate() this.cancel() }, - async loadVersions () { - this.loading = true - try { - const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/version` - const res = await this.$axios.get(url) - this.versions = res.data - console.debug('versions', this.versions) - } catch (err) { - console.error('Failed to get versions', err) - this.$toast.error('Failed to get versions') + formatDate () { + if (this.date === null || this.time === null) { + return null } - this.loading = false + console.debug('selected date', this.date, 'time', this.time) + return Date.parse(this.date + ' ' + this.time) } } } diff --git a/fda-ui/components/query/Builder.vue b/fda-ui/components/query/Builder.vue index b75952bb7c..2577ed9979 100644 --- a/fda-ui/components/query/Builder.vue +++ b/fda-ui/components/query/Builder.vue @@ -8,10 +8,10 @@ <v-toolbar-title>Create Query</v-toolbar-title> <v-spacer /> <v-toolbar-title> - <v-btn :disabled="!valid" color="blue-grey white--text" @click="save"> + <v-btn :disabled="!valid || !token" color="blue-grey white--text" @click="save"> Save without execution </v-btn> - <v-btn :disabled="!valid" color="primary" @click="execute"> + <v-btn :disabled="!valid || !token" color="primary" @click="execute"> <v-icon left>mdi-run</v-icon> Execute </v-btn> @@ -115,6 +115,15 @@ export default { }, tableId () { return this.table.id + }, + token () { + return this.$store.state.token + }, + headers () { + if (this.token === null) { + return null + } + return { Authorization: `Bearer ${this.token}` } } }, watch: { @@ -125,18 +134,21 @@ export default { } } }, - async mounted () { - // XXX same as in TableList - try { - const res = await this.$axios.get( - `/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table`) - this.tables = res.data - console.debug('tables', this.tables) - } catch (err) { - this.$toast.error('Could not list table.') - } + beforeMount () { + this.loadTables() }, methods: { + async loadTables () { + try { + const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table`, { + headers: this.headers + }) + this.tables = res.data + console.debug('tables', this.tables) + } catch (err) { + this.$toast.error('Could not list table.') + } + }, async execute () { this.$refs.form.validate() this.loading = true @@ -149,7 +161,9 @@ export default { })] } console.debug('send data', data) - const res = await this.$axios.put(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table/${this.tableId}/query/execute`, data) + const res = await this.$axios.put(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/query/execute`, data, { + headers: this.headers + }) console.debug('query result', res) this.$toast.success('Successfully executed query') this.loading = false @@ -169,13 +183,14 @@ export default { const query = this.query.sql.replaceAll('`', '') this.loading = true try { - const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table/${this.tableId}/query/save`, { - statement: query + const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/query/save`, { statement: query }, { + headers: this.headers }) console.debug('query result', res) this.$toast.success('Successfully saved query') this.loading = false this.queryId = res.data.id + this.$router.push(`/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/query/${this.queryId}`) } catch (err) { console.error('query save', err) this.$toast.error('Could not save query') @@ -204,7 +219,9 @@ export default { async loadColumns () { const tableId = this.table.id try { - const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table/${tableId}`) + const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table/${tableId}`, { + headers: this.headers + }) this.tableDetails = res.data this.buildQuery() } catch (err) { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue b/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue index 6ce715e645..d99119cc37 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue @@ -4,23 +4,21 @@ <v-toolbar-title>{{ identifier.title }}</v-toolbar-title> <v-spacer /> <v-toolbar-title> - <v-btn color="blue-grey white--text" class="mr-2" :disabled="!query.execution || identifier.id" @click.stop="persistQueryDialog = true"> + <v-btn color="blue-grey white--text" class="mr-2" :disabled="!query.execution || identifier.id || !token" @click.stop="persistQueryDialog = true"> <v-icon left>mdi-fingerprint</v-icon> Persist </v-btn> - <v-btn color="primary" disabled> + <v-btn color="primary" :disabled="!token"> <v-icon left>mdi-run</v-icon> Re-Execute </v-btn> </v-toolbar-title> </v-toolbar> - <v-card flat> - <v-card-title v-if="!loading"> - <span v-if="query.execution != null"> - sha256:{{ query.query_hash }} - </span> + <v-card v-if="!loading" flat> + <v-card-title> + Query Information </v-card-title> - <v-card-subtitle v-if="!loading"> - <span v-if="query.execution != null"> - Executed {{ formatDate(query.execution) }} + <v-card-subtitle> + <span v-if="query.created != null"> + Created {{ formatDate(query.created) }} </span> <span v-if="query.execution == null"> Query was never executed @@ -31,9 +29,8 @@ <strong>Query</strong> </p> <div> - <p>Persistent Identifier</p> - <p v-if="identifier.id"> - <code>https://dbrepo.ossdip.at/pid/{{ identifier.id }}</code> + <p> + Persistent Identifier: <code v-if="identifier.id">https://dbrepo.ossdip.at/pid/{{ identifier.id }}</code><span v-if="!identifier.id">(empty)</span> </p> <p>Statement</p> <v-alert @@ -58,13 +55,23 @@ <strong>Result</strong> </p> <p> - Hash: <code>{{ query.result_hash }}</code> + Hash: <code v-if="query.result_hash">{{ query.result_hash }}</code><span v-if="!query.result_hash">(empty)</span> + </p> + <p> + Rows: <code v-if="query.result_number">{{ query.result_number }}</code><span v-if="!query.result_number">(empty)</span> + </p> + <p> + Executed: <code v-if="query.execution">{{ query.execution }}</code><span v-if="!query.execution">(empty)</span> + </p> + <p class="mt-2"> + <strong>Creator</strong> </p> <p> - Rows: <code>{{ query.result_number }}</code> + Username: <code v-if="query.username">{{ query.username }}</code><span v-if="!query.username">(empty)</span> </p> </v-card-text> </v-card> + <v-breadcrumbs :items="items" class="pa-0 mt-2" /> <v-dialog v-model="persistQueryDialog" persistent @@ -84,6 +91,12 @@ export default { }, data () { return { + items: [ + { text: 'Databases', href: '/container' }, + { text: `${this.$route.params.database_id}`, href: `/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}` }, + { text: 'Queries', href: `/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/query` }, + { text: `${this.$route.params.query_id}`, href: `/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/query/${this.$route.params.query_id}` } + ], query: { id: this.$route.params.query_id, database_id: null, @@ -109,6 +122,17 @@ export default { loading: true } }, + computed: { + token () { + return this.$store.state.token + }, + headers () { + if (this.token === null) { + return null + } + return { Authorization: `Bearer ${this.token}` } + } + }, mounted () { this.loadMetadata() }, diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue index 25c5f8223d..dca090de21 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/index.vue @@ -18,7 +18,7 @@ <v-toolbar :color="versionColor" flat> <v-toolbar-title> <strong>Versioning</strong> - <span v-if="version.id !== null">{{ version.created }}</span> + <span v-if="version !== null">{{ versionFormatted }}</span> </v-toolbar-title> <v-spacer /> <v-toolbar-title> @@ -58,6 +58,7 @@ </template> <script> import TimeTravel from '@/components/dialogs/TimeTravel' +import { format } from 'date-fns' export default { components: { @@ -73,10 +74,7 @@ export default { dateMenu: false, timeMenu: false, pickVersionDialog: null, - version: { - id: null, - created: null - }, + version: null, options: { page: 1, itemsPerPage: 10 @@ -101,10 +99,16 @@ export default { }, versionColor () { console.debug('version', this.version) - if (this.version.created === null) { + if (this.version === null) { return 'grey lighten-1' } return 'primary white--text' + }, + versionFormatted () { + if (this.version === null) { + return null + } + return this.formatDate(this.version) } }, watch: { @@ -139,9 +143,9 @@ export default { try { this.loading = true let url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data?page=${this.options.page - 1}&size=${this.options.itemsPerPage}` - if (this.version.created !== null) { + if (this.version !== null) { console.info('versioning active', this.version) - url += `×tamp=${this.version.created}` + url += `×tamp=${new Date(this.version).toISOString()}` } const res = await this.$axios.get(url) console.debug('version', this.datetime, 'table data', res.data) @@ -152,16 +156,17 @@ export default { } this.loading = false }, - async loadDataCount () { - try { - this.loading = true - const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data` - const res = await this.$axios.head(url) - console.debug('data count', res.data) - this.total = res.data.count - } catch (err) { - console.error('failed to load total count', err) - } + loadDataCount () { // TODO + // try { + // this.loading = true + // const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data` + // const res = await this.$axios.head(url) + // console.debug('data count', res.data) + // this.total = res.data.count + // } catch (err) { + // console.error('failed to load total count', err) + // } + this.total = 1000000 this.loading = false }, columnAddition (column) { @@ -172,6 +177,9 @@ export default { return '† ' } return '' + }, + formatDate (d) { + return format(new Date(d), 'dd.MM.yyyy HH:mm') } } } diff --git a/fda-ui/pages/container/index.vue b/fda-ui/pages/container/index.vue index ed25283940..ac075e9271 100644 --- a/fda-ui/pages/container/index.vue +++ b/fda-ui/pages/container/index.vue @@ -18,8 +18,8 @@ <thead> <tr> <th>Name</th> - <th>Description</th> <th>Engine</th> + <th>Tables</th> <th>Created</th> </tr> </thead> @@ -31,15 +31,12 @@ </tr> <tr v-for="item in databases" - :key="item.id"> - <td> - <v-btn :to="`/container/${item.container_id}/database/${item.id}/info`" icon> - <v-icon>{{ iconSelect }}</v-icon> - </v-btn> - {{ item.name }} - </td> - <td>{{ item.description }}</td> + :key="item.id" + class="database" + @click="loadDatabase(item)"> + <td>{{ item.name }}</td> <td>{{ item.engine }}</td> + <td></td> <td>{{ formatDate(item.created) }}</td> </tr> </tbody> @@ -107,6 +104,9 @@ export default { this.error = true } }, + loadDatabase (database) { + this.$router.push(`/container/${database.container_id}/database/${database.id}/info`) + }, trim (s) { return s.slice(0, 12) }, @@ -131,6 +131,9 @@ export default { overflow: hidden; text-overflow: ellipsis; } + .database:hover { + cursor: pointer; + } .color-grey { color: #aaa; } diff --git a/fda-ui/pages/pid/_pid_id/index.vue b/fda-ui/pages/pid/_pid_id/index.vue index 20d0aa7208..24c62a04fa 100644 --- a/fda-ui/pages/pid/_pid_id/index.vue +++ b/fda-ui/pages/pid/_pid_id/index.vue @@ -1,5 +1,18 @@ <template> - <div /> + <div> + <v-card> + <v-card-title>PID Not Found</v-card-title> + <v-card-subtitle>{{ pid }}</v-card-subtitle> + <v-card-text> + <p>This PID cannot be found in the system. Possible reasons are:</p> + <ul> + <li>The PID is incorrect in your source.</li> + <li>The PID was copied incorrectly.</li> + <li>The PID has not been activated yet.</li> + </ul> + </v-card-text> + </v-card> + </div> </template> <script> @@ -7,6 +20,11 @@ export default { mounted () { this.findPid() }, + computed: { + pid () { + return this.$route.params.pid_id + } + }, methods: { async findPid () { try { -- GitLab