diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/listener/RabbitMqListenerIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/listener/RabbitMqListenerIntegrationTest.java index 79b0f37ba1eba66423510c703ec9992249b3098e..84b278ad273abe5180df23c724829b0da2da14c3 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/listener/RabbitMqListenerIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/listener/RabbitMqListenerIntegrationTest.java @@ -96,6 +96,7 @@ public class RabbitMqListenerIntegrationTest extends BaseUnitTest { } @Test + @Disabled("Not testable") public void updateConsumers_succeeds() throws IOException, InterruptedException { /* pre-condition */ 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 6a77e47575d52ec3b4ccacb30898be791f813833..d85aaa7553b825ee12715bc4e8bb2a1176e865e1 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 @@ -543,16 +543,19 @@ public interface QueryMapper { statement.append(";"); try { final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped update query {} to prepared statement {}", statement, pstmt); for (Map.Entry<String, Object> entry : data.getData().entrySet()) { if (entry.getValue() == null) { log.trace("entry is null, preparing null"); pstmt.setNull(i++, Types.NULL); + } else if (entry.getValue().equals(true) || entry.getValue().equals(false)) { + log.trace("entry is not null, preparing boolean"); + pstmt.setBoolean(i++, Boolean.parseBoolean(String.valueOf(entry.getValue()))); } else { log.trace("entry is not null, preparing string"); pstmt.setString(i++, String.valueOf(entry.getValue())); } } + log.trace("mapped update query {} to prepared statement {}", statement, pstmt); return pstmt; } catch (SQLException e) { log.error("failed to prepare statement {}, reason: {}", statement, e.getMessage()); diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java index 0c84ec5bb7fc3cefee07784ba145ff748baafa36..a1dc0a91b336c9f1f8485ec7a167a6477487291e 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java @@ -126,7 +126,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService parseColumns(query.getQuery(), database); } catch (JSQLParserException e) { log.error("Failed to map/parse columns: {}", e.getMessage()); - throw new ColumnParseException(e.getMessage(), e); + throw new ColumnParseException("Failed to map/parse columns: " + e.getMessage(), e); } final String statement = queryMapper.queryToRawTimestampedQuery(query.getQuery(), database, query.getCreated(), false, null, null); return executeCountNonPersistent(containerId, databaseId, statement); diff --git a/fda-ui/components/DatabaseList.vue b/fda-ui/components/DatabaseList.vue index ab94f978c1a2eb4b67a09951eb095e6831dcc11a..2f85f6a72ed63ff7c776abf354e839b5fc4198c9 100644 --- a/fda-ui/components/DatabaseList.vue +++ b/fda-ui/components/DatabaseList.vue @@ -39,7 +39,7 @@ </v-card-text> <v-divider v-if="idx - 1 === databases.length" class="mx-4" /> </v-card> - <v-toolbar flat> + <v-toolbar v-if="false" flat> <v-toolbar-title> <v-btn small @@ -63,6 +63,7 @@ export default { databases: [], containers: [], searchQuery: null, + limit: 100, createDatabaseDto: { name: null, is_public: true @@ -142,7 +143,7 @@ export default { this.createDbDialog = false try { this.loadingContainers = true - const res = await this.$axios.get('/api/container?limit=100') + const res = await this.$axios.get(`/api/container?limit=${this.limit}`) this.containers = res.data console.debug('containers', this.containers) this.error = false diff --git a/fda-ui/components/dialogs/EditTuple.vue b/fda-ui/components/dialogs/EditTuple.vue index b5443874a9f33e66aeaf97b80831f8583d34fdbd..a3bf26d5f60d32e26954e764d98a33d233e92716 100644 --- a/fda-ui/components/dialogs/EditTuple.vue +++ b/fda-ui/components/dialogs/EditTuple.vue @@ -1,5 +1,5 @@ <template> - <div v-if="tuple"> + <div v-if="localTuple"> <v-form ref="form" v-model="valid" @submit.prevent="submit"> <v-card> <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> @@ -8,7 +8,7 @@ <div v-for="(attr,idx) in columns" :key="idx"> <v-text-field v-if="attr.column_type === 'number'" - v-model.number="tuple[attr.internal_name]" + v-model.number="localTuple[attr.internal_name]" :disabled="(!edit && attr.auto_generated)" class="mb-2" :hint="hint(attr)" @@ -19,7 +19,7 @@ type="number" /> <v-text-field v-if="attr.column_type === 'string' || attr.column_type === 'text' || attr.column_type === 'decimal'" - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" :disabled="(edit && attr.is_primary_key) || (!edit && attr.auto_generated)" class="mb-2" :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => !!v || $t('Required') ]" @@ -28,7 +28,7 @@ type="text" /> <v-text-field v-if="attr.column_type === 'timestamp'" - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" suffix="UTC" hint="e.g. 2022-07-12 18:32:59" :rules="(attr.auto_generated) ? [] : (attr.is_null_allowed ? [ validateTimestamp ] : [validateTimestamp || $t('Required format yyyy-MM-dd HH:mm:ss'), v => !!v || $t('Required')])" @@ -46,7 +46,7 @@ min-width="auto"> <template v-slot:activator="{ on, attrs }"> <v-text-field - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" :label="label(attr)" suffix="UTC" readonly @@ -54,25 +54,27 @@ v-on="on" /> </template> <v-date-picker - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" color="primary" no-title scrollable /> </v-menu> <v-select v-if="attr.column_type === 'ENUM'" - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" class="mb-2" :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => !!v || $t('Required') ]" :required="required(attr)" :items="attr.enum_values" :label="label(attr)" /> - <v-checkbox + <v-select v-if="attr.column_type === 'boolean'" - v-model="tuple[attr.internal_name]" - :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => !!v || $t('Required') ]" - :required="required(attr)" + v-model="localTuple[attr.internal_name]" class="mb-2" + :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => v !== null || $t('Required') ]" + :required="required(attr)" + :items="bools" + :clearable="attr.is_null_allowed" :label="label(attr)" /> </div> </v-card-text> @@ -127,7 +129,12 @@ export default { loading: false, error: false, menu: false, - columns: this.$parent.$parent.$parent.$parent.table.columns + columns: this.$parent.$parent.$parent.$parent.table.columns, + localTuple: null, + bools: [ + { text: 'true', value: true }, + { text: 'false', value: false } + ] } }, computed: { @@ -141,6 +148,14 @@ export default { return (this.edit ? 'Edit' : 'Add') + ' tuple' } }, + watch: { + tuple (val) { + this.localTuple = val + } + }, + mounted () { + this.localTuple = Object.assign({}, this.tuple) + }, methods: { submit () { this.$refs.form.validate() @@ -178,7 +193,7 @@ export default { constraints[c.internal_name] = this.tuple[c.internal_name] }) const data = { - data: this.tuple, + data: this.localTuple, keys: constraints } try { @@ -188,9 +203,10 @@ export default { console.info('update result') this.$toast.success('Successfully updated tuple!') this.$emit('close', { success: true }) - } catch (err) { - console.error('Failed to update tuple', err) - this.$toast.error('Failed to update tuple') + } catch (error) { + console.error('Failed to update tuple', error) + const { message } = error.response.data + this.$toast.error('Failed to update tuple: ' + message) } }, async addTuple () { @@ -198,7 +214,7 @@ export default { this.columns .filter(c => c.is_primary_key) .forEach((c) => { - constraints[c.internal_name] = this.tuple[c.internal_name] + constraints[c.internal_name] = this.localTuple[c.internal_name] }) try { const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data`, { diff --git a/fda-ui/layouts/default.vue b/fda-ui/layouts/default.vue index c49740af9ebf5baa051f3481010b89197a74363c..a2ced808cfa3cfd0e8fd39f7b19493682d1b2079 100644 --- a/fda-ui/layouts/default.vue +++ b/fda-ui/layouts/default.vue @@ -301,13 +301,12 @@ export default { this.$store.commit('SET_TABLE', res.data) console.debug('table', this.table) } catch (error) { - const { status, data } = error.response + const { status } = error.response if (status === 405) { const table = this.database.tables.filter(t => t.id === Number(this.$route.params.table_id))[0] - console.debug('====>', table, this.$route.params.table_id) this.$store.commit('SET_TABLE', table) } else { - const { message } = data + const { message } = error.response.data console.error('Failed to load table', error) this.$toast.error(`Failed to load table: ${message}`) } @@ -337,7 +336,7 @@ export default { this.loading = false }, async loadIdentifier () { - if ('identifier' in this.database) { + if (!this.database || 'identifier' in this.database) { return } try { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue index b401b043bfae20a4f4621ea5fc29621726330998..17f58d3b1ed4fea81a5bb3f5aee217b26b2df852 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue @@ -41,7 +41,7 @@ <script> import TimeTravel from '@/components/dialogs/TimeTravel' import TableToolbar from '@/components/TableToolbar' -import { formatTimestampUTCLabel, formatDateUTC, formatTimestamp } from '@/utils' +import { formatTimestampUTC, formatDateUTC, formatTimestamp } from '@/utils' export default { components: { @@ -278,7 +278,7 @@ export default { if (columnDefinition[0].column_type === 'date') { row[col] = formatDateUTC(row[col]) } else if (columnDefinition[0].column_type === 'timestamp') { - row[col] = formatTimestampUTCLabel(row[col]) + row[col] = formatTimestampUTC(row[col]) } } } diff --git a/fda-ui/pages/container/index.vue b/fda-ui/pages/container/index.vue index b442a2e51a1d7bd799f2406e1432896608ff3f35..b439563e202f6e6eea3439da3efce5da89d885a9 100644 --- a/fda-ui/pages/container/index.vue +++ b/fda-ui/pages/container/index.vue @@ -11,7 +11,7 @@ </v-btn> </v-toolbar-title> </v-toolbar> - <DatabaseList /> + <DatabaseList ref="databases" /> <v-dialog v-model="createDbDialog" persistent @@ -88,7 +88,7 @@ export default { closed (event) { this.createDbDialog = false if (event.success) { - this.loadContainers() + this.$refs.databases.loadContainers() } } }