Skip to content
Snippets Groups Projects
Commit b5ca0db5 authored by Martin Weise's avatar Martin Weise
Browse files

Last commit, deploy to the public instance

Former-commit-id: 67804dd9
parent 049e360b
No related branches found
No related tags found
1 merge request!42Fixed the query service tests
Showing
with 190 additions and 107 deletions
......@@ -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;
......
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);
}
......@@ -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.
......
......@@ -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;
......
......@@ -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();
}
}
......@@ -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);
......
......@@ -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)
}
}
}
}
......
......@@ -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)
}
}
}
......
......@@ -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
beforeMount () {
this.loadTables()
},
methods: {
async loadTables () {
try {
const res = await this.$axios.get(
`/api/container/${this.$route.params.container_id}/database/${this.databaseId}/table`)
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.')
}
},
methods: {
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) {
......
......@@ -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()
},
......
......@@ -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 += `&timestamp=${this.version.created}`
url += `&timestamp=${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')
}
}
}
......
......@@ -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;
}
......
<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 {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment