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

Moved text/csv and text/xml

parent 7f2d2a8d
No related branches found
No related tags found
2 merge requests!106Dev,!101Resolve "Accept xml and csv for pid"
Showing
with 86 additions and 71 deletions
......@@ -59,6 +59,7 @@ public class IdentifierEndpoint {
}
@GetMapping("/{id}")
@Deprecated
@Transactional(readOnly = true)
@Timed(value = "identifier.export", description = "Time needed to export an identifier")
@Operation(summary = "Export some identifier metadata")
......
package at.tuwien.endpoints;
import at.tuwien.ExportResource;
import at.tuwien.config.EndpointConfig;
import at.tuwien.entities.identifier.Identifier;
import at.tuwien.exception.IdentifierNotFoundException;
......@@ -43,7 +44,7 @@ public class PersistenceEndpoint {
@Operation(summary = "Find some identifier")
public ResponseEntity<?> find(@Valid @PathVariable("pid") Long pid,
@RequestHeader(HttpHeaders.ACCEPT) String accept) throws IdentifierNotFoundException,
QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException {
QueryNotFoundException, RemoteUnavailableException {
log.debug("find identifier endpoint, pid={}, accept={}", pid, accept);
final Identifier identifier = identifierService.find(pid);
log.info("Found persistent identifier with id {}", identifier.getId());
......@@ -56,6 +57,13 @@ public class PersistenceEndpoint {
} else if (accept.equals("text/csv")) {
log.trace("accept header matches csv");
return ResponseEntity.ok(identifierService.exportResource(pid));
} else if (accept.equals("text/xml")) {
final HttpHeaders headers = new HttpHeaders();
final ExportResource resource = identifierService.exportMetadata(pid);
headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
return ResponseEntity.ok()
.headers(headers)
.body(resource.getResource());
}
}
log.trace("no accept header present, serving http redirect");
......
......@@ -25,6 +25,16 @@ public interface QueryServiceGateway {
QueryDto find(Long containerId, Long databaseId, IdentifierCreateDto identifier, String authorization)
throws QueryNotFoundException, RemoteUnavailableException;
ExportDto export(Long containerId, Long databaseId, Long queryId) throws RemoteUnavailableException,
/**
* Exports a query by given id.
*
* @param containerId The container id.
* @param databaseId The database id.
* @param queryId The query id.
* @return The exported resource as bytes.
* @throws RemoteUnavailableException The remote service is not available.
* @throws QueryNotFoundException The query was not found.
*/
byte[] export(Long containerId, Long databaseId, Long queryId) throws RemoteUnavailableException,
QueryNotFoundException;
}
......@@ -57,13 +57,15 @@ public class QueryServiceGatewayImpl implements QueryServiceGateway {
}
@Override
public ExportDto export(Long containerId, Long databaseId, Long queryId)
public byte[] export(Long containerId, Long databaseId, Long queryId)
throws RemoteUnavailableException, QueryNotFoundException {
final String url = "/api/container/" + containerId + "/database/" + databaseId + "/query/" + queryId + "/export";
final ResponseEntity<ExportDto> response;
final HttpHeaders headers = new HttpHeaders();
headers.add("Accept", "text/csv");
final ResponseEntity<byte[]> response;
try {
log.trace("call gateway path {}", url);
response = restTemplate.exchange(url, HttpMethod.GET, null, ExportDto.class);
response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null, headers), byte[].class);
} catch (HttpServerErrorException.ServiceUnavailable e) {
log.error("Query service not available: {}", e.getMessage());
throw new RemoteUnavailableException("Query service not available", e);
......
......@@ -79,13 +79,12 @@ public interface IdentifierService {
*
* @param identifierId The identifier id.
* @return The XML resource, if successful.
* @throws IdentifierNotFoundException
* @throws QueryNotFoundException
* @throws IdentifierNotFoundException The identifier was not found in the metadata database or was deleted.
* @throws QueryNotFoundException The query was not found in the metadata database or was deleted.
* @throws RemoteUnavailableException
* @throws IdentifierRequestException
*/
InputStreamResource exportResource(Long identifierId)
throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException, IdentifierRequestException;
throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException;
/**
* Updated the metadata (only) on the identifier for a given id in the metadata database.
......
......@@ -25,6 +25,7 @@ import org.springframework.core.io.InputStreamResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.security.Principal;
......@@ -184,8 +185,7 @@ public class IdentifierServiceImpl implements IdentifierService {
@Override
@Transactional(readOnly = true)
public InputStreamResource exportResource(Long identifierId)
throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException,
IdentifierRequestException {
throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException {
/* check */
final Identifier identifier = find(identifierId);
if (identifier.getType().equals(IdentifierType.DATABASE)) {
......@@ -194,15 +194,9 @@ public class IdentifierServiceImpl implements IdentifierService {
throw new IdentifierNotFoundException("Failed to find identifier");
}
/* export */
final ExportDto export = queryServiceGateway.export(identifier.getContainerId(),
final byte[] file = queryServiceGateway.export(identifier.getContainerId(),
identifier.getDatabaseId(), identifier.getQueryId());
final InputStreamResource resource;
try {
resource = new InputStreamResource(FileUtils.openInputStream(new File("/tmp/" + export.getLocation())));
} catch (IOException e) {
log.error("Failed to open export file: {}", e.getMessage());
throw new IdentifierRequestException("Failed to open export file", e);
}
final InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(file));
log.trace("found resource {}", resource);
return resource;
}
......
......@@ -112,13 +112,13 @@ public class QueryEndpoint extends AbstractEndpoint {
public ResponseEntity<?> export(@NotNull @PathVariable("id") Long containerId,
@NotNull @PathVariable("databaseId") Long databaseId,
@NotNull @PathVariable("queryId") Long queryId,
@RequestParam(value = "download", required = false) String download,
@RequestHeader(HttpHeaders.ACCEPT) String accept,
Principal principal)
throws QueryStoreException, QueryNotFoundException, DatabaseNotFoundException, ImageNotSupportedException,
ContainerNotFoundException, TableMalformedException, FileStorageException, NotAllowedException,
QueryMalformedException, DatabaseConnectionException {
log.debug("endpoint export query, containerId={}, databaseId={}, queryId={}, download={}, principal={}",
containerId, databaseId, queryId, download, principal);
log.debug("endpoint export query, containerId={}, databaseId={}, queryId={}, accept={}, principal={}",
containerId, databaseId, queryId, accept, principal);
if (!hasQueryPermission(containerId, databaseId, queryId, "QUERY_EXPORT", principal)) {
log.error("Missing export query permission");
throw new NotAllowedException("Missing export query permission");
......@@ -127,7 +127,7 @@ public class QueryEndpoint extends AbstractEndpoint {
final Query query = storeService.findOne(containerId, databaseId, queryId);
log.trace("querystore returned query {}", query);
final ExportResource resource = queryService.findOne(containerId, databaseId, queryId);
if (download != null) {
if (accept.equals("text/csv")) {
final HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
log.trace("export query resulted in resource {}", resource);
......@@ -135,12 +135,9 @@ public class QueryEndpoint extends AbstractEndpoint {
.headers(headers)
.body(resource.getResource());
}
final ExportDto dto = ExportDto.builder()
.location(resource.getFilename())
log.error("Failed to export, non-csv exports are not supported");
return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED)
.build();
log.trace("export query resulted in export file {}", dto);
return ResponseEntity.ok()
.body(dto);
}
}
......@@ -61,6 +61,7 @@ public class TableDataEndpoint extends AbstractEndpoint {
}
@PutMapping
@Deprecated
@Transactional
@Timed(value = "data.update", description = "Time needed to update data in a table")
@Operation(summary = "Update data", security = @SecurityRequirement(name = "bearerAuth"))
......
......@@ -323,6 +323,16 @@ public interface QueryMapper {
.append("`");
idx[0]++;
});
statement.append(" UNION ALL SELECT ");
int[] jdx = new int[]{0};
table.getColumns()
.forEach(column -> {
statement.append(jdx[0] != 0 ? "," : "")
.append("`")
.append(column.getInternalName())
.append("`");
jdx[0]++;
});
statement.append("FROM `")
.append(table.getInternalName())
.append("`");
......
......@@ -96,6 +96,7 @@
disable-sort
:loading="loadingDetails"
hide-default-footer
items-per-page="-1"
:headers="headers"
:items="tableDetails.columns">
<template v-slot:item.is_null_allowed="{ item }">
......
......@@ -97,6 +97,15 @@
</v-row>
</v-tab-item>
<v-tab-item>
<v-row>
<v-col>
<v-alert
border="left"
color="info">
Currently, comments in the query (e.g. <code>-- Comment</code>) are not supported!
</v-alert>
</v-col>
</v-row>
<v-row>
<v-col>
<QueryRaw
......@@ -165,9 +174,6 @@ export default {
columnNames () {
return this.selectItems && this.selectItems.map(s => s.internal_name)
},
defaultRawSqlText () {
return '-- MariaDB 10.5 Query'
},
tableId () {
return this.table.id
},
......@@ -198,10 +204,7 @@ export default {
return null
},
canExecute () {
if (!this.sql || this.sql.length === 0) {
return false
}
return this.sql.trim() !== this.defaultRawSqlText
return !(!this.sql || this.sql.length === 0)
},
backTo () {
return `/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/` + (this.isView ? 'view' : 'query')
......@@ -293,13 +296,8 @@ export default {
this.resultId = res.data.id
console.debug('view', res.data)
} catch (err) {
if (err.response.status === 423) {
console.error('View name exist', err)
this.$toast.error('View name already exists')
return
}
console.error('Failed to create view', err)
this.$toast.error('Failed to create view ' + err.response.text)
this.$toast.error(err.response.data.message)
}
this.loadingQuery = false
await this.$refs.queryResults.reExecute(this.resultId)
......
......@@ -29,7 +29,7 @@ export default {
},
data () {
return {
content: this.value || '-- MariaDB 10.5 Query\n'
content: this.value
}
},
computed: {
......
......@@ -18,7 +18,7 @@
<v-btn v-if="token && query.is_persisted && !identifier.id && !loadingIdentifier && is_owner" class="mb-1 mr-2" color="primary" :disabled="error || erroneous || !executionUTC" @click.stop="openDialog()">
<v-icon left>mdi-content-save-outline</v-icon> Get PID
</v-btn>
<v-btn v-if="result_visibility" class="mb-1" :loading="downloadLoading" @click.stop="download">
<v-btn v-if="result_visibility" class="mb-1" :loading="downloadLoading" @click.stop="download('text/csv')">
<v-icon left>mdi-download</v-icon> Data .csv
</v-btn>
<v-btn
......@@ -26,7 +26,7 @@
color="secondary"
class="ml-2"
:loading="metadataLoading"
@click.stop="metadata">
@click.stop="download('text/xml')">
<v-icon left>mdi-code-tags</v-icon> Metadata .xml
</v-btn>
</v-toolbar-title>
......@@ -39,7 +39,7 @@
<v-list dense>
<v-list-item>
<v-list-item-icon>
<v-icon :color="database_visibility ? 'success' : 'error'">mdi-database-outline</v-icon>
<v-icon v-if="database_visibility" :color="database_visibility ? 'success' : 'error'">mdi-database-outline</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
......@@ -187,7 +187,7 @@
</v-list-item>
<v-list-item>
<v-list-item-icon>
<v-icon :color="result_visibility_icon ? 'success' : 'error'">{{ result_icon }}</v-icon>
<v-icon v-if="result_visibility_icon" :color="result_visibility_icon ? 'success' : 'error'">{{ result_icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content v-if="!erroneous">
<v-list-item-title>
......@@ -365,7 +365,7 @@ export default {
return this.$store.state.user && this.$store.state.user.username
},
database_visibility () {
return this.database.is_public
return this.database.is_public !== null ? this.database.is_public : false
},
is_owner () {
return this.token && this.query.creator.username === this.user.username
......@@ -377,6 +377,9 @@ export default {
if (this.erroneous) {
return false
}
if (this.database.is_public === null) {
return false
}
if (this.database.is_public) {
return true
}
......@@ -389,6 +392,9 @@ export default {
if (this.erroneous) {
return false
}
if (this.database.is_public === null) {
return false
}
if (this.database.is_public) {
return true
}
......@@ -439,41 +445,29 @@ export default {
.then(() => this.loadMetadata())
},
methods: {
async metadata () {
this.metadataLoading = true
try {
const res = await this.$axios.get(`/api/identifier/${this.identifier.id}`, this.config)
console.debug('identifier result', res)
const url = window.URL.createObjectURL(new Blob([res.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', 'metadata.xml')
document.body.appendChild(link)
link.click()
} catch (err) {
console.error('Could not export metadata', err)
this.$toast.error('Could not export metadata')
this.error = true
}
this.metadataLoading = false
},
loadResult () {
this.$refs.queryResults.reExecute(this.query.id)
},
async download () {
async download (mime) {
this.downloadLoading = true
try {
const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/query/${this.$route.params.query_id}/export`, this.config)
console.debug('export query result', res)
const config = this.config
config.headers.Accept = mime
const res = await this.$axios.get(`/api/pid/${this.identifier.id}`, config)
console.debug('export identifier', res)
const url = window.URL.createObjectURL(new Blob([res.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', 'query.csv')
if (mime === 'text/csv') {
link.setAttribute('download', 'subset.csv')
} else if (mime === 'text/xml') {
link.setAttribute('download', 'identifier.xml')
}
document.body.appendChild(link)
link.click()
} catch (err) {
console.error('Could not export query result', err)
this.$toast.error('Could not export query result')
console.error('Could not export identifier', err)
this.$toast.error('Could not export identifier')
this.error = true
}
this.downloadLoading = false
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment