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

Updated frontend to include publication, fixed the table import

Former-commit-id: 97beba2e
parent 0a55235a
Branches
Tags
1 merge request!42Fixed the query service tests
Showing
with 106 additions and 42 deletions
......@@ -15,8 +15,6 @@ COPY ./rest-service ./rest-service
COPY ./services ./services
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -15,8 +15,6 @@ COPY ./rest-service ./rest-service
COPY ./services ./services
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -15,8 +15,6 @@ COPY ./rest-service ./rest-service
COPY ./services ./services
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -15,8 +15,6 @@ COPY ./rest-service ./rest-service
COPY ./services ./services
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -9,8 +9,6 @@ RUN mvn -fn -B dependency:go-offline > /dev/null
COPY ./discovery ./discovery
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -9,8 +9,6 @@ RUN mvn -fn -B dependency:go-offline > /dev/null
COPY ./gateway ./gateway
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -9,8 +9,6 @@ RUN mvn -fn -B dependency:go-offline > /dev/null
COPY ./api ./api
COPY ./entities ./entities
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -3,6 +3,7 @@ package at.tuwien.api.database.query;
import at.tuwien.api.database.deposit.files.FileDto;
import at.tuwien.api.database.table.TableDto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import javax.persistence.Column;
......@@ -29,11 +30,18 @@ public class QueryDto {
private Instant executionTimestamp;
@NotBlank
@ApiModelProperty(name = "query raw", example = "select * from table")
private String query;
@NotBlank
@ApiModelProperty(name = "query title", example = "Select all weather events for 2012")
private String title;
@NotBlank
@ApiModelProperty(name = "query description", example = "Returns a list of measurements for the year 2012")
private String description;
@ApiModelProperty(name = "doi", example = "Digital Object Identifier")
private String doi;
@JsonProperty("deposit_id")
......@@ -44,6 +52,7 @@ public class QueryDto {
private TableDto table;
@JsonProperty("query_normalized")
@ApiModelProperty(name = "query normalized", example = "select id, name from table")
private String queryNormalized;
@JsonProperty("query_hash")
......
......@@ -46,6 +46,9 @@ public class Query {
@Column
private String query;
@Column
private String description;
@Column
private String queryNormalized;
......
......@@ -203,6 +203,7 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E
title character varying(255) NULL,
doi character varying(255),
query TEXT NULL,
description TEXT NULL,
query_normalized TEXT NULL,
query_hash character varying(255) NULL,
result_hash character varying(255) NULL,
......
......@@ -15,8 +15,6 @@ COPY ./rest-service ./rest-service
COPY ./services ./services
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -11,6 +11,7 @@ import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
......
......@@ -2,6 +2,7 @@ package at.tuwien.service;
import at.tuwien.entities.database.Database;
import at.tuwien.entities.database.table.Table;
import at.tuwien.exception.ImageNotSupportedException;
import at.tuwien.mapper.ImageMapper;
import lombok.extern.log4j.Log4j2;
......@@ -11,8 +12,12 @@ import org.jooq.*;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigInteger;
import java.sql.*;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.sequence;
@Log4j2
public abstract class JdbcConnector {
......@@ -25,9 +30,23 @@ public abstract class JdbcConnector {
protected DSLContext open(Database database) throws SQLException, ImageNotSupportedException {
final String url = "jdbc:" + database.getContainer().getImage().getJdbcMethod() + "://" + database.getContainer().getInternalName() + "/" + database.getInternalName();
log.trace("Attempt to connect to '{}'", url);
final Connection connection = DriverManager.getConnection(url, imageMapper.containerImageToProperties(database.getContainer().getImage()));
return DSL.using(connection, SQLDialect.valueOf(database.getContainer().getImage().getDialect()));
}
/**
* Retrieve the next id from the sequence of a database
*
* @param database The database.
* @return The next id.
* @throws SQLException
* @throws ImageNotSupportedException
*/
protected BigInteger nextSequence(Database database) throws SQLException, ImageNotSupportedException {
final DSLContext context = open(database);
final Field<BigInteger> id = sequence(name(QueryStoreService.QUERYSTORE_SEQ_NAME))
.nextval();
return context.select(id).fetchOne(id);
}
}
......@@ -19,13 +19,16 @@ import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.commons.codec.digest.DigestUtils;
import org.jooq.*;
import org.jooq.exception.DataAccessException;
import org.junit.jupiter.params.shadow.com.univocity.parsers.common.DataProcessingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.StringReader;
import java.math.BigInteger;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
......@@ -43,7 +46,8 @@ import static org.jooq.impl.SQLDataType.*;
@Service
public class QueryStoreService extends JdbcConnector {
private final static String QUERYSTORENAME = "mdb_querystore";
public final static String QUERYSTORE_NAME = "userdb_querystore";
public final static String QUERYSTORE_SEQ_NAME = "seq_querystore_id";
private final DatabaseRepository databaseRepository;
private final QueryRepository queryRepository;
......@@ -75,7 +79,7 @@ public class QueryStoreService extends JdbcConnector {
.fetch()
.toString());
return queryMapper.recordListToQueryList(context
.selectFrom(QUERYSTORENAME)
.selectFrom(QUERYSTORE_NAME)
.orderBy(field("execution_timestamp"))
.fetch());
}
......@@ -93,9 +97,16 @@ public class QueryStoreService extends JdbcConnector {
log.trace("select query {}", context.selectQuery()
.fetch()
.toString());
final List<org.jooq.Record> records = context
.selectFrom(QUERYSTORENAME).where(condition("id = " + queryId))
List<org.jooq.Record> records;
try {
records = context
.selectFrom(QUERYSTORE_NAME)
.where(condition("id = " + queryId))
.fetch();
} catch (DataAccessException e) {
log.error("Failed to select records: {}", e.getMessage());
throw new QueryStoreException("Failed to select records", e);
}
if (records.size() != 1) {
throw new QueryStoreException("Failed to get query from querystore");
}
......@@ -123,9 +134,9 @@ public class QueryStoreService extends JdbcConnector {
/* create database in container */
try {
final DSLContext context = open(database);
context.createSequence("seq_id")
context.createSequence(QUERYSTORE_SEQ_NAME)
.execute();
context.createTable(QUERYSTORENAME)
context.createTable(QUERYSTORE_NAME)
.column("id", BIGINT)
.column("doi", VARCHAR(255).nullable(false))
.column("title", VARCHAR(255).nullable(false))
......@@ -156,7 +167,8 @@ public class QueryStoreService extends JdbcConnector {
query.setResultNumber(0L);
try {
final DSLContext context = open(database);
int success = context.insertInto(table(QUERYSTORENAME))
final BigInteger idVal = nextSequence(database);
int success = context.insertInto(table(QUERYSTORE_NAME))
.columns(field("id"),
field("doi"),
field("title"),
......@@ -165,9 +177,9 @@ public class QueryStoreService extends JdbcConnector {
field("execution_timestamp"),
field("result_hash"),
field("result_number"))
.values(sequence("seq_id").nextval(), "doi/" + query.getId(), query.getTitle(), query.getQuery(),
.values(idVal, "doi/" + idVal, query.getTitle(), query.getQuery(),
query.getQueryHash(), LocalDateTime.ofInstant(query.getExecutionTimestamp(),
ZoneId.of("Europe/Vienna")), "" + queryResult.hashCode(),
ZoneId.of("Europe/Vienna")), getResultHash(queryResult),
queryResult.getResult().size())
.execute();
log.info("Saved query into query store id {}", query.getId());
......@@ -188,6 +200,16 @@ public class QueryStoreService extends JdbcConnector {
return query;
}
/**
* Retrieve the result hash
*
* @param result The result.
* @return The hash.
*/
private String getResultHash(QueryResultDto result) {
return "sha256:" + DigestUtils.sha256Hex(result.getResult().toString());
}
@Deprecated
private boolean checkValidity(String query) {
String queryparts[] = query.toLowerCase().split("from");
......@@ -449,7 +471,7 @@ public class QueryStoreService extends JdbcConnector {
}
return context.select(count())
.from("information_schema.tables")
.where("table_name like '" + QUERYSTORENAME + "'")
.where("table_name like '" + QUERYSTORE_NAME + "'")
.fetchOne(0, int.class) == 1;
}
......
......@@ -15,8 +15,6 @@ COPY ./rest-service ./rest-service
COPY ./services ./services
COPY ./report ./report
ARG CI_JOB_STAGE
# Make sure it compiles
RUN mvn -q clean package -DskipTests > /dev/null
......
......@@ -29,6 +29,17 @@
required />
</v-col>
</v-row>
<v-row class="mt-2">
<v-col cols="6">
<v-textarea
v-model="description"
:rules="[rules.required]"
rows="3"
class="pa-0"
label="Query Description"
required />
</v-col>
</v-row>
<v-row class="mt-2">
<v-col cols="6">
<v-select
......@@ -95,6 +106,7 @@ export default {
table: null,
tables: [],
title: null,
description: null,
tableDetails: null,
queryId: null,
query: {
......@@ -162,7 +174,7 @@ export default {
this.loading = false
this.queryId = res.data.id
this.result.headers = this.select.map((s) => {
return { text: s.name, value: 'mdb_' + s.name, sortable: false }
return { text: s.name, value: s.name, sortable: false }
})
this.result.rows = res.data.result
} catch (err) {
......@@ -196,7 +208,7 @@ export default {
const url = '/server-middleware/query/build'
const data = {
table: this.table.internalName,
select: this.select.map(s => 'mdb_' + s.name),
select: this.select.map(s => s.name),
clauses: this.clauses
}
try {
......
......@@ -21,9 +21,9 @@
<v-toolbar-title v-text="title" />
<v-spacer />
<v-btn
class="mr-2 white--text"
color="blue-grey"
@click="authenticate"
class="mr-2 white--text">
@click="authenticate">
<v-icon left>mdi-login</v-icon> Login
</v-btn>
<v-menu bottom offset-y left>
......@@ -50,7 +50,7 @@
<nuxt />
</v-container>
</v-main>
<v-footer padless v-if="sandbox">
<v-footer v-if="sandbox" padless>
<v-card
flat
tile
......
......@@ -11,6 +11,7 @@
</v-card-text>
</v-card>
</v-tabs-items>
<v-breadcrumbs :items="items" class="pa-0 mt-2" />
<v-dialog v-model="dialogDelete" max-width="500">
<v-card>
<v-card-title class="headline">
......@@ -45,7 +46,11 @@ export default {
data () {
return {
dialogDelete: false,
confirm: null
confirm: null,
items: [
{ text: 'Databases', href: '/databases' },
{ text: `${this.$route.params.database_id}`, href: `/databases/${this.$route.params.database_id}/info` }
]
}
},
computed: {
......
<template>
<div v-if="db">
<DBToolbar />
<v-tabs-items v-model="tab">
<v-tabs-items v-if="!loading" v-model="tab">
<v-tab-item>
<v-card flat>
<v-card-title>
......@@ -16,17 +16,19 @@
<p>{{ description }}</p>
</blockquote>
<span>
Created {{ db.created }}
Created {{ formatDate(db.created) }}
</span>
</v-card-text>
</v-card>
</v-tab-item>
</v-tabs-items>
<v-breadcrumbs :items="items" class="pa-0 mt-2" />
</div>
</template>
<script>
import DBToolbar from '@/components/DBToolbar'
import { format } from 'date-fns'
export default {
components: {
......@@ -34,7 +36,11 @@ export default {
},
data () {
return {
loading: false
loading: false,
items: [
{ text: 'Databases', href: '/databases' },
{ text: `${this.$route.params.database_id}`, href: `/databases/${this.$route.params.database_id}/info` }
]
}
},
computed: {
......@@ -56,11 +62,12 @@ export default {
},
methods: {
async init () {
if (this.db != null) {
this.loading = true
if (this.db != null && this.db.id === this.$route.params.database_id) {
this.loading = false
return
}
try {
this.loading = true
const res = await this.$axios.get(`/api/database/${this.$route.params.database_id}`)
console.debug('database', res.data)
this.$store.commit('SET_DATABASE', res.data)
......@@ -69,6 +76,9 @@ export default {
this.$toast.error('Could not load database.')
this.loading = false
}
},
formatDate (d) {
return format(new Date(d), 'dd.MM.yyyy HH:mm:ss.SSS')
}
}
}
......
......@@ -11,7 +11,7 @@ export default {
return {
items: [
{ text: 'Databases', href: '/databases' },
{ text: `${this.$route.params.database_id}`, href: `/databases/${this.$route.params.database_id}` },
{ text: `${this.$route.params.database_id}`, href: `/databases/${this.$route.params.database_id}/info` },
{ text: 'Tables', href: `/databases/${this.$route.params.database_id}/tables` }
]
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment