diff --git a/dbrepo-auth-service/init/app.py b/dbrepo-auth-service/init/app.py index 7d43f163fa3f50e326ba71933674500121d9fef2..28f88789c84607dc12418cceeb19ce0cf766b779 100644 --- a/dbrepo-auth-service/init/app.py +++ b/dbrepo-auth-service/init/app.py @@ -1,4 +1,5 @@ import os + import mariadb from keycloak import KeycloakAdmin @@ -8,8 +9,10 @@ admin = KeycloakAdmin(server_url=os.getenv('AUTH_SERVICE_ENDPOINT', 'http://loca username=os.getenv('AUTH_SERVICE_ADMIN', 'admin'), password=os.getenv('AUTH_SERVICE_ADMIN_PASSWORD', 'admin'), verify=True) -user_id = admin.get_user_id(username=system_username) -print(f'Successfully fetched user id: {user_id}') +keycloak_user_id = admin.get_user_id(username=system_username) +print(f'Successfully fetched keycloak user id: {keycloak_user_id}') +ldap_user_id = admin.get_user(user_id=keycloak_user_id).get('attributes')['LDAP_ID'][0] +print(f'Successfully fetched ldap user id: {ldap_user_id}') try: conn = mariadb.connect(user=os.getenv('METADATA_USERNAME', 'root'), @@ -20,7 +23,7 @@ try: cursor = conn.cursor() cursor.execute( "INSERT IGNORE INTO `mdb_users` (`id`, `username`, `email`, `mariadb_password`) VALUES (?, ?, ?, PASSWORD(?))", - (user_id, system_username, 'some@admin', '1234567890')) + (ldap_user_id, system_username, 'some@admin', '1234567890')) conn.commit() conn.close() except mariadb.Error as e: diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index f7e39c7302a5f18ae9dd97e01cce5bb501233887..b2805f8d77157d380cdeac412bce97a438eff392 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -5,6 +5,8 @@ import at.tuwien.api.error.ApiErrorDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; +import at.tuwien.entities.database.View; +import at.tuwien.entities.database.table.Table; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.MetadataMapper; @@ -519,13 +521,14 @@ public class DatabaseEndpoint extends AbstractEndpoint { /* reduce metadata */ database.setTables(database.getTables() .stream() - .filter(t -> t.getIsPublic() || t.getIsSchemaPublic() || optional.isPresent()) + .filter(t -> t.getIsPublic() || optional.isPresent()) .toList()); database.setViews(database.getViews() .stream() - .filter(v -> v.getIsPublic() || v.getIsSchemaPublic() || optional.isPresent()) + .filter(v -> v.getIsPublic() || optional.isPresent()) .toList()); if (!database.getOwner().getId().equals(getId(principal))) { + log.trace("authenticated user is not owner: remove access list"); database.setAccesses(List.of()); } } else { @@ -536,11 +539,11 @@ public class DatabaseEndpoint extends AbstractEndpoint { /* reduce metadata */ database.setTables(database.getTables() .stream() - .filter(t -> t.getIsPublic() || t.getIsSchemaPublic()) + .filter(Table::getIsPublic) .toList()); database.setViews(database.getViews() .stream() - .filter(v -> v.getIsPublic() || v.getIsSchemaPublic()) + .filter(View::getIsPublic) .toList()); database.setAccesses(List.of()); } diff --git a/dbrepo-ui/components/dialogs/DropTable.vue b/dbrepo-ui/components/dialogs/DropTable.vue deleted file mode 100644 index d465c882d0529a817be21f836ea82739fc30155b..0000000000000000000000000000000000000000 --- a/dbrepo-ui/components/dialogs/DropTable.vue +++ /dev/null @@ -1,114 +0,0 @@ -<template> - <div> - <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit"> - <v-card - :title="$t('pages.table.subpages.drop.title') + ' ' + table.internal_name" - variant="elevated"> - <v-card-text> - <v-row dense> - <v-col> - <span> - {{ $t('pages.table.subpages.drop.warning.prefix') }} - </span> - <code class="code-key">{{ table.internal_name }}</code> - <span> - {{ $t('pages.table.subpages.drop.warning.suffix') }} - </span> - </v-col> - </v-row> - <v-row> - <v-col> - <v-text-field - id="confirm" - v-model="confirm" - name="confirm" - persistent-hint - :variant="inputVariant" - :label="$t('pages.table.subpages.drop.name.label')" - :hint="$t('pages.table.subpages.drop.name.hint')" - autofocus - required /> - </v-col> - </v-row> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn - :variant="buttonVariant" - :text="$t('navigation.cancel')" - @click="cancel" /> - <v-btn - color="error" - variant="flat" - :text="$t('navigation.delete')" - :loading="loadingDelete" - :disabled="confirm !== table.internal_name" - type="submit" - @click="dropTable" /> - </v-card-actions> - </v-card> - </v-form> - </div> -</template> - -<script> -import { useCacheStore } from '@/stores/cache' - -export default { - data () { - return { - confirm: null, - loadingDelete: false, - valid: false, - cacheStore: useCacheStore() - } - }, - computed: { - table () { - return this.cacheStore.getTable - }, - database () { - return this.cacheStore.getDatabase - }, - inputVariant () { - const runtimeConfig = useRuntimeConfig() - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal - }, - buttonVariant () { - const runtimeConfig = useRuntimeConfig() - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal - } - }, - methods: { - submit () { - this.$refs.form.validate() - }, - cancel () { - this.$emit('close', { action: 'closed' }) - }, - dropTable () { - if (!this.table.id) { - return - } - this.loadingDelete = true - const tableService = useTableService() - tableService.remove(this.database.id, this.table.id) - .then(() => { - console.info('Deleted table with id ', this.table.id) - this.cacheStore.reloadDatabase() - const toast = useToastInstance() - toast.success('Successfully deleted table with id ' + this.table.id) - this.$router.push(`/database/${this.$route.params.database_id}/table`) - }) - .finally(() => { - this.loadingDelete = false - }) - } - } -} -</script> -<style scoped> -.code-key { - padding: 2px 4px; -} -</style> diff --git a/dbrepo-ui/components/dialogs/UpdateTable.vue b/dbrepo-ui/components/dialogs/UpdateTable.vue deleted file mode 100644 index 8defb2669e68edd7c36af6b528cec0d252e4d12f..0000000000000000000000000000000000000000 --- a/dbrepo-ui/components/dialogs/UpdateTable.vue +++ /dev/null @@ -1,171 +0,0 @@ -<template> - <div> - <v-form - ref="form" - v-model="valid" - autocomplete="off" - @submit.prevent="submit"> - <v-card - :title="$t('pages.view.visibility.title')"> - <v-card-text> - <v-row> - <v-col> - <v-textarea - v-model="modify.description" - rows="2" - :rules="[ - v => (!!v || v.length <= 180) || ($t('validation.max-length') + 180), - ]" - clearable - counter="180" - persistent-counter - persistent-hint - :variant="inputVariant" - :hint="$t('pages.table.subpages.import.description.hint')" - :label="$t('pages.table.subpages.import.description.label')"/> - </v-col> - </v-row> - <v-row - dense> - <v-col - md="6"> - <v-select - v-model="modify.is_public" - :items="dataOptions" - persistent-hint - :variant="inputVariant" - required - :rules="[ - v => v !== null || $t('validation.required') - ]" - :label="$t('pages.database.resource.data.label')" - :hint="$t('pages.database.resource.data.hint')" /> - </v-col> - <v-col - md="6"> - <v-select - v-model="modify.is_schema_public" - :items="schemaOptions" - persistent-hint - :variant="inputVariant" - required - :rules="[ - v => v !== null || $t('validation.required') - ]" - :label="$t('pages.database.resource.schema.label')" - :hint="$t('pages.database.resource.schema.hint')" /> - </v-col> - </v-row> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn - :variant="buttonVariant" - :text="$t('navigation.cancel')" - @click="cancel" /> - <v-btn - id="database" - variant="flat" - :disabled="!valid || !isChange" - :color="buttonColor" - :loading="loading" - type="submit" - :text="$t('navigation.modify')" - @click="update" /> - </v-card-actions> - </v-card> - </v-form> - </div> -</template> - -<script> -import { useCacheStore } from '@/stores/cache' - -export default { - props: { - table: { - type: Object, - default () { - return { - is_public: true, - is_schema_public: true, - description: null - } - } - }, - }, - data () { - return { - valid: false, - loading: false, - dataOptions: [ - { title: this.$t('pages.database.resource.data.enabled'), value: true }, - { title: this.$t('pages.database.resource.data.disabled'), value: false }, - ], - schemaOptions: [ - { title: this.$t('pages.database.resource.schema.enabled'), value: true }, - { title: this.$t('pages.database.resource.schema.disabled'), value: false }, - ], - modify: { - description: this.table.description, - is_public: this.table.is_public, - is_schema_public: this.table.is_schema_public - }, - cacheStore: useCacheStore() - } - }, - computed: { - database () { - return this.cacheStore.getDatabase - }, - inputVariant () { - const runtimeConfig = useRuntimeConfig() - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal - }, - buttonVariant () { - const runtimeConfig = useRuntimeConfig() - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal - }, - isChange () { - if (this.table.description !== this.modify.description) { - return true - } - if (this.table.is_public !== this.modify.is_public) { - return true - } - return this.table.is_schema_public !== this.modify.is_schema_public - }, - buttonColor () { - return !this.isChange ? null : 'warning' - } - }, - methods: { - submit () { - this.$refs.form.validate() - }, - cancel () { - this.$emit('close', { success: false }) - }, - update () { - this.loading = true - const tableService = useTableService() - tableService.update(this.$route.params.database_id, this.$route.params.table_id, this.modify) - .then(() => { - this.loading = false - const toast = useToastInstance() - toast.success(this.$t('success.table.updated')) - this.$emit('close', { success: true }) - this.cacheStore.reloadTable() - }) - .catch(({ code }) => { - this.loading = false - const toast = useToastInstance() - toast.error(this.$t(code)) - }) - .finally(() => { - this.loading = false - }) - } - } -} -</script> diff --git a/dbrepo-ui/components/table/TableToolbar.vue b/dbrepo-ui/components/table/TableToolbar.vue index 2840e2e4c43e545a98385bb42b993650940dc4a2..e2f1ad4b231c8fab498f7689d5ddfbea87298f18 100644 --- a/dbrepo-ui/components/table/TableToolbar.vue +++ b/dbrepo-ui/components/table/TableToolbar.vue @@ -45,22 +45,6 @@ :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')" class="mr-2" :to="`/database/${$route.params.database_id}/view/create?tid=${$route.params.table_id}`" /> - <v-btn - v-if="canUpdateTable" - class="mr-2" - variant="flat" - :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-table-edit' : null" - color="warning" - :text="($vuetify.display.lgAndUp ? $t('toolbars.database.update-table.xl') + ' ' : '') + $t('toolbars.database.update-table.permanent')" - @click="updateTableDialog = true" /> - <v-btn - v-if="canDropTable" - :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-delete' : null" - color="error" - variant="flat" - :text="($vuetify.display.lgAndUp ? 'Drop ' : '') + 'Table'" - class="mr-2" - @click="dropTableDialog = true" /> <v-btn v-if="canGetPid" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null" @@ -82,37 +66,24 @@ v-if="canViewSchema" :text="$t('navigation.schema')" :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/schema`" /> + <v-tab + v-if="canUpdateTable" + :text="$t('navigation.settings')" + :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/settings`" /> </v-tabs> </template> </v-toolbar> - <v-dialog - v-model="dropTableDialog" - max-width="640"> - <DropTable - @close="closeDelete" /> - </v-dialog> - <v-dialog - v-model="updateTableDialog" - max-width="640"> - <UpdateTable - :table="table" - @close="closeUpdate" /> - </v-dialog> </div> </template> <script> import EditTuple from '@/components/dialogs/EditTuple.vue' -import DropTable from '@/components/dialogs/DropTable.vue' -import UpdateTable from '@/components/dialogs/UpdateTable.vue' import { useCacheStore } from '@/stores/cache' import { useUserStore } from '@/stores/user' export default { components: { - EditTuple, - DropTable, - UpdateTable + EditTuple }, data () { return { @@ -121,7 +92,6 @@ export default { error: false, edit: false, dropTableDialog: false, - updateTableDialog: false, cacheStore: useCacheStore(), userStore: useUserStore() } @@ -161,16 +131,6 @@ export default { const userService = useUserService() return userService.hasReadAccess(this.access) && this.roles.includes('execute-query') }, - canDropTable () { - if (!this.roles || !this.table || !this.user) { - return false - } - if (this.roles.includes('delete-foreign-table')) { - return true - } - const tableService = useTableService() - return tableService.isOwner(this.table, this.user) && this.roles.includes('delete-table') && this.table.identifiers.length === 0 - }, canCreateView () { if (!this.roles || !this.table || !this.user) { return false @@ -217,27 +177,6 @@ export default { buttonVariant () { const runtimeConfig = useRuntimeConfig() return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal - }, - isContrastTheme () { - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') - }, - isDarkTheme () { - return this.$vuetify.theme.global.name.toLowerCase().startsWith('dark') - }, - colorVariant () { - return this.isContrastTheme ? '' : (this.isDarkTheme ? 'tertiary' : 'secondary') - }, - }, - methods: { - closeDelete ({success}) { - this.dropTableDialog = false - if (success) { - this.cacheStore.reloadDatabase() - this.$router.push(`/database/${this.$route.params.database_id}/table`) - } - }, - closeUpdate () { - this.updateTableDialog = false } } } diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index 53fec12c18ffac36b71817dae7d5f404323453d1..6eb0b81974d570ecf2e5176a06785d7717be421d 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -291,8 +291,13 @@ "read": "You can read all contents of this table" } }, - "visibility": { - "title": "Visibility", + "settings": { + "title": "Metadata", + "subtitle": "Optional table description for humans and visibility settings." + }, + "delete": { + "title": "Delete this table", + "subtitle": "This action deletes {table} and all data in it. There is no going back." }, "description": { "title": "Description", diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue new file mode 100644 index 0000000000000000000000000000000000000000..44133802b8327d4fdee2e787b09df354a9078451 --- /dev/null +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue @@ -0,0 +1,336 @@ +<template> + <div + v-if="canUpdateTable"> + <TableToolbar /> + <v-window + v-if="user" + v-model="tab"> + <v-window-item> + <v-form + ref="form" + v-model="valid" + autocomplete="off" + @submit.prevent="submit"> + <v-card + variant="flat" + rounded="0" + :title="$t('pages.table.settings.title')" + :subtitle="$t('pages.table.settings.subtitle')"> + <v-card-text> + <v-row> + <v-col + md="8"> + <v-textarea + v-model="modify.description" + rows="2" + :rules="[ + v => (!!v || v.length <= 180) || ($t('validation.max-length') + 180), + ]" + clearable + counter="180" + persistent-counter + persistent-hint + :variant="inputVariant" + :hint="$t('pages.table.subpages.import.description.hint')" + :label="$t('pages.table.subpages.import.description.label')"/> + </v-col> + </v-row> + <v-row + dense> + <v-col + md="4"> + <v-select + v-model="modify.is_public" + :items="dataOptions" + persistent-hint + :variant="inputVariant" + required + :rules="[ + v => v !== null || $t('validation.required') + ]" + :label="$t('pages.database.resource.data.label')" + :hint="$t('pages.database.resource.data.hint')" /> + </v-col> + <v-col + md="4"> + <v-select + v-model="modify.is_schema_public" + :items="schemaOptions" + persistent-hint + :variant="inputVariant" + required + :rules="[ + v => v !== null || $t('validation.required') + ]" + :label="$t('pages.database.resource.schema.label')" + :hint="$t('pages.database.resource.schema.hint')" /> + </v-col> + </v-row> + <v-row> + <v-col> + <v-btn + id="database" + variant="flat" + size="small" + :disabled="!valid || !isChange" + :color="buttonColor" + :loading="loading" + type="submit" + :text="$t('navigation.modify')" + @click="update" /> + </v-col> + </v-row> + </v-card-text> + </v-card> + </v-form> + <v-divider + v-if="canDropTable" /> + <v-card + v-if="canDropTable" + variant="flat" + rounded="0" + :title="$t('pages.table.delete.title')" + :subtitle="$t('pages.table.delete.subtitle', { table: table.internal_name })"> + <v-card-text> + <v-row> + <v-col + md="8"> + <v-btn + size="small" + variant="flat" + color="error" + @click="askDelete"> + Delete + </v-btn> + </v-col> + </v-row> + </v-card-text> + </v-card> + </v-window-item> + </v-window> + <v-breadcrumbs + :items="items" + class="pa-0 mt-2" /> + </div> +</template> + +<script> +import TableToolbar from '@/components/table/TableToolbar.vue' +import { useUserStore } from '@/stores/user' +import { useCacheStore } from '@/stores/cache' + +export default { + components: { + TableToolbar + }, + data () { + return { + tab: 0, + valid: null, + loading: false, + modify: { + description: '', + is_public: null, + is_schema_public: null + }, + dataOptions: [ + { title: this.$t('pages.database.resource.data.enabled'), value: true }, + { title: this.$t('pages.database.resource.data.disabled'), value: false }, + ], + schemaOptions: [ + { title: this.$t('pages.database.resource.schema.enabled'), value: true }, + { title: this.$t('pages.database.resource.schema.disabled'), value: false }, + ], + items: [ + { + title: this.$t('navigation.databases'), + to: '/database' + }, + { + title: `${this.$route.params.database_id}`, + to: `/database/${this.$route.params.database_id}/info` + }, + { + title: this.$t('navigation.tables'), + to: `/database/${this.$route.params.database_id}/table` + }, + { + title: `${this.$route.params.table_id}`, + to: `/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}` + }, + { + title: this.$t('navigation.schema'), + to: `/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/schema`, + disabled: true + } + ], + headers: [ + { value: 'internal_name', title: this.$t('pages.table.subpages.schema.internal-name.title') }, + { value: 'column_type', title: this.$t('pages.table.subpages.schema.column-type.title') }, + { value: 'extra', title: this.$t('pages.table.subpages.schema.extra.title') }, + { value: 'column_concept', title: this.$t('pages.table.subpages.schema.concept.title') }, + { value: 'column_unit', title: this.$t('pages.table.subpages.schema.unit.title') }, + { value: 'is_null_allowed', title: this.$t('pages.table.subpages.schema.nullable.title') }, + { value: 'description', title: this.$t('pages.table.subpages.schema.description.title') }, + ], + dateColumns: [], + userStore: useUserStore(), + cacheStore: useCacheStore() + } + }, + computed: { + user () { + return this.userStore.getUser + }, + database () { + return this.cacheStore.getDatabase + }, + table () { + return this.cacheStore.getTable + }, + access () { + return this.userStore.getAccess + }, + hasReadAccess () { + if (!this.access) { + return false + } + return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own' + }, + roles () { + return this.userStore.getRoles + }, + isChange () { + if (!this.table) { + return false + } + if (this.table.is_public !== this.modify.is_public) { + return true + } + return this.table.is_schema_public !== this.modify.is_schema_public + }, + canUpdateTable () { + if (!this.roles || !this.user || !this.table) { + return false + } + return this.roles.includes('update-table') && this.table.owner.id === this.user.id + }, + canModifyVisibility () { + if (!this.roles || !this.user || !this.table) { + return false + } + return this.roles.includes('update-table') && this.table.owner.id === this.user.id + }, + canDropTable () { + if (!this.roles || !this.table || !this.user) { + return false + } + if (this.roles.includes('delete-foreign-table')) { + return true + } + const tableService = useTableService() + return tableService.isOwner(this.table, this.user) && this.roles.includes('delete-table') && this.table.identifiers.length === 0 + }, + inputVariant () { + const runtimeConfig = useRuntimeConfig() + return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal + }, + buttonVariant () { + const runtimeConfig = useRuntimeConfig() + return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal + }, + buttonColor () { + return !this.isChange ? null : 'warning' + } + }, + mounted() { + if (!this.table) { + return + } + this.modify.is_public = this.table.is_public + this.modify.is_schema_public = this.table.is_schema_public + this.modify.description = this.table.description + }, + methods: { + submit () { + this.$refs.form.validate() + }, + extra (column) { + if (column.column_type === 'float') { + return `precision=${column.size}` + } else if (['decimal', 'double'].includes(column.column_type)) { + let extra = '' + if (column.size !== null) { + extra += `size=${column.size}` + } + if (column.d !== null) { + if (extra.length > 0) { + extra += ', ' + } + extra += `d=${column.d}` + } + return extra + } else if (column.column_type === 'enum') { + return `(${column.enums.join(', ')})` + } else if (column.column_type === 'set') { + return `(${column.sets.join(', ')})` + } else if (['int', 'char', 'varchar', 'binary', 'varbinary', 'tinyint', 'size="small"int', 'mediumint', 'bigint'].includes(column.column_type)) { + return column.size !== null ? `size=${column.size}` : '' + } + return null + }, + closed (event) { + const { success } = event + console.debug('closed dialog', event) + if (success) { + const toast = useToastInstance() + toast.success(this.$t('success.table.semantics')) + this.cacheStore.reloadTable() + } + this.dialogSemantic = false + }, + update () { + this.loading = true + const tableService = useTableService() + tableService.update(this.$route.params.database_id, this.$route.params.table_id, this.modify) + .then(() => { + this.loading = false + const toast = useToastInstance() + toast.success(this.$t('success.table.updated')) + this.$emit('close', { success: true }) + this.cacheStore.reloadTable() + }) + .catch(({ code }) => { + this.loading = false + const toast = useToastInstance() + toast.error(this.$t(code)) + }) + .finally(() => { + this.loading = false + }) + }, + askDelete () { + if (!confirm(this.$t('pages.table.delete.subtitle', { table: this.table.internal_name }))) { + return + } + this.loadingDelete = true + const tableService = useTableService() + tableService.remove(this.database.id, this.table.id) + .then(() => { + console.info('Deleted table with id ', this.table.id) + this.cacheStore.reloadDatabase() + const toast = useToastInstance() + toast.success('Successfully deleted table with id ' + this.table.id) + this.$router.push(`/database/${this.$route.params.database_id}/table`) + }) + .catch(({code, message}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) + }) + .finally(() => { + this.loadingDelete = false + }) + } + } +} +</script>