1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

feat: stored routines creation

This commit is contained in:
2021-01-06 11:57:49 +01:00
parent aa33850286
commit 3bcd02fc4e
9 changed files with 269 additions and 84 deletions

View File

@ -69,75 +69,81 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.context { .context {
display: flex;
color: $body-font-color;
font-size: 16px;
z-index: 400;
justify-content: center;
align-items: center;
overflow: hidden;
position: fixed;
height: 100vh;
right: 0;
top: 0;
left: 0;
bottom: 0;
.context-container {
min-width: 100px;
z-index: 10;
box-shadow: 0 0 2px 0 #000;
padding: 0;
background: #1d1d1d;
border-radius: 0.1rem;
display: flex; display: flex;
color: $body-font-color; flex-direction: column;
font-size: 16px; position: absolute;
z-index: 400; pointer-events: initial;
justify-content: center;
align-items: center;
overflow: hidden;
position: fixed;
height: 100vh;
right: 0;
top: 0;
left: 0;
bottom: 0;
.context-container { .context-element {
min-width: 100px;
z-index: 10;
box-shadow: 0 0 2px 0 #000;
padding: 0;
background: #1d1d1d;
border-radius: 0.1rem;
display: flex; display: flex;
flex-direction: column; align-items: center;
position: absolute; padding: 0.1rem 0.3rem;
pointer-events: initial; cursor: pointer;
justify-content: space-between;
position: relative;
.context-element { .context-submenu {
display: flex; opacity: 0;
align-items: center; visibility: hidden;
padding: 0.1rem 0.3rem; transition: opacity 0.2s;
cursor: pointer; position: absolute;
justify-content: space-between; left: 100%;
position: relative; top: 0;
background: #1d1d1d;
box-shadow: 0 0 2px 0 #000;
min-width: 100px;
}
&:hover {
background: $primary-color;
.context-submenu { .context-submenu {
opacity: 0; display: block;
visibility: hidden; visibility: visible;
transition: opacity 0.2s; opacity: 1;
position: absolute;
left: 100%;
top: 0;
background: #1d1d1d;
box-shadow: 0 0 2px 0 #000;
min-width: 100px;
}
&:hover {
background: $primary-color;
.context-submenu {
display: block;
visibility: visible;
opacity: 1;
}
} }
} }
} }
.context-overlay {
background: transparent;
bottom: 0;
cursor: default;
display: block;
left: 0;
position: absolute;
right: 0;
top: 0;
}
} }
.context-overlay {
background: transparent;
bottom: 0;
cursor: default;
display: block;
left: 0;
position: absolute;
right: 0;
top: 0;
}
}
.disabled {
pointer-events: none;
filter: grayscale(100%);
opacity: 0.5;
}
</style> </style>

View File

@ -0,0 +1,147 @@
<template>
<ConfirmModal
:confirm-text="$t('word.confirm')"
size="400"
@confirm="confirmNewRoutine"
@hide="$emit('close')"
>
<template :slot="'header'">
<div class="d-flex">
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('message.createNewRoutine') }}
</div>
</template>
<div :slot="'body'">
<form class="form-horizontal">
<div class="form-group">
<label class="form-label col-4">
{{ $t('word.name') }}
</label>
<div class="column">
<input
ref="firstInput"
v-model="localRoutine.name"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<label class="form-label col-4">
{{ $t('word.definer') }}
</label>
<div class="column">
<select
v-if="workspace.users.length"
v-model="localRoutine.definer"
class="form-select"
>
<option value="">
{{ $t('message.currentUser') }}
</option>
<option
v-for="user in workspace.users"
:key="`${user.name}@${user.host}`"
:value="`\`${user.name}\`@\`${user.host}\``"
>
{{ user.name }}@{{ user.host }}
</option>
</select>
<select v-if="!workspace.users.length" class="form-select">
<option value="">
{{ $t('message.currentUser') }}
</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label col-4">
{{ $t('word.comment') }}
</label>
<div class="column">
<input
v-model="localRoutine.comment"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<label class="form-label col-4">
{{ $t('message.sqlSecurity') }}
</label>
<div class="column">
<select v-model="localRoutine.security" class="form-select">
<option>DEFINER</option>
<option>INVOKER</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label col-4">
{{ $t('message.dataAccess') }}
</label>
<div class="column">
<select v-model="localRoutine.dataAccess" class="form-select">
<option>CONTAINS SQL</option>
<option>NO SQL</option>
<option>READS SQL DATA</option>
<option>MODIFIES SQL DATA</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-4" />
<div class="column">
<label class="form-checkbox form-inline">
<input v-model="localRoutine.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
</label>
</div>
</div>
</form>
</div>
</ConfirmModal>
</template>
<script>
import ConfirmModal from '@/components/BaseConfirmModal';
export default {
name: 'ModalNewRoutine',
components: {
ConfirmModal
},
props: {
workspace: Object
},
data () {
return {
localRoutine: {
definer: '',
sql: 'BEGIN\r\n\r\nEND',
parameters: [],
name: '',
comment: '',
security: 'DEFINER',
deterministic: false,
dataAccess: 'CONTAINS SQL'
},
isOptionsChanging: false
};
},
computed: {
schema () {
return this.workspace.breadcrumbs.schema;
}
},
mounted () {
setTimeout(() => {
this.$refs.firstInput.focus();
}, 20);
},
methods: {
confirmNewRoutine () {
this.$emit('open-create-routine-editor', this.localRoutine);
}
}
};
</script>

View File

@ -2,7 +2,7 @@
<ConfirmModal <ConfirmModal
:confirm-text="$t('word.confirm')" :confirm-text="$t('word.confirm')"
size="400" size="400"
@confirm="confirmOptionsChange" @confirm="confirmNewTrigger"
@hide="$emit('close')" @hide="$emit('close')"
> >
<template :slot="'header'"> <template :slot="'header'">
@ -132,7 +132,7 @@ export default {
}, 20); }, 20);
}, },
methods: { methods: {
confirmOptionsChange () { confirmNewTrigger () {
this.$emit('open-create-trigger-editor', this.localTrigger); this.$emit('open-create-trigger-editor', this.localTrigger);
} }
} }

View File

@ -67,6 +67,12 @@
@close="hideCreateTriggerModal" @close="hideCreateTriggerModal"
@open-create-trigger-editor="openCreateTriggerEditor" @open-create-trigger-editor="openCreateTriggerEditor"
/> />
<ModalNewRoutine
v-if="isNewRoutineModal"
:workspace="workspace"
@close="hideCreateRoutineModal"
@open-create-routine-editor="openCreateRoutineEditor"
/>
<DatabaseContext <DatabaseContext
v-if="isDatabaseContext" v-if="isDatabaseContext"
:selected-database="selectedDatabase" :selected-database="selectedDatabase"
@ -75,6 +81,7 @@
@show-create-table-modal="showCreateTableModal" @show-create-table-modal="showCreateTableModal"
@show-create-view-modal="showCreateViewModal" @show-create-view-modal="showCreateViewModal"
@show-create-trigger-modal="showCreateTriggerModal" @show-create-trigger-modal="showCreateTriggerModal"
@show-create-routine-modal="showCreateRoutineModal"
@reload="refresh" @reload="refresh"
/> />
<TableContext <TableContext
@ -97,9 +104,12 @@
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import _ from 'lodash';// TODO: remove import _ from 'lodash';// TODO: remove
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import Views from '@/ipc-api/Views'; import Views from '@/ipc-api/Views';
import Triggers from '@/ipc-api/Triggers'; import Triggers from '@/ipc-api/Triggers';
import Routines from '@/ipc-api/Routines';
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel'; import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase'; import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase';
import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext'; import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext';
@ -109,6 +119,7 @@ import ModalNewDatabase from '@/components/ModalNewDatabase';
import ModalNewTable from '@/components/ModalNewTable'; import ModalNewTable from '@/components/ModalNewTable';
import ModalNewView from '@/components/ModalNewView'; import ModalNewView from '@/components/ModalNewView';
import ModalNewTrigger from '@/components/ModalNewTrigger'; import ModalNewTrigger from '@/components/ModalNewTrigger';
import ModalNewRoutine from '@/components/ModalNewRoutine';
export default { export default {
name: 'WorkspaceExploreBar', name: 'WorkspaceExploreBar',
@ -121,7 +132,8 @@ export default {
ModalNewDatabase, ModalNewDatabase,
ModalNewTable, ModalNewTable,
ModalNewView, ModalNewView,
ModalNewTrigger ModalNewTrigger,
ModalNewRoutine
}, },
props: { props: {
connection: Object, connection: Object,
@ -135,6 +147,7 @@ export default {
isNewTableModal: false, isNewTableModal: false,
isNewViewModal: false, isNewViewModal: false,
isNewTriggerModal: false, isNewTriggerModal: false,
isNewRoutineModal: false,
localWidth: null, localWidth: null,
isDatabaseContext: false, isDatabaseContext: false,
@ -308,6 +321,29 @@ export default {
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
},
showCreateRoutineModal () {
this.closeDatabaseContext();
this.isNewRoutineModal = true;
},
hideCreateRoutineModal () {
this.isNewRoutineModal = false;
},
async openCreateRoutineEditor (payload) {
const params = {
uid: this.connection.uid,
...payload
};
const { status, response } = await Routines.createRoutine(params);
if (status === 'success') {
await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, procedure: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
}
else
this.addNotification({ status: 'error', message: response });
} }
} }
}; };

View File

@ -16,13 +16,13 @@
<div class="context-element" @click="showCreateTriggerModal"> <div class="context-element" @click="showCreateTriggerModal">
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
</div> </div>
<div class="context-element" @click="false"> <div class="context-element" @click="showCreateRoutineModal">
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
</div> </div>
<div class="context-element" @click="false"> <div class="context-element disabled" @click="false">
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
</div> </div>
<div class="context-element" @click="false"> <div class="context-element disabled" @click="false">
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
</div> </div>
</div> </div>
@ -105,6 +105,9 @@ export default {
showCreateTriggerModal () { showCreateTriggerModal () {
this.$emit('show-create-trigger-modal'); this.$emit('show-create-trigger-modal');
}, },
showCreateRoutineModal () {
this.$emit('show-create-routine-modal');
},
showDeleteModal () { showDeleteModal () {
this.isDeleteModal = true; this.isDeleteModal = true;
}, },

View File

@ -4,11 +4,11 @@
@close-context="closeContext" @close-context="closeContext"
> >
<div <div
v-if="selectedMisc.type === 'table'" v-if="selectedMisc.type === 'procedure'"
class="context-element" class="context-element disabled"
@click="showEmptyModal" @click="showRunModal"
> >
<span class="d-flex"><i class="mdi mdi-18px mdi-table-off text-light pr-1" /> {{ $t('message.emptyTable') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
</div> </div>
<div class="context-element" @click="showDeleteModal"> <div class="context-element" @click="showDeleteModal">
<span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ $t('word.delete') }}</span> <span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
@ -52,7 +52,7 @@ export default {
data () { data () {
return { return {
isDeleteModal: false, isDeleteModal: false,
isEmptyModal: false isRunModal: false
}; };
}, },
computed: { computed: {
@ -88,11 +88,11 @@ export default {
hideDeleteModal () { hideDeleteModal () {
this.isDeleteModal = false; this.isDeleteModal = false;
}, },
showEmptyModal () { showRunModal () {
this.isEmptyModal = true; this.isRunModal = true;
}, },
hideEmptyModal () { hideRunModal () {
this.isEmptyModal = false; this.isRunModal = false;
}, },
closeContext () { closeContext () {
this.$emit('close-context'); this.$emit('close-context');

View File

@ -25,7 +25,7 @@
<div class="divider-vert py-3" /> <div class="divider-vert py-3" />
<button <button
class="btn btn-dark btn-sm" class="btn btn-dark btn-sm disabled"
:disabled="isChanged" :disabled="isChanged"
@click="false" @click="false"
> >

View File

@ -77,11 +77,3 @@ export default {
} }
}; };
</script> </script>
<style lang="scss" scoped>
.disabled {
pointer-events: none;
filter: grayscale(100%);
opacity: 0.5;
}
</style>

View File

@ -157,6 +157,7 @@ module.exports = {
dataAccess: 'Data access', dataAccess: 'Data access',
thereAreNoParameters: 'There are no parameters', thereAreNoParameters: 'There are no parameters',
createNewParameter: 'Create new parameter', createNewParameter: 'Create new parameter',
createNewRoutine: 'Create new stored routine',
deleteRoutine: 'Delete stored routine' deleteRoutine: 'Delete stored routine'
}, },
// Date and Time // Date and Time