diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java index 9f91dd3bc28b461e2b3981ab658242d194528b01..f63e9913ee605356b9bc7701557027b873f5b25b 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java @@ -40,6 +40,7 @@ import org.mapstruct.Named; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Statement; import java.text.Normalizer; import java.util.*; import java.util.regex.Pattern; @@ -219,14 +220,14 @@ public interface TableMapper { @Mappings({ @Mapping(source = "table", target = "table"), @Mapping(target = "id", ignore = true), - @Mapping(target = "autoGenerated", ignore = true), + @Mapping(target = "autoGenerated", expression = "java(data.getInternalName() == \"id\" && generatedSequence)"), @Mapping(source = "data.name", target = "name"), @Mapping(source = "data.internalName", target = "internalName"), @Mapping(source = "data.created", target = "created"), @Mapping(source = "data.dateFormat", target = "dateFormat"), @Mapping(source = "data.lastModified", target = "lastModified"), }) - TableColumn tableColumnToTableColumn(Table table, TableColumn data); + TableColumn tableColumnToTableColumn(Table table, TableColumn data, Boolean generatedSequence); @Named("internalMapping") default String nameToInternalName(String data) { @@ -247,6 +248,7 @@ public interface TableMapper { @Mapping(target = "columnType", source = "data.type"), @Mapping(target = "isNullAllowed", source = "data.nullAllowed"), @Mapping(target = "name", source = "data.name"), + @Mapping(target = "autoGenerated", expression = "java(false)"), @Mapping(target = "internalName", expression = "java(nameToInternalName(data.getName()))"), @Mapping(target = "dateFormat", expression = "java(dateFormatIdToContainerImageDate(data.getDfid(), image))"), }) @@ -333,11 +335,12 @@ public interface TableMapper { * Map the table to a create table and eventual create sequence query. * * @param data The table - * @return The create table query + * @return True if a sequence has been generated, false otherwise. */ - default PreparedStatement tableToCreateTableRawQuery(Connection connection, TableCreateDto data) + default Boolean tableToCreateTableRawQuery(Connection connection, TableCreateDto data) throws TableMalformedException, QueryMalformedException { - final StringBuilder query = new StringBuilder("CREATE TABLE `") + final StringBuilder sequence = new StringBuilder(); + final StringBuilder table = new StringBuilder("CREATE TABLE `") .append(nameToInternalName(data.getName())) .append("` ("); /* internal checks */ @@ -364,10 +367,16 @@ public interface TableMapper { columns.add(idColumn); columns.addAll(data.getColumns()); data.setColumns(columns); + /* data */ + final String sequenceName = tableCreateDtoToSequenceName(data); + log.debug("create sequence with name {}", sequenceName); + sequence.append("CREATE SEQUENCE `") + .append(sequenceName) + .append("` START WITH 1 INCREMENT BY 1 NOCACHE; "); } final int[] idx = {0}; for (ColumnCreateDto column : data.getColumns()) { - query.append(idx[0]++ > 0 ? ", " : "") + table.append(idx[0]++ > 0 ? ", " : "") .append("`") .append(nameToInternalName(column.getName())) .append("` ") @@ -376,10 +385,11 @@ public interface TableMapper { /* null expressions */ .append(column.getNullAllowed() != null && column.getNullAllowed() ? " NULL" : " NOT NULL") /* default expressions */ - .append(!primaryColumnExists && column.getName().equals("id") ? " AUTO_INCREMENT" : ""); + .append(!primaryColumnExists && column.getName().equals( + "id") ? " DEFAULT NEXTVAL(`" + tableCreateDtoToSequenceName(data) + "`)" : ""); } /* create primary key index */ - query.append(", PRIMARY KEY (") + table.append(", PRIMARY KEY (") .append(String.join(",", data.getColumns() .stream() .filter(c -> Objects.nonNull(c.getPrimaryKey())) @@ -393,7 +403,7 @@ public interface TableMapper { if (data.getConstraints().getUniques() != null) { /* create unique indices */ data.getConstraints().getUniques() - .forEach(u -> query.append(", ") + .forEach(u -> table.append(", ") .append("UNIQUE KEY (`") .append(u.stream().map(this::nameToInternalName).collect(Collectors.joining("`,`"))) .append("`)")); @@ -402,7 +412,7 @@ public interface TableMapper { /* create foreign key indices */ data.getConstraints().getForeignKeys() .forEach(fk -> { - query.append(", FOREIGN KEY (`") + table.append(", FOREIGN KEY (`") .append(fk.getColumns().stream().map(this::nameToInternalName).collect(Collectors.joining("`,`"))) .append("`) REFERENCES `") .append(nameToInternalName(fk.getReferencedTable())) @@ -410,53 +420,61 @@ public interface TableMapper { .append(fk.getReferencedColumns().stream().map(this::nameToInternalName).collect(Collectors.joining("`,`"))) .append("`)"); if (fk.getOnDelete() != null) { - query.append(" ON DELETE ").append(fk.getOnDelete()); + table.append(" ON DELETE ").append(fk.getOnDelete()); } if (fk.getOnUpdate() != null) { - query.append(" ON UPDATE ").append(fk.getOnUpdate()); + table.append(" ON UPDATE ").append(fk.getOnUpdate()); } }); } if (data.getConstraints().getChecks() != null) { /* create check constraints */ data.getConstraints().getChecks() - .forEach(ck -> query.append(", ") + .forEach(ck -> table.append(", ") .append("CHECK (") .append(ck) .append(")")); } } - query.append(") WITH SYSTEM VERSIONING;"); + table.append(") WITH SYSTEM VERSIONING;"); log.trace("create table query built with {} columns and system versioning", data.getColumns().size()); try { - log.trace("mapped create table statement: {}", query); - return connection.prepareStatement(query.toString()); + final Statement statement = connection.createStatement(); + if (sequence.length() > 0) { + log.trace("mapped create sequence statement: {}", sequence); + statement.execute(sequence.toString()); + } + log.trace("mapped create table statement: {}", table); + statement.execute(table.toString()); + return !sequence.isEmpty(); } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", query, e.getMessage()); + log.error("Failed to prepare statement {}, reason: {}", table, e.getMessage()); throw new QueryMalformedException("Failed to prepare statement", e); } } + default String tableCreateDtoToSequenceName(TableCreateDto data) { + final String name = "seq_" + nameToInternalName(data.getName()) + "_id"; + log.trace("mapped name {} to internal name {}", data.getName(), name); + return name; + } + default PreparedStatement tableToCreateHistoryViewRawQuery(Connection connection, Table data) throws QueryMalformedException { final StringBuilder statement = new StringBuilder("CREATE VIEW `hs_") .append(data.getInternalName()) .append("` AS SELECT * FROM (SELECT "); final int[] idx = new int[]{0}; - final StringBuilder keys = new StringBuilder(); data.getColumns() .stream() .filter(c -> Objects.nonNull(c.getIsPrimaryKey())) .filter(TableColumn::getIsPrimaryKey) - .forEach(c -> keys.append(idx[0]++ > 0 ? "," : "") + .forEach(c -> statement.append(idx[0]++ > 0 ? "," : "") .append("`") .append(c.getInternalName()) .append("`")); - statement.append(keys) - .append(", ROW_START AS inserted_at, IF(ROW_END > NOW(), NULL, ROW_END) AS deleted_at, COUNT(*) as total FROM `") + statement.append(", ROW_START AS inserted_at, IF(ROW_END > NOW(), NULL, ROW_END) AS deleted_at, COUNT(*) as total FROM `") .append(data.getInternalName()) - .append("` FOR SYSTEM_TIME ALL GROUP BY ") - .append(keys) - .append(", inserted_at, deleted_at ORDER BY deleted_at DESC LIMIT 50) AS v ORDER BY v.inserted_at, v.deleted_at ASC"); + .append("` FOR SYSTEM_TIME ALL GROUP BY inserted_at, deleted_at ORDER BY deleted_at DESC LIMIT 50) AS v ORDER BY v.inserted_at, v.deleted_at ASC"); try { final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); log.trace("prepared create sequence statement {}", statement); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index 33769e83399068dd90c23204aadec8c9d28e9fc4..fa5e9d60f43bd61e3cbd915e097d7dcad7d5bb35 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -172,10 +172,10 @@ public class TableServiceImpl extends HibernateConnector implements TableService } /* run query */ final ComboPooledDataSource dataSource = getPrivilegedDataSource(database.getContainer().getImage(), database.getContainer(), database); + final Boolean generatedSequence; try { final Connection connection = dataSource.getConnection(); - final PreparedStatement preparedStatement = tableMapper.tableToCreateTableRawQuery(connection, createDto); - preparedStatement.executeUpdate(); + generatedSequence = tableMapper.tableToCreateTableRawQuery(connection, createDto); } catch (Exception e) { log.error("Failed to create table, reason: {}", e.getMessage()); throw new TableMalformedException("Failed to create table", e); @@ -198,7 +198,7 @@ public class TableServiceImpl extends HibernateConnector implements TableService entity.setColumns(createDto.getColumns() .stream() .map(column -> tableMapper.columnCreateDtoToTableColumn(column, database.getContainer().getImage())) - .map(column -> tableMapper.tableColumnToTableColumn(entity, column)) + .map(column -> tableMapper.tableColumnToTableColumn(entity, column, generatedSequence)) .toList()); /* set the ordinal position for the columns */ entity.getColumns() diff --git a/dbrepo-ui/api/container.service.js b/dbrepo-ui/api/container.service.js index 97d0fc7164efb1c783a52b60614547f17171dfe5..2d6021c89f6bf75e0f15b4a1950faf4d626ab933 100644 --- a/dbrepo-ui/api/container.service.js +++ b/dbrepo-ui/api/container.service.js @@ -11,7 +11,7 @@ class ContainerService { resolve(containers) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load containers', error) Vue.$toast.error(`[${code}] Failed to load containers: ${message}`) reject(error) @@ -28,7 +28,7 @@ class ContainerService { resolve(container) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load container', error) Vue.$toast.error(`[${code}] Failed to load container: ${message}`) reject(error) @@ -45,7 +45,7 @@ class ContainerService { resolve(image) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load image', error) Vue.$toast.error(`[${code}] Failed to load image: ${message}`) reject(error) diff --git a/dbrepo-ui/api/database.service.js b/dbrepo-ui/api/database.service.js index f09e71de207c51f0c7722df6ac6e772b0fd8d2e9..7eb8f36f074b8d2197172665bd92df323f9dfe2c 100644 --- a/dbrepo-ui/api/database.service.js +++ b/dbrepo-ui/api/database.service.js @@ -11,7 +11,7 @@ class DatabaseService { resolve(databases) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load databases', error) Vue.$toast.error(`[${code}] Failed to load databases: ${message}`) reject(error) @@ -28,7 +28,7 @@ class DatabaseService { resolve(databases) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load my databases', error) Vue.$toast.error(`[${code}] Failed to load my databases: ${message}`) reject(error) @@ -45,7 +45,7 @@ class DatabaseService { resolve(count) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to count databases', error) Vue.$toast.error(`[${code}] Failed to count databases: ${message}`) reject(error) @@ -61,7 +61,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load database', error) Vue.$toast.error(`[${code}] Failed to load database: ${message}`) reject(error) @@ -77,7 +77,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create database', error) Vue.$toast.error(`[${code}] Failed to create database: ${message}`) reject(error) @@ -90,7 +90,7 @@ class DatabaseService { api.delete(`/api/database/${databaseId}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete database', error) Vue.$toast.error(`[${code}] Failed to delete database: ${message}`) reject(error) @@ -106,7 +106,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to modify database visibility', error) Vue.$toast.error(`[${code}] Failed to modify database visibility: ${message}`) reject(error) @@ -122,7 +122,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to modify database owner', error) Vue.$toast.error(`[${code}] Failed to modify database owner: ${message}`) reject(error) @@ -139,8 +139,8 @@ class DatabaseService { resolve(databases) }) .catch((error) => { - const { code, message, response } = error - const { status } = response + const { status } = error + const { code, message } = error.response.data if (status !== 401 && status !== 403 && status !== 405) { /* ignore no access errors */ console.error('Failed to check database access', error) Vue.$toast.error(`[${code}] Failed to check database access: ${message}`) @@ -159,7 +159,7 @@ class DatabaseService { resolve(database) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to modify database access', error) Vue.$toast.error(`[${code}] Failed to modify database access: ${message}`) reject(error) @@ -172,7 +172,7 @@ class DatabaseService { api.delete(`/api/database/${databaseId}/access/${userId}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to revoke database access', error) Vue.$toast.error(`[${code}] Failed to revoke database access: ${message}`) reject(error) @@ -185,7 +185,7 @@ class DatabaseService { api.post(`/api/database/${databaseId}/access/${userId}`, { type }, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to give database access', error) Vue.$toast.error(`[${code}] Failed to give database access: ${message}`) reject(error) @@ -202,7 +202,7 @@ class DatabaseService { resolve(licenses) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load licenses', error) Vue.$toast.error(`[${code}] Failed to load licenses: ${message}`) reject(error) @@ -219,7 +219,7 @@ class DatabaseService { resolve(view) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to find view', error) Vue.$toast.error(`[${code}] Failed to find view: ${message}`) reject(error) @@ -236,7 +236,7 @@ class DatabaseService { resolve(view) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete view', error) Vue.$toast.error(`[${code}] Failed to delete view: ${message}`) reject(error) @@ -249,7 +249,7 @@ class DatabaseService { api.delete(`/api/database/${databaseId}/view/${viewId}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete view', error) Vue.$toast.error(`[${code}] Failed to delete view: ${message}`) reject(error) diff --git a/dbrepo-ui/api/identifier.service.js b/dbrepo-ui/api/identifier.service.js index 7708df66e4b32548938f74a5de262c2273db9b1a..c377aa03264748308cff834555cf8a5622527660 100644 --- a/dbrepo-ui/api/identifier.service.js +++ b/dbrepo-ui/api/identifier.service.js @@ -12,7 +12,9 @@ class IdentifierService { resolve(identifiers) }) .catch((error) => { + const { code, message } = error.response.data console.error('Failed to load identifiers', error) + Vue.$toast.error(`[${code}] Failed to load identifiers: ${message}`) reject(error) }) }) @@ -39,7 +41,9 @@ class IdentifierService { } }) .catch((error) => { - console.error('Failed to load metadata', error) + const { code, message } = error.response.data + console.error('Failed to load identifier metadata', error) + Vue.$toast.error(`[${code}] Failed to load identifier metadata: ${message}`) reject(error) }) }) @@ -54,7 +58,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load identifier', error) Vue.$toast.error(`[${code}] Failed to load identifier: ${message}`) reject(error) @@ -71,7 +75,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create identifier', error) Vue.$toast.error(`[${code}] Failed to create identifier: ${message}`) reject(error) @@ -88,7 +92,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update identifier', error) Vue.$toast.error(`[${code}] Failed to update identifier: ${message}`) reject(error) @@ -105,7 +109,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export identifier', error) Vue.$toast.error(`[${code}] Failed to export identifier: ${message}`) reject(error) @@ -118,7 +122,7 @@ class IdentifierService { api.delete(`/api/pid/${pid}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete identifier', error) Vue.$toast.error(`[${code}] Failed to delete identifier: ${message}`) reject(error) diff --git a/dbrepo-ui/api/metadata.service.js b/dbrepo-ui/api/metadata.service.js index 9c02ea9247eea2ab242bf5387652d4ed2e50b5bc..c49d1d952da0b0865d6c42c3affd35ed737f1c94 100644 --- a/dbrepo-ui/api/metadata.service.js +++ b/dbrepo-ui/api/metadata.service.js @@ -11,7 +11,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load messages', error) Vue.$toast.error(`[${code}] Failed to load messages: ${message}`) reject(error) @@ -28,7 +28,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create message', error) Vue.$toast.error(`[${code}] Failed to create message: ${message}`) reject(error) @@ -45,7 +45,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to find message', error) Vue.$toast.error(`[${code}] Failed to find message: ${message}`) reject(error) @@ -62,7 +62,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update message', error) Vue.$toast.error(`[${code}] Failed to update message: ${message}`) reject(error) @@ -75,7 +75,7 @@ class MetadataService { api.delete(`/api/maintenance/message/${id}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete message', error) Vue.$toast.error(`[${code}] Failed to delete message: ${message}`) reject(error) @@ -92,7 +92,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load active messages', error) Vue.$toast.error(`[${code}] Failed to load active messages: ${message}`) reject(error) diff --git a/dbrepo-ui/api/query.service.js b/dbrepo-ui/api/query.service.js index 857464873a17ffaece8ce68026b7d7d4cfcbaa24..8f8488d6478611c671b62b8bd36fcf36dd92d538 100644 --- a/dbrepo-ui/api/query.service.js +++ b/dbrepo-ui/api/query.service.js @@ -11,7 +11,9 @@ class QueryService { resolve(queries) }) .catch((error) => { + const { code, message } = error.response.data console.error('Failed to load queries', error) + Vue.$toast.error(`[${code}] Failed to load queries: ${message}`) reject(error) }) }) @@ -25,7 +27,9 @@ class QueryService { console.debug('response query', query) resolve(query) }).catch((error) => { + const { code, message } = error.response.data console.error('Failed to load query', error) + Vue.$toast.error(`[${code}] Failed to load query: ${message}`) reject(error) }) }) @@ -40,7 +44,7 @@ class QueryService { resolve(query) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to persist query', error) Vue.$toast.error(`[${code}] Failed to persist query: ${message}`) reject(error) @@ -57,7 +61,7 @@ class QueryService { resolve(table) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to import csv to table', error) Vue.$toast.error(`[${code}] Failed to import csv to table: ${message}`) reject(error) @@ -74,16 +78,14 @@ class QueryService { resolve(tuple) }) .catch((error) => { - const { response } = error - const { status, data } = response - const { message } = data + const { status } = error + const { code, message } = error.response.data if (status === 423) { console.error('Database failed to accept tuple', error) - Vue.$toast.error(message) } else { console.error('Failed to insert tuple', error) - Vue.$toast.error(message) } + Vue.$toast.error(`[${code}] Failed to insert tuple: ${message}`) reject(error) }) }) @@ -98,15 +100,14 @@ class QueryService { resolve(tuple) }) .catch((error) => { - const { code, message, response } = error - const { status } = response + const { status } = error + const { code, message } = error.response.data if (status === 423) { console.error('Database failed to accept tuple', error) - Vue.$toast.error(`Database failed to accept tuple: ${message}`) } else { console.error('Failed to update tuple', error) - Vue.$toast.error(`[${code}] Failed to update tuple: ${message}`) } + Vue.$toast.error(`[${code}] Failed to update tuple: ${message}`) reject(error) }) }) @@ -121,7 +122,7 @@ class QueryService { resolve(subset) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export query', error) Vue.$toast.error(`[${code}] Failed to export query: ${message}`) reject(error) @@ -138,7 +139,7 @@ class QueryService { resolve(metadata) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export metadata', error) Vue.$toast.error(`[${code}] Failed to export metadata: ${message}`) reject(error) @@ -155,7 +156,7 @@ class QueryService { resolve(result) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to execute statement', error) Vue.$toast.error(`[${code}] Failed to execute statement: ${message}`) reject(error) @@ -172,7 +173,7 @@ class QueryService { resolve(result) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to re-execute query', error) Vue.$toast.error(`[${code}] Failed to re-execute query: ${message}`) reject(error) @@ -189,7 +190,7 @@ class QueryService { resolve(count) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to re-execute query count', error) Vue.$toast.error(`[${code}] Failed to re-execute query count: ${message}`) reject(error) @@ -206,8 +207,7 @@ class QueryService { resolve(result) }) .catch((error) => { - const { code } = error - const { message } = error.response.data + const { code, message } = error.response.data console.error('Failed to re-execute view', error) Vue.$toast.error(`[${code}] Failed to re-execute view: ${message}`) reject(error) @@ -224,8 +224,7 @@ class QueryService { resolve(count) }) .catch((error) => { - const { code } = error - const { message } = error.response.data + const { code, message } = error.response.data console.error('Failed to re-execute view count', error) Vue.$toast.error(`[${code}] Failed to re-execute view count: ${message}`) reject(error) @@ -242,7 +241,7 @@ class QueryService { resolve(view) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to find view', error) Vue.$toast.error(`[${code}] Failed to find view: ${message}`) reject(error) diff --git a/dbrepo-ui/api/semantic.service.js b/dbrepo-ui/api/semantic.service.js index 01b8b021a3fc86bde81b7ad9a92d383591d35591..88c6b75388c4d51734ca282c968238d556240c83 100644 --- a/dbrepo-ui/api/semantic.service.js +++ b/dbrepo-ui/api/semantic.service.js @@ -11,7 +11,7 @@ class SemanticService { resolve(ontologies) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load ontologies', error) Vue.$toast.error(`[${code}] Failed to load ontologies: ${message}`) reject(error) @@ -28,7 +28,7 @@ class SemanticService { resolve(concepts) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load concepts', error) Vue.$toast.error(`[${code}] Failed to load concepts: ${message}`) reject(error) @@ -45,7 +45,7 @@ class SemanticService { resolve(concept) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update concept', error) Vue.$toast.error(`[${code}] Failed to update concept: ${message}`) reject(error) @@ -62,7 +62,7 @@ class SemanticService { resolve(units) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load units', error) Vue.$toast.error(`[${code}] Failed to load units: ${message}`) reject(error) @@ -79,7 +79,7 @@ class SemanticService { resolve(unit) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update unit', error) Vue.$toast.error(`[${code}] Failed to update unit: ${message}`) reject(error) @@ -96,7 +96,7 @@ class SemanticService { resolve(ontology) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load ontology', error) Vue.$toast.error(`[${code}] Failed to load ontology: ${message}`) reject(error) @@ -113,7 +113,7 @@ class SemanticService { resolve(ontology) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to register ontology', error) Vue.$toast.error(`[${code}] Failed to register ontology: ${message}`) reject(error) @@ -130,7 +130,7 @@ class SemanticService { resolve(ontology) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update ontology', error) Vue.$toast.error(`[${code}] Failed to update ontology: ${message}`) reject(error) @@ -143,7 +143,7 @@ class SemanticService { api.delete(`/api/semantic/ontology/${id}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete ontology', error) Vue.$toast.error(`[${code}] Failed to delete ontology: ${message}`) reject(error) @@ -160,7 +160,7 @@ class SemanticService { resolve(semantics) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table column semantics', error) Vue.$toast.error(`[${code}] Failed to load table column semantics: ${message}`) reject(error) diff --git a/dbrepo-ui/api/table.service.js b/dbrepo-ui/api/table.service.js index 6d9f92953948f2748a7e37c25c16cedd00afc803..a438b46aaedf589bb1bc877302157b6b42e4e02b 100644 --- a/dbrepo-ui/api/table.service.js +++ b/dbrepo-ui/api/table.service.js @@ -16,7 +16,7 @@ class TableService { resolve(tables) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load tables', error) Vue.$toast.error(`[${code}] Failed to load tables: ${message}`) reject(error) @@ -33,7 +33,7 @@ class TableService { resolve(table) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table', error) Vue.$toast.error(`[${code}] Failed to load table: ${message}`) reject(error) @@ -50,7 +50,7 @@ class TableService { resolve(column) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update column', error) Vue.$toast.error(`[${code}] Failed to update column: ${message}`) reject(error) @@ -63,7 +63,7 @@ class TableService { api.post(`/api/database/${databaseId}/table/${tableId}/data/import`, data, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to import table data', error) Vue.$toast.error(`[${code}] Failed to import table data: ${message}`) reject(error) @@ -80,7 +80,7 @@ class TableService { resolve(data) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table data', error) Vue.$toast.error(`[${code}] Failed to load table data: ${message}`) reject(error) @@ -97,7 +97,7 @@ class TableService { resolve(count) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table count', error) Vue.$toast.error(`[${code}] Failed to load table count: ${message}`) reject(error) @@ -114,7 +114,7 @@ class TableService { resolve(history) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table history', error) Vue.$toast.error(`[${code}] Failed to load table history: ${message}`) reject(error) @@ -135,7 +135,7 @@ class TableService { resolve(data) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export table data', error) Vue.$toast.error(`[${code}] Failed to export table data: ${message}`) reject(error) @@ -152,7 +152,7 @@ class TableService { resolve(table) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create table', error) Vue.$toast.error(`[${code}] Failed to create table: ${message}`) reject(error) @@ -165,7 +165,7 @@ class TableService { api.delete(`/api/database/${databaseId}/table/${tableId}/data`, { headers: { Accept: 'application/json' }, data }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete table tuple', error) Vue.$toast.error(`[${code}] Failed to delete table tuple: ${message}`) reject(error) diff --git a/dbrepo-ui/api/user.service.js b/dbrepo-ui/api/user.service.js index 0fe5a464bd50a40b9af0a945dc86ec52eae2ecb7..0eacd13861c1c9ef74b10cb74c4a91ba554b2aaf 100644 --- a/dbrepo-ui/api/user.service.js +++ b/dbrepo-ui/api/user.service.js @@ -12,7 +12,7 @@ class UserService { resolve(users) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load users', error) Vue.$toast.error(`[${code}] Failed to load users: ${message}`) reject(error) @@ -28,7 +28,7 @@ class UserService { console.debug('response user', response.data, 'mapped user', user) resolve(user) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load user', error) Vue.$toast.error(`[${code}] Failed to load user: ${message}`) reject(error) @@ -44,7 +44,7 @@ class UserService { console.debug('response user', response.data, 'mapped user', user) resolve(user) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update user information', error) Vue.$toast.error(`[${code}] Failed to update user information: ${message}`) reject(error) @@ -60,8 +60,8 @@ class UserService { console.debug('response user', user) resolve(user) }).catch((error) => { - const { code, message, response } = error - const { status } = response + const { status } = error + const { code, message } = error.response.data if (status === 417) { Vue.$toast.error('This e-mail address is already taken') } else if (status === 409) { @@ -83,7 +83,7 @@ class UserService { api.put(`/api/user/${id}/password`, { password }, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update user password', error) Vue.$toast.error(`[${code}] Failed to update user password: ${message}`) reject(error) @@ -96,7 +96,7 @@ class UserService { api.put(`/api/user/${id}/theme`, { theme_dark: themeDark }, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update user theme', error) Vue.$toast.error(`[${code}] Failed to update user theme: ${message}`) reject(error) diff --git a/dbrepo-ui/components/TableSchema.vue b/dbrepo-ui/components/TableSchema.vue index 9f2a0f4103ade28fe58833df9f3aab2a7e698359..d2447f2e277bedb67a13950177532e499915337d 100644 --- a/dbrepo-ui/components/TableSchema.vue +++ b/dbrepo-ui/components/TableSchema.vue @@ -55,6 +55,7 @@ type="number" required :rules="[v => (v !== null && v !== '') || $t('Required')]" + :error-messages="sizeErrorMessages(c)" label="size *" /> </v-col> <v-col cols="1" :hidden="defaultD(c) === false"> @@ -63,6 +64,7 @@ type="number" required :rules="[v => (v !== null && v !== '') || $t('Required')]" + :error-messages="dErrorMessages(c)" label="d *" /> </v-col> <v-col v-if="hasDate(c)" cols="1"> @@ -287,6 +289,24 @@ export default { } return df.has_time }) + }, + sizeErrorMessages (column) { + if (column.size < column.d) { + return ['Size needs to be bigger or equal to d'] + } + if (column.size < 0) { + return ['Size must be positive'] + } + return [] + }, + dErrorMessages (column) { + if (column.size < column.d) { + return ['D needs to be smaller or equal to size'] + } + if (column.d < 0) { + return ['D must be positive'] + } + return [] } } } diff --git a/dbrepo-ui/components/dialogs/EditTuple.vue b/dbrepo-ui/components/dialogs/EditTuple.vue index 781b58014ae8d763b2df40a2ac0523fdc2eb70c8..9547f44d6893510c9b52cadae990de3fa74e9f5e 100644 --- a/dbrepo-ui/components/dialogs/EditTuple.vue +++ b/dbrepo-ui/components/dialogs/EditTuple.vue @@ -194,7 +194,7 @@ export default { }, hint (column) { if (!this.edit && column.auto_generated) { - return 'Value is auto-generated' + return 'Auto-generated by sequence' } if (this.edit && column.is_primary_key) { return 'Required (Primary Key)' @@ -268,9 +268,6 @@ export default { disabled (column) { return (this.edit && column.is_primary_key) || (!this.edit && column.auto_generated) }, - validateTimestamp (val) { - return /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/.test(val) - }, updateTuple () { const constraints = {} this.columns diff --git a/dbrepo-ui/pages/database/_database_id/table/_table_id/import.vue b/dbrepo-ui/pages/database/_database_id/table/_table_id/import.vue index c9221fafcbb1c8724d9bf75b660af346fe5ff33d..5fa9cc6a387e3ae89cd8e9864d6d1749f215a25c 100644 --- a/dbrepo-ui/pages/database/_database_id/table/_table_id/import.vue +++ b/dbrepo-ui/pages/database/_database_id/table/_table_id/import.vue @@ -136,7 +136,7 @@ export default { quote: '"', false_element: null, true_element: null, - null_element: null, + null_element: 'NA', separator: ',', skip_lines: 1 }, diff --git a/dbrepo-ui/pages/database/_database_id/table/import.vue b/dbrepo-ui/pages/database/_database_id/table/import.vue index 5501a4929b3b5718ac21b457e95466bd832f1711..789259e4fb5fdd01580f9e4525725d6fc19cdb52 100644 --- a/dbrepo-ui/pages/database/_database_id/table/import.vue +++ b/dbrepo-ui/pages/database/_database_id/table/import.vue @@ -256,7 +256,7 @@ export default { quote: '"', false_element: null, true_element: null, - null_element: null, + null_element: 'NA', separator: ',', skip_lines: 1 },