refactor(gui): linting

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
This commit is contained in:
Pranav C
2021-07-19 16:30:37 +05:30
parent f9508c2d44
commit fa00be39b8
303 changed files with 34924 additions and 31761 deletions

View File

@@ -1,286 +1,323 @@
<!-- eslint-disable -->
<template>
<div>
<v-card style="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload ACL"
color="primary"
small
v-ge="['acl-gql','reload']"
@click="aclInit"
<v-spacer />
<x-btn
v-ge="['acl-gql','reload']"
outlined
tooltip="Reload ACL"
color="primary"
small
@click="aclInit"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small v-ge="['acl-gql','open-folder']"
color="primary"
@click="openFolder">
<x-btn
v-ge="['acl-gql','open-folder']"
tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small
color="primary"
@click="openFolder"
>
Open Folder
</x-btn>
<x-btn outlined tooltip="Save ACL"
color="primary"
class="primary"
small
@click="save"
:disabled="disableSaveButton"
v-ge="['acl-gql','save']">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['acl-gql','save']"
outlined
tooltip="Save ACL"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click="save"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search"
outlined
/>
<v-simple-table v-if="data1" dense>
<thead>
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-1 flex-shrink-1" dense
v-model="allToggle"></v-checkbox>
</template>
<span>{{allToggle ? 'Disable' : 'Enable'}} all {{nodes.tn}} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{nodes.tn}} Resolvers</span>
</div>
</th>
<th v-for="role in roles"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex align-center justify-center">
<span>{{role}}</span>
</div>
</th>
</tr>
<tr>
<th v-for="role in roles"
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="allToggle"
v-ge="['acl-gql','open-folder']"
small
class="mt-1 flex-shrink-1"
dense
v-on="on"
/>
</template>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{ nodes.tn }} Resolvers</span>
</div>
</th>
<th
v-for="role in roles"
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<th
v-for="role in roles"
class="pa-1"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0" dense v-model="columnToggle[role]"
@change="toggleColumn(role,columnToggle[role])"></v-checkbox>
</template>
<span>
<span>{{columnToggle[role] ? 'Disable' : 'Enable'}} all resolvers for {{role}}</span></span>
</v-tooltip>
</div>
</th>
</tr>
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="columnToggle[role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
v-on="on"
@change="toggleColumn(role,columnToggle[role])"
/>
</template>
<span>
<span>{{ columnToggle[role] ? 'Disable' : 'Enable' }} all resolvers for {{ role }}</span></span>
</v-tooltip>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td width="20" class="pl-6 pr-3">
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td width="20" class="pl-6 pr-3">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="rowToggle[path]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0 ml-3"
dense
v-on="on"
@change="toggleRow(path,rowToggle[path])"
/>
</template>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0 ml-3" v-model="rowToggle[path]"
@change="toggleRow(path,rowToggle[path])"
dense></v-checkbox>
</template>
<span>{{rowToggle[path] ? 'Disable' : 'Enable'}} this resolver for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{path}}</span>
</template>
<span>{{path}}</span>
</v-tooltip>
</td>
<template v-for="(role,i) in roles">
<td style="border-left: 1px solid grey" class="pa-1" :key="`${path}_${role}`">
<div class="d-flex justify-center">
<v-checkbox
small v-ge="['acl-gql','open-folder']" class="mt-0" dense
v-model="data1[path][role]"
@change="toggleCell(path,role,data1[path][role])"
></v-checkbox>
</div>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this resolver for all roles</span>
</v-tooltip>
</td>
</template>
</tr>
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(role,i) in roles">
<td :key="`${path}_${role}`" style="border-left: 1px solid grey" class="pa-1">
<div class="d-flex justify-center">
<v-checkbox
v-model="data1[path][role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
@change="toggleCell(path,role,data1[path][role])"
/>
</div>
</td>
</template>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
export default {
name: 'AclGql',
export default {
name: "acl-gql",
props: ['nodes'],
data () {
return {
disableSaveButton: true,
search: '',
policyPath: '',
columnToggle: {},
rowToggle: {},
roles: [
'creator',
'editor',
'guest'
],
data1: null
}
},
methods: {
openFolder () {
// shell.openItem(path.dirname(this.policyPath))
},
toggleColumn (role, checked) {
for (const [resolver, roles] of Object.entries(this.data1)) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow (resolver, checked) {
for (const role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll (checked) {
this.disableSaveButton = false
for (const path in this.data1) {
this.rowToggle[path] = checked
}
for (const role of this.roles) {
this.columnToggle[role] = checked
}
props: ["nodes"],
methods: {
openFolder() {
shell.openItem(path.dirname(this.policyPath));
},
toggleColumn(role, checked) {
for (let [resolver, roles] of Object.entries(this.data1)) {
for (const roles of Object.values(this.data1)) {
for (const role of this.roles) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow(resolver, checked) {
for (let role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll(checked) {
this.disableSaveButton = false;
for (let path in this.data1) {
this.rowToggle[path] = checked;
}
for (let role of this.roles) {
this.columnToggle[role] = checked;
}
for (let roles of Object.values(this.data1)) {
for (let role of this.roles) {
this.$set(roles, role, checked)
}
}
},
toggleCell(resolver, role, checked) {
this.disableSaveButton = false;
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]));
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled));
},
initColumnCheckBox() {
for (let role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role]);
}
},
initRowCheckBox() {
for (let path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
return v;
}).length;
}
},
async aclInit() {
this.disableSaveButton = true;
this.policyPath = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'projectGetGqlPolicyPath', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}]);
try {
console.log(this.policyPath, this.data1)
this.data1 = JSON.parse(
JSON.stringify(
(
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', {path: this.policyPath}])
).permissions
)
);
this.initColumnCheckBox();
this.initRowCheckBox();
} catch (e) {
console.log(e)
}
},
async save() {
try {
// await this.sqlMgr.writeFile({
// path: this.policyPath,
// data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
// });
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
}]);
this.disableSaveButton = true;
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000);
} catch (e) {
console.log(e);
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000);
}
}
},
data() {
return {
disableSaveButton: true,
search: '',
policyPath: '',
columnToggle: {},
rowToggle: {},
roles: [
'creator',
'editor',
'guest'
],
data1: null
toggleCell (resolver, role, checked) {
this.disableSaveButton = false
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]))
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled))
},
initColumnCheckBox () {
for (const role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role])
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
allToggle: {
get() {
return this.data1 && Object.values(this.data1).some(roles => Object.values(roles).some(v => v))
},
set(checked) {
this.toggleAll(checked)
}
initRowCheckBox () {
for (const path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) { this.roles = [...this.roles, role] }
return v
}).length
}
},
async created() {
await this.aclInit();
async aclInit () {
this.disableSaveButton = true
this.policyPath = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'projectGetGqlPolicyPath', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}])
try {
console.log(this.policyPath, this.data1)
this.data1 = JSON.parse(
JSON.stringify(
(
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', { path: this.policyPath }])
).permissions
)
)
this.initColumnCheckBox()
this.initRowCheckBox()
} catch (e) {
console.log(e)
}
},
watch: {}
async save () {
try {
// await this.sqlMgr.writeFile({
// path: this.policyPath,
// data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
// });
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
}])
this.disableSaveButton = true
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000)
}
}
},
computed: {
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
allToggle: {
get () {
return this.data1 && Object.values(this.data1).some(roles => Object.values(roles).some(v => v))
},
set (checked) {
this.toggleAll(checked)
}
}
},
watch: {},
async created () {
await this.aclInit()
}
}
</script>
<style scoped>

View File

@@ -1,272 +1,204 @@
<!-- eslint-disable -->
<template>
<div>
<v-card style="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload ACL"
color="primary"
small
v-ge="['acl-gql','reload']"
@click="aclInit"
<v-spacer />
<x-btn
v-ge="['acl-gql','reload']"
outlined
tooltip="Reload ACL"
color="primary"
small
@click="aclInit"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small v-ge="['acl-gql','open-folder']"
color="primary"
@click="openFolder">
<x-btn
v-ge="['acl-gql','open-folder']"
tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small
color="primary"
@click="openFolder"
>
Open Folder
</x-btn>
<x-btn outlined tooltip="Save ACL"
color="primary"
class="primary"
small
@click="save"
:disabled="disableSaveButton"
v-ge="['acl-gql','save']">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['acl-gql','save']"
outlined
tooltip="Save ACL"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click="save"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search"
outlined
/>
<v-simple-table v-if="policies && policies.length" dense>
<thead>
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-1 flex-shrink-1" dense
v-model="allToggle"></v-checkbox>
</template>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{ nodes.tn }} RPC Services</span>
</div>
</th>
<th v-for="role in roles"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<th v-for="role in roles"
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="allToggle"
v-ge="['acl-gql','open-folder']"
small
class="mt-1 flex-shrink-1"
dense
v-on="on"
/>
</template>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{ nodes.tn }} RPC Services</span>
</div>
</th>
<th
v-for="role in roles"
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<th
v-for="role in roles"
class="pa-1"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0" dense v-model="columnToggle[role]"
@change="toggleColumn(role,columnToggle[role])"></v-checkbox>
</template>
<span>
<span>{{ columnToggle[role] ? 'Disable' : 'Enable' }} all resolvers for {{ role }}</span></span>
</v-tooltip>
</div>
</th>
</tr>
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="columnToggle[role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
v-on="on"
@change="toggleColumn(role,columnToggle[role])"
/>
</template>
<span>
<span>{{ columnToggle[role] ? 'Disable' : 'Enable' }} all resolvers for {{ role }}</span></span>
</v-tooltip>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td width="20" class="pl-6 pr-3">
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td width="20" class="pl-6 pr-3">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="rowToggle[path]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0 ml-3"
dense
v-on="on"
@change="toggleRow(path,rowToggle[path])"
/>
</template>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0 ml-3" v-model="rowToggle[path]"
@change="toggleRow(path,rowToggle[path])"
dense></v-checkbox>
</template>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this resolver for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(role,i) in roles">
<td style="border-left: 1px solid grey" class="pa-1" :key="`${path}_${role}`">
<div class="d-flex justify-center">
<v-checkbox
small v-ge="['acl-gql','open-folder']" class="mt-0" dense
v-model="data1[path][role]"
@change="toggleCell(path,role,data1[path][role])"
></v-checkbox>
</div>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this resolver for all roles</span>
</v-tooltip>
</td>
</template>
</tr>
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(role,i) in roles">
<td :key="`${path}_${role}`" style="border-left: 1px solid grey" class="pa-1">
<div class="d-flex justify-center">
<v-checkbox
v-model="data1[path][role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
@change="toggleCell(path,role,data1[path][role])"
/>
</div>
</td>
</template>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else-if="policies" outlined type="info">Permission file not found</v-alert>
<v-alert v-else-if="policies" outlined type="info">
Permission file not found
</v-alert>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
export default {
name: "acl-grpc-db",
name: 'AclGrpcDb',
props: ["nodes"],
methods: {
openFolder() {
shell.openItem(path.dirname(this.policyPath));
},
toggleColumn(role, checked) {
for (let [resolver, roles] of Object.entries(this.data1)) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow(resolver, checked) {
for (let role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll(checked) {
this.disableSaveButton = false;
for (let path in this.data1) {
this.rowToggle[path] = checked;
}
for (let role of this.roles) {
this.columnToggle[role] = checked;
}
for (let roles of Object.values(this.data1)) {
for (let role of this.roles) {
this.$set(roles, role, checked)
}
}
},
toggleCell(resolver, role, checked) {
this.disableSaveButton = false;
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]));
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled));
},
initColumnCheckBox() {
for (let role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role]);
}
},
initRowCheckBox() {
for (let path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
return v;
}).length;
}
},
async aclInit() {
try {
console.log(this.sqlMgr)
this.disableSaveButton = true;
// this.policies = (await this.sqlMgr.projectGetGrpcPolicyFromDb({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// })).data.list;
this.policies = (await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'projectGetGrpcPolicyFromDb', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}])).data.list;
//.data.list;
this.data = JSON.parse(JSON.stringify(this.policies.filter(({service}) => service)));
this.data1 = this.data.reduce((aclObj, resolver) => {
aclObj[resolver.service] = resolver.acl;
return aclObj;
}, {});
this.initColumnCheckBox();
this.initRowCheckBox();
} catch (e) {
console.log(e)
}
},
async save() {
try {
//
// await this.sqlMgr.xcRpcPolicyUpdate({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn,
// data: this.data
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcRpcPolicyUpdate', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn,
data: this.data
}])
this.disableSaveButton = true;
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000);
} catch (e) {
console.log(e);
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000);
}
}
},
data() {
props: ['nodes'],
data () {
return {
disableSaveButton: true,
search: '',
@@ -282,21 +214,123 @@ export default {
data1: null
}
},
methods: {
openFolder () {
// shell.openItem(path.dirname(this.policyPath))
},
toggleColumn (role, checked) {
for (const [resolver, roles] of Object.entries(this.data1)) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow (resolver, checked) {
for (const role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll (checked) {
this.disableSaveButton = false
for (const path in this.data1) {
this.rowToggle[path] = checked
}
for (const role of this.roles) {
this.columnToggle[role] = checked
}
for (const roles of Object.values(this.data1)) {
for (const role of this.roles) {
this.$set(roles, role, checked)
}
}
},
toggleCell (resolver, role, checked) {
this.disableSaveButton = false
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]))
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled))
},
initColumnCheckBox () {
for (const role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role])
}
},
initRowCheckBox () {
for (const path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) { this.roles = [...this.roles, role] }
return v
}).length
}
},
async aclInit () {
try {
console.log(this.sqlMgr)
this.disableSaveButton = true
// this.policies = (await this.sqlMgr.projectGetGrpcPolicyFromDb({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// })).data.list;
this.policies = (await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'projectGetGrpcPolicyFromDb', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}])).data.list
// .data.list;
this.data = JSON.parse(JSON.stringify(this.policies.filter(({ service }) => service)))
this.data1 = this.data.reduce((aclObj, resolver) => {
aclObj[resolver.service] = resolver.acl
return aclObj
}, {})
this.initColumnCheckBox()
this.initRowCheckBox()
} catch (e) {
console.log(e)
}
},
async save () {
try {
//
// await this.sqlMgr.xcRpcPolicyUpdate({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn,
// data: this.data
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcRpcPolicyUpdate', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn,
data: this.data
}])
this.disableSaveButton = true
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000)
}
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
allToggle: {
get() {
get () {
return this.data1 && Object.values(this.data1).some(roles => Object.values(roles).some(v => v))
},
set(checked) {
set (checked) {
this.toggleAll(checked)
}
}
},
async mounted() {
await this.aclInit();
},
watch: {}
watch: {},
async mounted () {
await this.aclInit()
}
}
</script>

View File

@@ -1,328 +1,371 @@
<!-- eslint-disable -->
<template>
<div>
<v-card style="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload ACL"
color="primary"
small
v-ge="['acl','reload']"
@click="aclInit"
<v-spacer />
<x-btn
v-ge="['acl','reload']"
outlined
tooltip="Reload ACL"
color="primary"
small
@click="aclInit"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small
color="primary"
v-ge="['acl','open-folder']"
@click="openFolder">
<x-btn
v-ge="['acl','open-folder']"
tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small
color="primary"
@click="openFolder"
>
Open Folder
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
class="primary"
small
@click="save"
:disabled="disableSaveButton"
v-ge="['acl','save']">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['acl','save']"
outlined
tooltip="Save Changes"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click="save"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${nodes.tn} routes`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${nodes.tn} routes`"
prepend-inner-icon="search"
outlined
/>
<v-simple-table v-if="data1" dense>
<thead>
<tr>
<th colspan="2" class="text-center" rowspan="3">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-1 flex-shrink-1" dense
v-model="allToggle"></v-checkbox>
</template>
<span>{{allToggle ? 'Disable' : 'Enable'}} all {{nodes.tn}} routes for all roles</span>
</v-tooltip>
<span class="title">{{nodes.tn}} Routes</span>
</div>
</th>
<th v-for="role in roles" :colspan="methods.length"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex align-center justify-center">
<span>{{role}}</span>
</div>
</th>
</tr>
<tr>
<!-- <th colspan="2"></th>-->
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th width="25" class="caption px-1" :key="`${method}_${role}`"
:style="i ? '' : 'border-left: 1px solid grey'">{{method}}
</th>
</template>
</template>
</tr>
<tr>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th width="25" class="caption px-1" :key="`${method}_${role}`"
:style="i ? '' : 'border-left: 1px solid grey'">
<tr>
<th colspan="2" class="text-center" rowspan="3">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-0" dense
v-model="columnToggle[`${method}_${role}`]"
@change="toggleColumn(role,method,columnToggle[`${method}_${role}`])"></v-checkbox>
v-model="allToggle"
v-ge="['acl','toggle-checkbox']"
class="mt-1 flex-shrink-1"
dense
v-on="on"
/>
</template>
<span>{{columnToggle[`${method}_${role}`] ? 'Disable' : 'Enable'}} all {{method}} routes for {{role}}</span>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} routes for all roles</span>
</v-tooltip>
</th>
<span class="title">{{ nodes.tn }} Routes</span>
</div>
</th>
<th
v-for="role in roles"
:key="role"
:colspan="methods.length"
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<!-- <th colspan="2"></th>-->
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th
:key="`${method}_${role}`"
width="25"
class="caption px-1"
:style="i ? '' : 'border-left: 1px solid grey'"
>
{{ method }}
</th>
</template>
</template>
</template>
</tr>
</tr>
<tr>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th
:key="`${method}_${role}`"
width="25"
class="caption px-1"
:style="i ? '' : 'border-left: 1px solid grey'"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="columnToggle[`${method}_${role}`]"
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
v-on="on"
@change="toggleColumn(role,method,columnToggle[`${method}_${role}`])"
/>
</template>
<span>{{ columnToggle[`${method}_${role}`] ? 'Disable' : 'Enable' }} all {{ method }} routes for {{ role }}</span>
</v-tooltip>
</th>
</template>
</template>
</tr>
</thead>
<tbody>
<tr v-for="(route,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td width="20" class="px-0">
<tr v-for="(route,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1" :key="path">
<td width="20" class="px-0">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="rowToggle[path]"
v-ge="['acl','toggle-checkbox']"
class="mt-0 ml-3"
dense
v-on="on"
@change="toggleRow(path,rowToggle[path])"
/>
</template>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-0 ml-3" v-model="rowToggle[path]"
@change="toggleRow(path,rowToggle[path])"
dense></v-checkbox>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this route for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<td :key="`${path}_${method}_${role}`" :style="i ? '' : 'border-left: 1px solid grey'" class="pa-1">
<v-checkbox
v-if="data1[path][method]"
v-model="data1[path][method][role]"
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
:color="methodColor[method]"
:input-value="data1[path][method][role]"
@change="toggleCell(path,method,role,data1[path][method][role])"
/>
<span
v-else
@dblclick="$set(data1[path],method , {})"
>
<v-checkbox
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
:disabled="true"
/></span>
</td>
</template>
<span>{{rowToggle[path] ? 'Disable' : 'Enable'}} this route for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{path}}</span>
</template>
<span>{{path}}</span>
</v-tooltip>
</td>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<td :style="i ? '' : 'border-left: 1px solid grey'" class="pa-1" :key="`${path}_${method}_${role}`">
<v-checkbox
v-ge="['acl','toggle-checkbox']"
v-if="data1[path][method]" class="mt-0" dense
:color="methodColor[method]"
:input-value="data1[path][method][role]"
v-model="data1[path][method][role]"
@change="toggleCell(path,method,role,data1[path][method][role])"
></v-checkbox>
<span
v-else
@dblclick="$set(data1[path],method , {})">
<v-checkbox v-ge="['acl','toggle-checkbox']" class="mt-0" dense
:disabled="true">
</v-checkbox></span>
</td>
</template>
</template>
</tr>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
// const {fs, importFresh,shell,path} = require("electron").remote.require('./libs');
// const {fs, importFresh,shell,path} = require("electron").remote.require('./libs');
export default {
name: 'AclJs',
export default {
name: "acl-js",
props: ['nodes'],
data () {
return {
disableSaveButton: true,
search: '',
policyPath: '',
columnToggle: {},
rowToggle: {},
// allToggle: false,
methodColor: {
get: 'green',
post: 'orange',
put: 'deep-orange',
patch: 'pink lighten-1',
delete: 'red darken-3'
},
roles: [
'creator',
'editor',
'guest'
],
methods: [
'get', 'post', 'put', 'patch', 'delete'
],
data1: null
}
},
methods: {
props: ["nodes"],
methods: {
openFolder() {
shell.openItem(path.dirname(this.policyPath));
},
toggleColumn(role, method, checked) {
for (let [path, methods] of Object.entries(this.data1)) {
if (methods[method]) {
this.$set(methods[method], role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleRow(path, checked) {
for (let [method, roles] of Object.entries(this.data1[path])) {
for (let role in roles) {
this.$set(roles, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleAll(checked) {
this.disableSaveButton = false;
for (let path in this.data1) {
this.rowToggle[path] = checked;
}
for (let role of this.roles) {
for (let method of this.methods) {
this.columnToggle[`${method}_${role}`] = checked;
}
}
for (let methods of Object.values(this.data1)) {
for (let method of Object.values(methods)) {
for (let role of this.roles) {
this.$set(method, role, checked)
}
}
}
},
toggleCell(path, method, role, checked) {
this.disableSaveButton = false;
this.$set(this.columnToggle, `${method}_${role}`, Object.values(this.data1).some(methods => methods[method] && methods[method][role]));
this.$set(this.rowToggle, path, Object.values(this.data1[path]).some(roles => Object.values(roles).some(v => v)));
},
initColumnCheckBox() {
for (let role of this.roles) {
for (let method of this.methods) {
this.columnToggle[`${method}_${role}`] = Object.values(this.data1).some(methods => methods[method] && methods[method][role]);
}
}
},
initRowCheckBox() {
for (let path in this.data1) {
this.rowToggle[path] = Object.values(this.data1[path]).filter(roles => Object.entries(roles).filter(([role, v]) => {
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
return v;
}).length).length;
}
},
async aclInit() {
this.disableSaveButton = true;
this.policyPath = await this.sqlMgr.projectGetPolicyPath({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
});
try {
console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify((await this.sqlMgr.importFresh({path: this.policyPath})).permissions));
this.data1 = JSON.parse(JSON.stringify((await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', {path: this.policyPath}])).permissions));
this.initColumnCheckBox();
this.initRowCheckBox();
} catch (e) {
console.log(e)
}
},
async save() {
try {
// await this.sqlMgr.writeFile({
// path: this.policyPath,
// data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
// });
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
}]);
this.disableSaveButton = true;
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000);
} catch (e) {
console.log(e);
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000);
openFolder () {
// shell.openItem(path.dirname(this.policyPath))
},
toggleColumn (role, method, checked) {
for (const [path, methods] of Object.entries(this.data1)) {
if (methods[method]) {
this.$set(methods[method], role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
data() {
return {
disableSaveButton: true,
search: '',
policyPath: '',
columnToggle: {},
rowToggle: {},
// allToggle: false,
methodColor: {
get: 'green',
post: 'orange',
put: 'deep-orange',
patch: 'pink lighten-1',
delete: 'red darken-3',
},
roles: [
'creator',
'editor',
'guest'
],
methods: [
'get', 'post', 'put', 'patch', 'delete'
],
data1: null
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
allToggle: {
get() {
return this.data1 && Object.values(this.data1).some(methods => Object.values(methods).some(roles => Object.values(roles).some(v => v)))
},
set(checked) {
this.toggleAll(checked)
toggleRow (path, checked) {
for (const [method, roles] of Object.entries(this.data1[path])) {
for (const role in roles) {
this.$set(roles, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
async created() {
this.aclInit();
toggleAll (checked) {
this.disableSaveButton = false
for (const path in this.data1) {
this.rowToggle[path] = checked
}
for (const role of this.roles) {
for (const method of this.methods) {
this.columnToggle[`${method}_${role}`] = checked
}
}
for (const methods of Object.values(this.data1)) {
for (const method of Object.values(methods)) {
for (const role of this.roles) {
this.$set(method, role, checked)
}
}
}
},
watch: {}
toggleCell (path, method, role, checked) {
this.disableSaveButton = false
this.$set(this.columnToggle, `${method}_${role}`, Object.values(this.data1).some(methods => methods[method] && methods[method][role]))
this.$set(this.rowToggle, path, Object.values(this.data1[path]).some(roles => Object.values(roles).some(v => v)))
},
initColumnCheckBox () {
for (const role of this.roles) {
for (const method of this.methods) {
this.columnToggle[`${method}_${role}`] = Object.values(this.data1).some(methods => methods[method] && methods[method][role])
}
}
},
initRowCheckBox () {
for (const path in this.data1) {
this.rowToggle[path] = Object.values(this.data1[path]).filter(roles => Object.entries(roles).filter(([role, v]) => {
if (!this.roles.includes(role)) { this.roles = [...this.roles, role] }
return v
}).length).length
}
},
async aclInit () {
this.disableSaveButton = true
this.policyPath = await this.sqlMgr.projectGetPolicyPath({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
})
try {
console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify((await this.sqlMgr.importFresh({path: this.policyPath})).permissions));
this.data1 = JSON.parse(JSON.stringify((await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', { path: this.policyPath }])).permissions))
this.initColumnCheckBox()
this.initRowCheckBox()
} catch (e) {
console.log(e)
}
},
async save () {
try {
// await this.sqlMgr.writeFile({
// path: this.policyPath,
// data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
// });
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports.permissions = ${JSON.stringify(this.data1, 0, 2)}`
}])
this.disableSaveButton = true
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000)
}
}
},
computed: {
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
allToggle: {
get () {
return this.data1 && Object.values(this.data1).some(methods => Object.values(methods).some(roles => Object.values(roles).some(v => v)))
},
set (checked) {
this.toggleAll(checked)
}
}
},
watch: {},
async created () {
this.aclInit()
}
}
</script>
<style scoped>

View File

@@ -1,133 +1,159 @@
<!-- eslint-disable -->
<template>
<div>
<v-card style="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload ACL"
color="primary"
small
v-ge="['acl','reload']"
@click="reload"
<v-spacer />
<x-btn
v-ge="['acl','reload']"
outlined
tooltip="Reload ACL"
color="primary"
small
@click="reload"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn tooltip="Open Corresponding Folder"
icon="mdi-folder-open"
outlined
small
:disabled="!policyPaths || !policyPaths.length"
color="primary"
v-ge="['acl','open-folder']"
@click="openFolder">
<x-btn
v-ge="['acl','open-folder']"
tooltip="Open Corresponding Folder"
icon="mdi-folder-open"
outlined
small
:disabled="!policyPaths || !policyPaths.length"
color="primary"
@click="openFolder"
>
Open Folder
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
class="primary"
small
@click="save"
:disabled="disableSaveButton"
v-ge="['acl','save']">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['acl','save']"
outlined
tooltip="Save Changes"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click="save"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${nodes.tn} routes`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${nodes.tn} routes`"
prepend-inner-icon="search"
outlined
/>
<acl-ts-file-child
style="border-bottom: 1px solid grey"
v-for="(policyPath,k) in policyPaths"
:key="k"
ref="acl" :nodes="nodes" :search="search" :policyPath="policyPath"></acl-ts-file-child>
ref="acl"
style="border-bottom: 1px solid grey"
:nodes="nodes"
:search="search"
:policy-path="policyPath"
/>
<v-alert v-if="policyPaths && !policyPaths.length" outlined type="info">Permission file not found</v-alert>
<v-alert v-if="policyPaths && !policyPaths.length" outlined type="info">
Permission file not found
</v-alert>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import aclTsFileChild from './aclTsFileChild'
import { mapGetters } from 'vuex'
import aclTsFileChild from './aclTsFileChild'
// const {shell, path} = require("electron").remote.require('./libs');
// const {shell, path} = require("electron").remote.require('./libs');
export default {
name: 'AclTypeorm',
components: { aclTsFileChild },
props: ['nodes'],
data () {
return {
policyPaths: null,
search: ''
}
},
methods: {
export default {
name: "acl-typeorm",
components: {aclTsFileChild},
props: ["nodes"],
methods: {
async aclInit () {
// this.disableSaveButton = true;
// this.policyPaths = await this.sqlMgr.projectGetTsPolicyPath({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// });
async aclInit() {
// this.disableSaveButton = true;
// this.policyPaths = await this.sqlMgr.projectGetTsPolicyPath({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// });
this.policyPaths = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
}, 'projectGetTsPolicyPath', {
tn: this.nodes.tn
}]);
},
reload() {
for (const $acl of this.$refs.acl) {
$acl.aclInit();
}
},
save() {
for (const $acl of this.$refs.acl) {
$acl.save();
}
},
openFolder() {
// shell.openItem(path.dirname(this.policyPaths[0]));
},
this.policyPaths = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'projectGetTsPolicyPath', {
tn: this.nodes.tn
}])
},
data() {
return {
policyPaths: null,
search: ''
reload () {
for (const $acl of this.$refs.acl) {
$acl.aclInit()
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
save () {
for (const $acl of this.$refs.acl) {
$acl.save()
}
},
async created() {
await this.aclInit();
},
watch: {}
openFolder () {
// shell.openItem(path.dirname(this.policyPaths[0]));
}
},
computed: {
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' })
},
watch: {},
async created () {
await this.aclInit()
}
}
</script>
<style scoped>

View File

@@ -1,7 +1,7 @@
<!-- eslint-disable -->
<template>
<div>
<v-card style="" v-if="filteredGroupedData.length">
<v-card v-if="filteredGroupedData.length" style="">
<!-- <v-toolbar flat height="42" class="toolbar-border-bottom">-->
<!-- <v-toolbar-title>-->
<!-- <v-breadcrumbs :items="[{-->
@@ -61,291 +61,317 @@
<!-- outlined></v-text-field>-->
<v-simple-table v-if="data1" dense>
<thead>
<tr>
<th colspan="2" class="text-center" rowspan="3">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-1 flex-shrink-1" dense
v-model="allToggle"></v-checkbox>
</template>
<span>{{allToggle ? 'Disable' : 'Enable'}} all {{nodes.tn}} routes for all roles</span>
</v-tooltip>
<span class="title">{{routesName}} Routes</span>
</div>
</th>
<th v-for="role in roles" :colspan="methods.length"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex align-center justify-center">
<span>{{role}}</span>
</div>
</th>
</tr>
<tr>
<!-- <th colspan="2"></th>-->
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th width="25" class="caption px-1" :key="`${method}_${role}`"
:style="i ? '' : 'border-left: 1px solid grey'">{{method}}
</th>
</template>
</template>
</tr>
<tr>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th width="25" class="caption px-1" :key="`${method}_${role}`"
:style="i ? '' : 'border-left: 1px solid grey'">
<tr>
<th colspan="2" class="text-center" rowspan="3">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-0" dense
v-model="columnToggle[`${method}_${role}`]"
@change="toggleColumn(role,method,columnToggle[`${method}_${role}`])"></v-checkbox>
v-model="allToggle"
v-ge="['acl','toggle-checkbox']"
class="mt-1 flex-shrink-1"
dense
v-on="on"
/>
</template>
<span>{{columnToggle[`${method}_${role}`] ? 'Disable' : 'Enable'}} all {{method}} routes for {{role}}</span>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} routes for all roles</span>
</v-tooltip>
</th>
<span class="title">{{ routesName }} Routes</span>
</div>
</th>
<th
v-for="role in roles"
:key="role"
:colspan="methods.length"
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<!-- <th colspan="2"></th>-->
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th
:key="`${method}_${role}`"
width="25"
class="caption px-1"
:style="i ? '' : 'border-left: 1px solid grey'"
>
{{ method }}
</th>
</template>
</template>
</template>
</tr>
</tr>
<tr>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th
:key="`${method}_${role}`"
width="25"
class="caption px-1"
:style="i ? '' : 'border-left: 1px solid grey'"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="columnToggle[`${method}_${role}`]"
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
v-on="on"
@change="toggleColumn(role,method,columnToggle[`${method}_${role}`])"
/>
</template>
<span>{{ columnToggle[`${method}_${role}`] ? 'Disable' : 'Enable' }} all {{ method }} routes for {{ role }}</span>
</v-tooltip>
</th>
</template>
</template>
</tr>
</thead>
<tbody>
<tr v-for="[path,route] in filteredGroupedData"
>
<tr
v-for="([path,route], i) in filteredGroupedData"
:key="i"
>
<td width="20" class="px-0">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="rowToggle[path]"
v-ge="['acl','toggle-checkbox']"
class="mt-0 ml-3"
dense
v-on="on"
@change="toggleRow(path,rowToggle[path])"
/>
</template>
<td width="20" class="px-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-0 ml-3" v-model="rowToggle[path]"
@change="toggleRow(path,rowToggle[path])"
dense></v-checkbox>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this route for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<td :key="`${path}_${method}_${role}`" :style="i ? '' : 'border-left: 1px solid grey'" class="pa-1">
<v-checkbox
v-if="route[method]"
v-model="route[method].acl[role]"
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
:color="methodColor[method]"
:input-value="route[method].acl[role]"
@change="toggleCell(path,method,role,route[method].acl[role])"
/>
<span
v-else
>
<!-- todo: @dblclick="$set(data1[path],method , {})"-->
<v-checkbox
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
:disabled="true"
/></span>
</td>
</template>
<span>{{rowToggle[path] ? 'Disable' : 'Enable'}} this route for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{path}}</span>
</template>
<span>{{path}}</span>
</v-tooltip>
</td>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<td :style="i ? '' : 'border-left: 1px solid grey'" class="pa-1" :key="`${path}_${method}_${role}`">
<v-checkbox
v-ge="['acl','toggle-checkbox']"
v-if="route[method]" class="mt-0" dense
:color="methodColor[method]"
:input-value="route[method].acl[role]"
v-model="route[method].acl[role]"
@change="toggleCell(path,method,role,route[method].acl[role])">
</v-checkbox>
<span
v-else>
<!-- todo: @dblclick="$set(data1[path],method , {})"-->
<v-checkbox v-ge="['acl','toggle-checkbox']" class="mt-0" dense
:disabled="true">
</v-checkbox></span>
</td>
</template>
</template>
</tr>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
export default {
name: 'AclTsFileChild',
export default {
name: "acl-ts-file-child",
props: ['nodes', 'policyPath', 'search'],
data () {
return {
groupedData: null,
disableSaveButton: true,
// policyPath: '',
columnToggle: {},
rowToggle: {},
// allToggle: false,
methodColor: {
get: 'green',
post: 'orange',
put: 'deep-orange',
patch: 'pink lighten-1',
delete: 'red darken-3'
},
roles: [
'creator',
'editor',
'guest'
],
methods: [
'get', 'post', 'put', 'delete'
],
data1: null
}
},
methods: {
props: ["nodes", "policyPath", "search"],
methods: {
async aclInit () {
this.disableSaveButton = true
async aclInit() {
this.disableSaveButton = true;
try {
console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify(await this.sqlMgr.importFresh({path: this.policyPath})));
this.data1 = JSON.parse(JSON.stringify(await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', {path: this.policyPath}])));
this.groupRoutes();
this.initColumnCheckBox();
this.initRowCheckBox();
} catch (e) {
console.log(e)
}
},
groupRoutes() {
const groupedData = {};
for (const route of this.data1) {
groupedData[route.path] = groupedData[route.path] || {};
groupedData[route.path][route.type] = route;
}
this.groupedData = groupedData;
},
toggleColumn(role, method, checked) {
for (let [path, methods] of Object.entries(this.groupedData)) {
if (methods[method]) {
this.$set(methods[method].acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleRow(path, checked) {
for (let [method, route] of Object.entries(this.groupedData[path])) {
for (let role in route.acl) {
this.$set(route.acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleAll(checked) {
this.disableSaveButton = false;
for (let path in this.groupedData) {
this.rowToggle[path] = checked;
}
for (let role of this.roles) {
for (let method of this.methods) {
this.columnToggle[`${method}_${role}`] = checked;
}
}
for (let methods of Object.values(this.groupedData)) {
for (let router of Object.values(methods)) {
for (let role of this.roles) {
this.$set(router.acl, role, checked)
}
}
}
},
toggleCell(path, method, role, checked) {
this.disableSaveButton = false;
this.$set(this.columnToggle, `${method}_${role}`, Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role]));
this.$set(this.rowToggle, path, Object.values(this.groupedData[path]).some(route => Object.values(route.acl).some(v => v)));
},
initColumnCheckBox() {
for (let role of this.roles) {
for (let method of this.methods) {
this.columnToggle[`${method}_${role}`] = Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role]);
}
}
},
initRowCheckBox() {
for (let path in this.groupedData) {
this.rowToggle[path] = Object.values(this.groupedData[path])
.filter(route =>
Object.entries(route.acl).filter(([role, v]) => {
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
return v;
}).length
).length;
}
},
async save() {
try {
// await this.sqlMgr.writeFile({
// path: this.policyPath,
// data: `module.exports = ${JSON.stringify(this.data1, null, 2)}`
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports = ${JSON.stringify(this.data1, null, 2)}`
}])
this.disableSaveButton = true;
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000);
} catch (e) {
console.log(e);
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000);
try {
console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify(await this.sqlMgr.importFresh({path: this.policyPath})));
this.data1 = JSON.parse(JSON.stringify(await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', { path: this.policyPath }])))
this.groupRoutes()
this.initColumnCheckBox()
this.initRowCheckBox()
} catch (e) {
console.log(e)
}
},
groupRoutes () {
const groupedData = {}
for (const route of this.data1) {
groupedData[route.path] = groupedData[route.path] || {}
groupedData[route.path][route.type] = route
}
this.groupedData = groupedData
},
toggleColumn (role, method, checked) {
for (const [path, methods] of Object.entries(this.groupedData)) {
if (methods[method]) {
this.$set(methods[method].acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
data() {
return {
groupedData: null,
disableSaveButton: true,
policyPath: '',
columnToggle: {},
rowToggle: {},
// allToggle: false,
methodColor: {
get: 'green',
post: 'orange',
put: 'deep-orange',
patch: 'pink lighten-1',
delete: 'red darken-3',
},
roles: [
'creator',
'editor',
'guest'
],
methods: [
'get', 'post', 'put', 'delete'
],
data1: null
toggleRow (path, checked) {
for (const [method, route] of Object.entries(this.groupedData[path])) {
for (const role in route.acl) {
this.$set(route.acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
allToggle: {
get() {
return this.groupedData && Object.values(this.groupedData)
.some(methods => Object.values(methods)
.some(route => Object.values(route.acl)
.some(v => v)
)
toggleAll (checked) {
this.disableSaveButton = false
for (const path in this.groupedData) {
this.rowToggle[path] = checked
}
for (const role of this.roles) {
for (const method of this.methods) {
this.columnToggle[`${method}_${role}`] = checked
}
}
for (const methods of Object.values(this.groupedData)) {
for (const router of Object.values(methods)) {
for (const role of this.roles) {
this.$set(router.acl, role, checked)
}
}
}
},
toggleCell (path, method, role, checked) {
this.disableSaveButton = false
this.$set(this.columnToggle, `${method}_${role}`, Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role]))
this.$set(this.rowToggle, path, Object.values(this.groupedData[path]).some(route => Object.values(route.acl).some(v => v)))
},
initColumnCheckBox () {
for (const role of this.roles) {
for (const method of this.methods) {
this.columnToggle[`${method}_${role}`] = Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role])
}
}
},
initRowCheckBox () {
for (const path in this.groupedData) {
this.rowToggle[path] = Object.values(this.groupedData[path])
.filter(route =>
Object.entries(route.acl).filter(([role, v]) => {
if (!this.roles.includes(role)) { this.roles = [...this.roles, role] }
return v
}).length
).length
}
},
async save () {
try {
// await this.sqlMgr.writeFile({
// path: this.policyPath,
// data: `module.exports = ${JSON.stringify(this.data1, null, 2)}`
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports = ${JSON.stringify(this.data1, null, 2)}`
}])
this.disableSaveButton = true
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000)
}
}
},
computed: {
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
allToggle: {
get () {
return this.groupedData && Object.values(this.groupedData)
.some(methods => Object.values(methods)
.some(route => Object.values(route.acl)
.some(v => v)
)
},
set(checked) {
this.toggleAll(checked)
}
)
},
routesName() {
return this.policyPath && this.policyPath
.split('/').pop()
.replace(/\.routes.js$/, '')
.replace(/(?:^|\.)(\w+)/g, (_, m) => {
if (m === 'bt') return ' BelongsTo';
if (m === 'hm') return ' HasMany';
return ' ' + m[0].toUpperCase() + m.slice(1)
})
},
filteredGroupedData() {
return this.groupedData ? Object.entries(this.groupedData)
.filter(([path]) => !this.search || path.toLowerCase().indexOf(this.search.toLowerCase()) > -1) : [];
set (checked) {
this.toggleAll(checked)
}
},
async created() {
await this.aclInit();
routesName () {
return this.policyPath && this.policyPath
.split('/').pop()
.replace(/\.routes.js$/, '')
.replace(/(?:^|\.)(\w+)/g, (_, m) => {
if (m === 'bt') { return ' BelongsTo' }
if (m === 'hm') { return ' HasMany' }
return ' ' + m[0].toUpperCase() + m.slice(1)
})
},
watch: {}
filteredGroupedData () {
return this.groupedData
? Object.entries(this.groupedData)
.filter(([path]) => !this.search || path.toLowerCase().includes(this.search.toLowerCase()))
: []
}
},
watch: {},
async created () {
await this.aclInit()
}
}
</script>
<style scoped>

View File

@@ -1,79 +1,100 @@
<template>
<!-- eslint-disable --><template>
<div>
<v-card style="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload ACL"
color="primary"
small
v-ge="['acl','reload']"
@click="reload"
<v-spacer />
<x-btn
v-ge="['acl','reload']"
outlined
tooltip="Reload ACL"
color="primary"
small
@click="reload"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn tooltip="Open Corresponding Folder"
icon="mdi-folder-open"
outlined
small
:disabled="!policies || !policies.length"
color="primary"
v-ge="['acl','open-folder']"
@click="openFolder">
<x-btn
v-ge="['acl','open-folder']"
tooltip="Open Corresponding Folder"
icon="mdi-folder-open"
outlined
small
:disabled="!policies || !policies.length"
color="primary"
@click="openFolder"
>
Open Folder
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
class="primary"
small
@click="save"
:disabled="disableSaveButton"
v-ge="['acl','save']">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['acl','save']"
outlined
tooltip="Save Changes"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click="save"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<template v-if="loading">
<v-skeleton-loader
type="table"
width="100%"></v-skeleton-loader>
width="100%"
/>
</template>
<template v-else>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${nodes.tn} routes`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${nodes.tn} routes`"
prepend-inner-icon="search"
outlined
/>
<acl-typeorm-db-child
v-if="policies && policies.length"
ref="acl"
:nodes="nodes" :search="search" :policies="policies"
></acl-typeorm-db-child>
:nodes="nodes"
:search="search"
:policies="policies"
/>
</template>
<!-- <acl-ts-file-child-->
<!-- style="border-bottom: 1px solid grey"-->
@@ -85,19 +106,26 @@
</template>
<script>
import {mapGetters} from "vuex";
import AclTypeormDbChild from "./aclTsFileDbChild";
import { mapGetters } from 'vuex'
import AclTypeormDbChild from './aclTsFileDbChild'
// const {shell, path} = require("electron").remote.require('./libs');
export default {
name: "acl-typeorm-db",
components: {AclTypeormDbChild},
props: ["nodes"],
name: 'AclTypeormDb',
components: { AclTypeormDbChild },
props: ['nodes'],
data () {
return {
disableSaveButton: false,
policies: [],
search: '',
loading: false
}
},
methods: {
async aclInit() {
async aclInit () {
// // this.disableSaveButton = true;
// this.policies = (await this.sqlMgr.xcRoutesPolicyGet({
// env: this.nodes.env,
@@ -109,40 +137,31 @@ export default {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}])).data.list;
}])).data.list
},
reload() {
this.$refs.acl.aclInit();
reload () {
this.$refs.acl.aclInit()
},
save() {
this.$refs.acl.save();
save () {
this.$refs.acl.save()
},
openFolder() {
},
},
data() {
return {
disableSaveButton: false,
policies: [],
search: '',
loading: false
openFolder () {
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
},
mounted() {
this.$nuxt.$loading.start();
setTimeout(async () => {
await this.aclInit();
this.$nuxt.$loading.finish();
this.loading = false;
}, 500);
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' })
},
watch: {},
created() {
this.loading = true;
mounted () {
this.$nuxt.$loading.start()
setTimeout(async () => {
await this.aclInit()
this.$nuxt.$loading.finish()
this.loading = false
}, 500)
},
created () {
this.loading = true
}
}
</script>

View File

@@ -1,7 +1,7 @@
<!-- eslint-disable -->
<template>
<div>
<v-card class="elevation-0" style="" v-if="filteredGroupedData.length">
<v-card v-if="filteredGroupedData.length" class="elevation-0" style="">
<!-- <v-toolbar flat height="42" class="toolbar-border-bottom">-->
<!-- <v-toolbar-title>-->
<!-- <v-breadcrumbs :items="[{-->
@@ -61,251 +61,169 @@
<!-- outlined></v-text-field>-->
<v-simple-table v-if="data1" dense>
<thead>
<tr>
<th colspan="2" class="text-center" rowspan="3">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-1 flex-shrink-1" dense
v-model="allToggle"></v-checkbox>
</template>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} routes for all roles</span>
</v-tooltip>
<span class="title">{{ routesName }} Routes</span>
</div>
</th>
<th v-for="role in roles" :colspan="methods.length"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<!-- <th colspan="2"></th>-->
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th width="25" class="caption px-1" :key="`${method}_${role}`"
:style="i ? '' : 'border-left: 1px solid grey'">{{ method }}
</th>
</template>
</template>
</tr>
<tr>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th width="25" class="caption px-1" :key="`${method}_${role}`"
:style="i ? '' : 'border-left: 1px solid grey'">
<tr>
<th colspan="2" class="text-center" rowspan="3">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-0" dense
v-model="columnToggle[`${method}_${role}`]"
@change="toggleColumn(role,method,columnToggle[`${method}_${role}`])"></v-checkbox>
v-model="allToggle"
v-ge="['acl','toggle-checkbox']"
class="mt-1 flex-shrink-1"
dense
v-on="on"
/>
</template>
<span>{{ columnToggle[`${method}_${role}`] ? 'Disable' : 'Enable' }} all {{ method }} routes for {{ role }}</span>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} routes for all roles</span>
</v-tooltip>
</th>
<span class="title">{{ routesName }} Routes</span>
</div>
</th>
<th
v-for="role in roles"
:key="role"
:colspan="methods.length"
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<!-- <th colspan="2"></th>-->
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th
:key="`${method}_${role}`"
width="25"
class="caption px-1"
:style="i ? '' : 'border-left: 1px solid grey'"
>
{{ method }}
</th>
</template>
</template>
</template>
</tr>
</tr>
<tr>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<th
:key="`${method}_${role}`"
width="25"
class="caption px-1"
:style="i ? '' : 'border-left: 1px solid grey'"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="columnToggle[`${method}_${role}`]"
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
v-on="on"
@change="toggleColumn(role,method,columnToggle[`${method}_${role}`])"
/>
</template>
<span>{{ columnToggle[`${method}_${role}`] ? 'Disable' : 'Enable' }} all {{
method
}} routes for {{ role }}</span>
</v-tooltip>
</th>
</template>
</template>
</tr>
</thead>
<tbody>
<tr v-for="[path,route] in filteredGroupedData"
>
<tr
v-for="([path,route],i) in filteredGroupedData"
:key="i"
>
<td width="20" class="px-0">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="rowToggle[path]"
v-ge="['acl','toggle-checkbox']"
class="mt-0 ml-3"
dense
v-on="on"
@change="toggleRow(path,rowToggle[path])"
/>
</template>
<td width="20" class="px-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
v-ge="['acl','toggle-checkbox']" v-on="on" class="mt-0 ml-3" v-model="rowToggle[path]"
@change="toggleRow(path,rowToggle[path])"
dense></v-checkbox>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this route for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<td
:key="`${path}_${method}_${role}`"
:style="i ? '' : 'border-left: 1px solid grey'"
class="pa-1"
@dblclick="route[method] && showSourceCode(route,method)"
>
<v-checkbox
v-if="route[method]"
v-model="route[method].acl[role]"
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
:color="methodColor[method]"
:input-value="route[method].acl[role]"
@change="toggleCell(path,method,role,route[method].acl[role])"
/>
<span
v-else
>
<!-- todo: @dblclick="$set(data1[path],method , {})"-->
<v-checkbox
v-ge="['acl','toggle-checkbox']"
class="mt-0"
dense
:disabled="true"
/></span>
</td>
</template>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this route for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="role in roles">
<template v-for="(method,i) in methods">
<td @dblclick="route[method] && showSourceCode(route,method)"
:style="i ? '' : 'border-left: 1px solid grey'" class="pa-1" :key="`${path}_${method}_${role}`">
<v-checkbox
v-ge="['acl','toggle-checkbox']"
v-if="route[method]" class="mt-0" dense
:color="methodColor[method]"
:input-value="route[method].acl[role]"
v-model="route[method].acl[role]"
@change="toggleCell(path,method,role,route[method].acl[role])">
</v-checkbox>
<span
v-else>
<!-- todo: @dblclick="$set(data1[path],method , {})"-->
<v-checkbox v-ge="['acl','toggle-checkbox']" class="mt-0" dense
:disabled="true">
</v-checkbox></span>
</td>
</template>
</template>
</tr>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
<handler-code-editor :nodes="nodes" :route="editRoute"
:method="editMethod"
v-model="showCodeEditor"></handler-code-editor>
<handler-code-editor
v-model="showCodeEditor"
:nodes="nodes"
:route="editRoute"
:method="editMethod"
/>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import HandlerCodeEditor from "../restHandlerCodeEditor";
import { mapGetters } from 'vuex'
import HandlerCodeEditor from '../restHandlerCodeEditor'
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
export default {
name: "acl-ts-file-db-child",
components: {HandlerCodeEditor},
props: ["nodes", "policies", "search"],
methods: {
showSourceCode(route, method) {
this.editRoute = route;
this.editMethod = method;
this.showCodeEditor = true;
},
aclInit() {
this.disableSaveButton = true;
try {
// console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify(importFresh(this.policyPath)));
this.groupRoutes();
this.initColumnCheckBox();
this.initRowCheckBox();
} catch (e) {
console.log(e)
}
},
groupRoutes() {
const groupedData = {};
for (const route of this.data1) {
if (route.path) {
groupedData[route.path] = groupedData[route.path] || {};
groupedData[route.path][route.type] = route;
}
}
this.groupedData = groupedData;
},
toggleColumn(role, method, checked) {
for (let [path, methods] of Object.entries(this.groupedData)) {
if (methods[method]) {
this.$set(methods[method].acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleRow(path, checked) {
for (let [method, route] of Object.entries(this.groupedData[path])) {
for (let role in route.acl) {
this.$set(route.acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleAll(checked) {
this.disableSaveButton = false;
for (let path in this.groupedData) {
this.rowToggle[path] = checked;
}
for (let role of this.roles) {
for (let method of this.methods) {
this.columnToggle[`${method}_${role}`] = checked;
}
}
for (let methods of Object.values(this.groupedData)) {
for (let router of Object.values(methods)) {
for (let role of this.roles) {
this.$set(router.acl, role, checked)
}
}
}
},
toggleCell(path, method, role, checked) {
this.disableSaveButton = false;
this.$set(this.columnToggle, `${method}_${role}`, Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role]));
this.$set(this.rowToggle, path, Object.values(this.groupedData[path]).some(route => Object.values(route.acl).some(v => v)));
},
initColumnCheckBox() {
for (let role of this.roles) {
for (let method of this.methods) {
this.columnToggle[`${method}_${role}`] = Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role]);
}
}
},
initRowCheckBox() {
for (let path in this.groupedData) {
this.rowToggle[path] = Object.values(this.groupedData[path])
.filter(route =>
Object.entries(route.acl).filter(([role, v]) => {
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
return v;
}).length
).length;
}
},
async save() {
try {
//
// await this.sqlMgr.xcRoutesPolicyUpdate({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn,
// data: this.data1
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcRoutesPolicyUpdate', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn,
data: this.data1
}])
this.disableSaveButton = true;
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000);
} catch (e) {
console.log(e);
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000);
}
}
},
data() {
name: 'AclTsFileDbChild',
components: { HandlerCodeEditor },
props: ['nodes', 'policies', 'search'],
data () {
return {
showCodeEditor: false,
editRoute: null,
@@ -321,7 +239,7 @@ export default {
post: 'orange',
put: 'deep-orange',
patch: 'pink lighten-1',
delete: 'red darken-3',
delete: 'red darken-3'
},
roles: [
'creator',
@@ -334,10 +252,123 @@ export default {
data1: null
}
},
methods: {
showSourceCode (route, method) {
this.editRoute = route
this.editMethod = method
this.showCodeEditor = true
},
aclInit () {
this.disableSaveButton = true
try {
// console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify(importFresh(this.policyPath)));
this.groupRoutes()
this.initColumnCheckBox()
this.initRowCheckBox()
} catch (e) {
console.log(e)
}
},
groupRoutes () {
const groupedData = {}
for (const route of this.data1) {
if (route.path) {
groupedData[route.path] = groupedData[route.path] || {}
groupedData[route.path][route.type] = route
}
}
this.groupedData = groupedData
},
toggleColumn (role, method, checked) {
for (const [path, methods] of Object.entries(this.groupedData)) {
if (methods[method]) {
this.$set(methods[method].acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleRow (path, checked) {
for (const [method, route] of Object.entries(this.groupedData[path])) {
for (const role in route.acl) {
this.$set(route.acl, role, checked)
this.toggleCell(path, method, role, checked)
}
}
},
toggleAll (checked) {
this.disableSaveButton = false
for (const path in this.groupedData) {
this.rowToggle[path] = checked
}
for (const role of this.roles) {
for (const method of this.methods) {
this.columnToggle[`${method}_${role}`] = checked
}
}
for (const methods of Object.values(this.groupedData)) {
for (const router of Object.values(methods)) {
for (const role of this.roles) {
this.$set(router.acl, role, checked)
}
}
}
},
toggleCell (path, method, role, checked) {
this.disableSaveButton = false
this.$set(this.columnToggle, `${method}_${role}`, Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role]))
this.$set(this.rowToggle, path, Object.values(this.groupedData[path]).some(route => Object.values(route.acl).some(v => v)))
},
initColumnCheckBox () {
for (const role of this.roles) {
for (const method of this.methods) {
this.columnToggle[`${method}_${role}`] = Object.values(this.groupedData).some(methods => methods[method] && methods[method].acl[role])
}
}
},
initRowCheckBox () {
for (const path in this.groupedData) {
this.rowToggle[path] = Object.values(this.groupedData[path])
.filter(route =>
Object.entries(route.acl).filter(([role, v]) => {
if (!this.roles.includes(role)) {
this.roles = [...this.roles, role]
}
return v
}).length
).length
}
},
async save () {
try {
//
// await this.sqlMgr.xcRoutesPolicyUpdate({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn,
// data: this.data1
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcRoutesPolicyUpdate', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn,
data: this.data1
}])
this.disableSaveButton = true
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000)
}
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
allToggle: {
get() {
get () {
return this.groupedData && Object.values(this.groupedData)
.some(methods => Object.values(methods)
.some(route => Object.values(route.acl)
@@ -345,35 +376,39 @@ export default {
)
)
},
set(checked) {
set (checked) {
this.toggleAll(checked)
}
},
routesName() {
routesName () {
return this.policyPath && this.policyPath
.split('/').pop()
.replace(/\.routes.js$/, '')
.replace(/(?:^|\.)(\w+)/g, (_, m) => {
if (m === 'bt') return ' BelongsTo';
if (m === 'hm') return ' HasMany';
if (m === 'bt') {
return ' BelongsTo'
}
if (m === 'hm') {
return ' HasMany'
}
return ' ' + m[0].toUpperCase() + m.slice(1)
})
},
filteredGroupedData() {
filteredGroupedData () {
return Object.entries(this.groupedData)
.filter(([path]) => !this.search || path.toLowerCase().indexOf(this.search.toLowerCase()) > -1);
.filter(([path]) => !this.search || path.toLowerCase().includes(this.search.toLowerCase()))
}
},
watch: {
policies(d) {
this.data1 = JSON.parse(JSON.stringify(d));
this.aclInit();
policies (d) {
this.data1 = JSON.parse(JSON.stringify(d))
this.aclInit()
}
},
async mounted() {
this.data1 = JSON.parse(JSON.stringify(this.policies));
this.aclInit();
},
async mounted () {
this.data1 = JSON.parse(JSON.stringify(this.policies))
this.aclInit()
}
}
</script>

View File

@@ -1,274 +1,204 @@
<!-- eslint-disable -->
<template>
<div>
<v-card style="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload ACL"
color="primary"
small
v-ge="['acl-gql','reload']"
@click="aclInit"
<v-spacer />
<x-btn
v-ge="['acl-gql','reload']"
outlined
tooltip="Reload ACL"
color="primary"
small
@click="aclInit"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small v-ge="['acl-gql','open-folder']"
color="primary"
@click="openFolder">
<x-btn
v-ge="['acl-gql','open-folder']"
tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small
color="primary"
@click="openFolder"
>
Open Folder
</x-btn>
<x-btn outlined tooltip="Save ACL"
color="primary"
class="primary"
small
@click="save"
:disabled="disableSaveButton"
v-ge="['acl-gql','save']">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['acl-gql','save']"
outlined
tooltip="Save ACL"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click="save"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search"
outlined
/>
<v-simple-table v-if="policies && policies.length" dense>
<thead>
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-1 flex-shrink-1" dense
v-model="allToggle"></v-checkbox>
</template>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{ nodes.tn }} Resolvers</span>
</div>
</th>
<th v-for="role in roles"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<th v-for="role in roles"
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="allToggle"
v-ge="['acl-gql','open-folder']"
small
class="mt-1 flex-shrink-1"
dense
v-on="on"
/>
</template>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{ nodes.tn }} Resolvers</span>
</div>
</th>
<th
v-for="role in roles"
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<th
v-for="role in roles"
class="pa-1"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0" dense v-model="columnToggle[role]"
@change="toggleColumn(role,columnToggle[role])"></v-checkbox>
</template>
<span>
<span>{{ columnToggle[role] ? 'Disable' : 'Enable' }} all resolvers for {{ role }}</span></span>
</v-tooltip>
</div>
</th>
</tr>
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="columnToggle[role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
v-on="on"
@change="toggleColumn(role,columnToggle[role])"
/>
</template>
<span>
<span>{{ columnToggle[role] ? 'Disable' : 'Enable' }} all resolvers for {{ role }}</span></span>
</v-tooltip>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td width="20" class="pl-6 pr-3">
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1" key="path" :>
<td width="20" class="pl-6 pr-3">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="rowToggle[path]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0 ml-3"
dense
v-on="on"
@change="toggleRow(path,rowToggle[path])"
/>
</template>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0 ml-3" v-model="rowToggle[path]"
@change="toggleRow(path,rowToggle[path])"
dense></v-checkbox>
</template>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this resolver for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(role,i) in roles">
<td style="border-left: 1px solid grey" class="pa-1" :key="`${path}_${role}`">
<div class="d-flex justify-center">
<v-checkbox
small v-ge="['acl-gql','open-folder']" class="mt-0" dense
v-model="data1[path][role]"
@change="toggleCell(path,role,data1[path][role])"
></v-checkbox>
</div>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this resolver for all roles</span>
</v-tooltip>
</td>
</template>
</tr>
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(role) in roles">
<td :key="`${path}_${role}`" style="border-left: 1px solid grey" class="pa-1">
<div class="d-flex justify-center">
<v-checkbox
v-model="data1[path][role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
@change="toggleCell(path,role,data1[path][role])"
/>
</div>
</td>
</template>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else-if="policies" outlined type="info">Permission file not found</v-alert>
<v-alert v-else-if="policies" outlined type="info">
Permission file not found
</v-alert>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
export default {
name: "acl-ts-file-db-gql",
name: 'AclTsFileDbGql',
props: ["nodes"],
methods: {
openFolder() {
// shell.openItem(path.dirname(this.policyPath));
},
toggleColumn(role, checked) {
for (let [resolver, roles] of Object.entries(this.data1)) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow(resolver, checked) {
for (let role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll(checked) {
this.disableSaveButton = false;
for (let path in this.data1) {
this.rowToggle[path] = checked;
}
for (let role of this.roles) {
this.columnToggle[role] = checked;
}
for (let roles of Object.values(this.data1)) {
for (let role of this.roles) {
this.$set(roles, role, checked)
}
}
},
toggleCell(resolver, role, checked) {
this.disableSaveButton = false;
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]));
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled));
},
initColumnCheckBox() {
for (let role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role]);
}
},
initRowCheckBox() {
for (let path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
return v;
}).length;
}
},
async aclInit() {
try {
console.log(this.sqlMgr)
this.disableSaveButton = true;
// this.policies = (await this.sqlMgr.xcResolverPolicyGet({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// })).data.list;
this.policies = (await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcResolverPolicyGet', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}])).data.list;
//.data.list;
this.data = JSON.parse(JSON.stringify(this.policies));
this.data1 = this.data.reduce((aclObj, resolver) => {
if (resolver.resolver) {
aclObj[resolver.resolver] = resolver.acl;
}
return aclObj;
}, {});
this.initColumnCheckBox();
this.initRowCheckBox();
} catch (e) {
console.log(e)
}
},
async save() {
try {
// await this.sqlMgr.xcResolverPolicyUpdate({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn,
// data: this.data
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcResolverPolicyUpdate', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn,
data: this.data
}])
this.disableSaveButton = true;
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000);
} catch (e) {
console.log(e);
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000);
}
}
},
data() {
props: ['nodes'],
data () {
return {
disableSaveButton: true,
search: '',
@@ -284,21 +214,124 @@ export default {
data1: null
}
},
methods: {
openFolder () {
// shell.openItem(path.dirname(this.policyPath));
},
toggleColumn (role, checked) {
for (const [resolver, roles] of Object.entries(this.data1)) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow (resolver, checked) {
for (const role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll (checked) {
this.disableSaveButton = false
for (const path in this.data1) {
this.rowToggle[path] = checked
}
for (const role of this.roles) {
this.columnToggle[role] = checked
}
for (const roles of Object.values(this.data1)) {
for (const role of this.roles) {
this.$set(roles, role, checked)
}
}
},
toggleCell (resolver, role, checked) {
this.disableSaveButton = false
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]))
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled))
},
initColumnCheckBox () {
for (const role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role])
}
},
initRowCheckBox () {
for (const path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) { this.roles = [...this.roles, role] }
return v
}).length
}
},
async aclInit () {
try {
console.log(this.sqlMgr)
this.disableSaveButton = true
// this.policies = (await this.sqlMgr.xcResolverPolicyGet({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// })).data.list;
this.policies = (await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcResolverPolicyGet', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}])).data.list
// .data.list;
this.data = JSON.parse(JSON.stringify(this.policies))
this.data1 = this.data.reduce((aclObj, resolver) => {
if (resolver.resolver) {
aclObj[resolver.resolver] = resolver.acl
}
return aclObj
}, {})
this.initColumnCheckBox()
this.initRowCheckBox()
} catch (e) {
console.log(e)
}
},
async save () {
try {
// await this.sqlMgr.xcResolverPolicyUpdate({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn,
// data: this.data
// })
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcResolverPolicyUpdate', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn,
data: this.data
}])
this.disableSaveButton = true
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000)
}
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
allToggle: {
get() {
get () {
return this.data1 && Object.values(this.data1).some(roles => Object.values(roles).some(v => v))
},
set(checked) {
set (checked) {
this.toggleAll(checked)
}
}
},
async mounted() {
await this.aclInit();
},
watch: {}
watch: {},
async mounted () {
await this.aclInit()
}
}
</script>

View File

@@ -1,283 +1,322 @@
<!-- eslint-disable -->
<template>
<div>
<v-card style="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (ACL)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload ACL"
color="primary"
small
v-ge="['acl-gql','reload']"
@click="aclInit"
<v-spacer />
<x-btn
v-ge="['acl-gql','reload']"
outlined
tooltip="Reload ACL"
color="primary"
small
@click="aclInit"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small v-ge="['acl-gql','open-folder']"
color="primary"
@click="openFolder">
<x-btn
v-ge="['acl-gql','open-folder']"
tooltip="Open ACL Folder"
icon="mdi-folder-open"
outlined
small
color="primary"
@click="openFolder"
>
Open Folder
</x-btn>
<x-btn outlined tooltip="Save ACL"
color="primary"
class="primary"
small
@click="save"
:disabled="disableSaveButton"
v-ge="['acl-gql','save']">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['acl-gql','save']"
outlined
tooltip="Save ACL"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click="save"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${nodes.tn} resolvers`"
prepend-inner-icon="search"
outlined
/>
<v-simple-table v-if="data1" dense>
<thead>
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-1 flex-shrink-1" dense
v-model="allToggle"></v-checkbox>
</template>
<span>{{allToggle ? 'Disable' : 'Enable'}} all {{nodes.tn}} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{nodes.tn}} Resolvers</span>
</div>
</th>
<th v-for="role in roles"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex align-center justify-center">
<span>{{role}}</span>
</div>
</th>
</tr>
<tr>
<th v-for="role in roles"
<tr>
<th colspan="2" class="text-center" rowspan="2">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="allToggle"
v-ge="['acl-gql','open-folder']"
small
class="mt-1 flex-shrink-1"
dense
v-on="on"
/>
</template>
<span>{{ allToggle ? 'Disable' : 'Enable' }} all {{ nodes.tn }} resolvers for all roles</span>
</v-tooltip>
<span class="title">{{ nodes.tn }} Resolvers</span>
</div>
</th>
<th
v-for="role in roles"
:key="role"
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex align-center justify-center">
<span>{{ role }}</span>
</div>
</th>
</tr>
<tr>
<th
v-for="role in roles"
:key="role"
class="pa-1"
style="border-left: 1px solid grey;border-bottom: 1px solid grey">
<div class="d-flex justify-center">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0" dense v-model="columnToggle[role]"
@change="toggleColumn(role,columnToggle[role])"></v-checkbox>
</template>
<span>
<span>{{columnToggle[role] ? 'Disable' : 'Enable'}} all resolvers for {{role}}</span></span>
</v-tooltip>
</div>
</th>
</tr>
style="border-left: 1px solid grey;border-bottom: 1px solid grey"
>
<div class="d-flex justify-center">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="columnToggle[role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
v-on="on"
@change="toggleColumn(role,columnToggle[role])"
/>
</template>
<span>
<span>{{ columnToggle[role] ? 'Disable' : 'Enable' }} all resolvers for {{ role }}</span></span>
</v-tooltip>
</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td width="20" class="pl-6 pr-3">
<tr v-for="(resolver,path) in data1" v-show="!search || path.toLowerCase().indexOf(search.toLowerCase()) > -1" :key="path">
<td width="20" class="pl-6 pr-3">
<v-tooltip bottom>
<template #activator="{ on }">
<v-checkbox
v-model="rowToggle[path]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0 ml-3"
dense
v-on="on"
@change="toggleRow(path,rowToggle[path])"
/>
</template>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-checkbox
small v-ge="['acl-gql','open-folder']" v-on="on" class="mt-0 ml-3" v-model="rowToggle[path]"
@change="toggleRow(path,rowToggle[path])"
dense></v-checkbox>
</template>
<span>{{rowToggle[path] ? 'Disable' : 'Enable'}} this resolver for all roles</span>
</v-tooltip>
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{path}}</span>
</template>
<span>{{path}}</span>
</v-tooltip>
</td>
<template v-for="(role,i) in roles">
<td style="border-left: 1px solid grey" class="pa-1" :key="`${path}_${role}`">
<div class="d-flex justify-center">
<v-checkbox
small v-ge="['acl-gql','open-folder']" class="mt-0" dense
v-model="data1[path][role]"
@change="toggleCell(path,role,data1[path][role])"
></v-checkbox>
</div>
<span>{{ rowToggle[path] ? 'Disable' : 'Enable' }} this resolver for all roles</span>
</v-tooltip>
</td>
</template>
</tr>
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(role) in roles">
<td :key="`${path}_${role}`" style="border-left: 1px solid grey" class="pa-1">
<div class="d-flex justify-center">
<v-checkbox
v-model="data1[path][role]"
v-ge="['acl-gql','open-folder']"
small
class="mt-0"
dense
@change="toggleCell(path,role,data1[path][role])"
/>
</div>
</td>
</template>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from 'vuex'
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
// const {fs, importFresh, shell, path} = require("electron").remote.require('./libs');
export default {
name: 'AclTsFileGql',
export default {
name: "acl-ts-file-gql",
props: ['nodes'],
data () {
return {
disableSaveButton: true,
search: '',
policyPath: '',
columnToggle: {},
rowToggle: {},
roles: [
'creator',
'editor',
'guest'
],
data1: null
}
},
methods: {
openFolder () {
// shell.openItem(path.dirname(this.policyPath))
},
toggleColumn (role, checked) {
for (const [resolver, roles] of Object.entries(this.data1)) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow (resolver, checked) {
for (const role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll (checked) {
this.disableSaveButton = false
for (const path in this.data1) {
this.rowToggle[path] = checked
}
for (const role of this.roles) {
this.columnToggle[role] = checked
}
props: ["nodes"],
methods: {
openFolder() {
shell.openItem(path.dirname(this.policyPath));
},
toggleColumn(role, checked) {
for (let [resolver, roles] of Object.entries(this.data1)) {
for (const roles of Object.values(this.data1)) {
for (const role of this.roles) {
this.$set(roles, role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleRow(resolver, checked) {
for (let role in this.data1[resolver]) {
this.$set(this.data1[resolver], role, checked)
this.toggleCell(resolver, role, checked)
}
},
toggleAll(checked) {
this.disableSaveButton = false;
for (let path in this.data1) {
this.rowToggle[path] = checked;
}
for (let role of this.roles) {
this.columnToggle[role] = checked;
}
for (let roles of Object.values(this.data1)) {
for (let role of this.roles) {
this.$set(roles, role, checked)
}
}
},
toggleCell(resolver, role, checked) {
this.disableSaveButton = false;
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]));
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled));
},
initColumnCheckBox() {
for (let role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role]);
}
},
initRowCheckBox() {
for (let path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) this.roles = [...this.roles, role];
return v;
}).length;
}
},
async aclInit() {
this.disableSaveButton = true;
// this.policyPath = await this.sqlMgr.projectGetGqlPolicyPath({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// });
this.policyPath = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'projectGetGqlPolicyPath', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}]);
try {
console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify(await this.sqlMgr.importFresh({path: this.policyPath})));
this.data1 = JSON.parse(JSON.stringify(await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', {path: this.policyPath}])));
this.initColumnCheckBox();
this.initRowCheckBox();
} catch (e) {
console.log(e)
}
},
async save() {
try {
// this.sqlMgr.writeFile({path: this.policyPath, data: `module.exports = ${JSON.stringify(this.data1, 0, 2)}`})
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports = ${JSON.stringify(this.data1, 0, 2)}`
}])
this.disableSaveButton = true;
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000);
} catch (e) {
console.log(e);
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000);
}
}
},
data() {
return {
disableSaveButton: true,
search: '',
policyPath: '',
columnToggle: {},
rowToggle: {},
roles: [
'creator',
'editor',
'guest'
],
data1: null
toggleCell (resolver, role, checked) {
this.disableSaveButton = false
this.$set(this.columnToggle, role, Object.values(this.data1).some(roles => roles[role]))
this.$set(this.rowToggle, resolver, Object.values(this.data1[resolver]).some(enabled => enabled))
},
initColumnCheckBox () {
for (const role of this.roles) {
this.columnToggle[role] = Object.values(this.data1).some(roles => roles[role])
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
allToggle: {
get() {
return this.data1 && Object.values(this.data1).some(roles => Object.values(roles).some(v => v))
},
set(checked) {
this.toggleAll(checked)
}
initRowCheckBox () {
for (const path in this.data1) {
this.rowToggle[path] = Object.entries(this.data1[path]).filter(([role, v]) => {
if (!this.roles.includes(role)) { this.roles = [...this.roles, role] }
return v
}).length
}
},
async created() {
await this.aclInit();
async aclInit () {
this.disableSaveButton = true
// this.policyPath = await this.sqlMgr.projectGetGqlPolicyPath({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias,
// tn: this.nodes.tn
// });
this.policyPath = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'projectGetGqlPolicyPath', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn: this.nodes.tn
}])
try {
console.log(this.policyPath, this.data1)
// this.data1 = JSON.parse(JSON.stringify(await this.sqlMgr.importFresh({path: this.policyPath})));
this.data1 = JSON.parse(JSON.stringify(await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'importFresh', { path: this.policyPath }])))
this.initColumnCheckBox()
this.initRowCheckBox()
} catch (e) {
console.log(e)
}
},
watch: {}
async save () {
try {
// this.sqlMgr.writeFile({path: this.policyPath, data: `module.exports = ${JSON.stringify(this.data1, 0, 2)}`})
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'writeFile', {
path: this.policyPath,
data: `module.exports = ${JSON.stringify(this.data1, 0, 2)}`
}])
this.disableSaveButton = true
this.$toast.success(`${this.policyPath} updated successfully`).goAway(3000)
} catch (e) {
console.log(e)
this.$toast.error(`${this.policyPath} updating failed`).goAway(3000)
}
}
},
computed: {
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
allToggle: {
get () {
return this.data1 && Object.values(this.data1).some(roles => Object.values(roles).some(v => v))
},
set (checked) {
this.toggleAll(checked)
}
}
},
watch: {},
async created () {
await this.aclInit()
}
}
</script>
<style scoped>

View File

@@ -1,84 +1,83 @@
<template>
<v-dialog
v-model="dialogShow"
width="60%"
v-model="dialogShow">
>
<v-card>
<h5 class="body-1 pa-1 text-center">Paste JSON/JSON5 String</h5>
<h5 class="body-1 pa-1 text-center">
Paste JSON/JSON5 String
</h5>
<div class="d-flex" style="height: 100%; width:100%">
<div style="" class="text-center flex-shrink-1 d-flex flex-column">
<x-icon
icon-class="mx-2 mt-3 elevation-1"
color="success success"
@click="$emit('load',jsonContent)"
>mdi-send
>
mdi-send
</x-icon>
</div>
<div class="flex-grow-1" style="overflow:auto;height:100%">
<monaco-json-editor
:validate="false"
v-model="jsonContent"
lang="json" style="height: 500px;width: 100%;min-width: 250px"
></monaco-json-editor>
:validate="false"
lang="json"
style="height: 500px;width: 100%;min-width: 250px"
/>
</div>
</div>
</v-card>
</v-dialog>
</template>
<script>
import {MonacoJsonEditor} from '../../../monaco/index'
import { MonacoJsonEditor } from '../../../monaco/index'
export default {
name: "jsonToColumn",
data() {
return {
jsonContent: JSON.stringify({first_name: "James", last_name: "Bond"}, 0, 2),
activeTab: 0
};
},
methods: {},
beforeCreated() {
},
created() {
},
mounted() {
},
beforeDestroy() {
},
destroy() {
},
validate({params}) {
return true;
},
head() {
return {};
},
props: {
value: Boolean,
show: Boolean
},
computed: {
dialogShow: {
get() {
return this.show;
}, set(val) {
this.$emit('update:show', val);
}
export default {
name: 'JsonToColumn',
directives: {},
components: { MonacoJsonEditor },
validate ({ params }) {
return true
},
props: {
value: Boolean,
show: Boolean
},
data () {
return {
jsonContent: JSON.stringify({ first_name: 'James', last_name: 'Bond' }, 0, 2),
activeTab: 0
}
},
head () {
return {}
},
computed: {
dialogShow: {
get () {
return this.show
},
set (val) {
this.$emit('update:show', val)
}
},
watch: {},
directives: {},
components: {MonacoJsonEditor}
}
},
watch: {},
created () {
},
mounted () {
},
beforeDestroy () {
},
methods: {},
beforeCreated () {
},
destroy () {
}
}
</script>
<style scoped>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,52 +2,57 @@
<div class="">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (table)',
disabled: true,
href: '#'
}]" divider=">" small >
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (table)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
small
color="primary"
class="primary"
@click="loadConstraintList"
>
<v-icon left>refresh</v-icon>
<v-icon left>
refresh
</v-icon>
Refresh
</v-btn
>
</v-btn>
<v-btn
small
color="error "
@click="deleteTable('showDialog')"
class="error text-right"
>Delete Table
</v-btn
@click="deleteTable('showDialog')"
>
Delete Table
</v-btn>
<v-btn
icon
class="text-right">
class="text-right"
>
<v-icon>mdi-help-circle-outline</v-icon>
</v-btn>
</v-toolbar>
<v-data-table
@@ -56,7 +61,7 @@
:items="constraints"
footer-props.items-per-page-options="30"
>
<template v-slot:item="props">
<template #item="props">
<td>{{ props.item.cstn }}</td>
<td>{{ props.item.cst }}</td>
<td>{{ props.item.cn }}</td>
@@ -67,62 +72,62 @@
</template>
<script>
import {mapGetters, mapActions} from "vuex";
import { mapGetters } from 'vuex'
export default {
data() {
return {
constraints: [],
headers: [
{
text: "Constraint",
sortable: false
},
{text: "Constraint Type", sortable: false},
{text: "Column Name", sortable: false},
{text: "Constraint Ordinal Position", sortable: false}
]
};
},
methods: {
async loadConstraintList() {
if (this.newTable) return;
// console.log("env: this.nodes.env", this.nodes.env, this.nodes.dbAlias);
const client = await this.sqlMgr.projectGetSqlClient({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
});
const result = await client.constraintList({
tn: this.nodes.tn
});
// console.log("cons", result.data.list);
this.constraints = result.data.list;
}
},
computed: {...mapGetters({sqlMgr: "sqlMgr/sqlMgr"})},
export default {
data () {
return {
constraints: [],
headers: [
{
text: 'Constraint',
sortable: false
},
{ text: 'Constraint Type', sortable: false },
{ text: 'Column Name', sortable: false },
{ text: 'Constraint Ordinal Position', sortable: false }
]
}
},
methods: {
async loadConstraintList () {
if (this.newTable) { return }
// console.log("env: this.nodes.env", this.nodes.env, this.nodes.dbAlias);
const client = await this.sqlMgr.projectGetSqlClient({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
})
const result = await client.constraintList({
tn: this.nodes.tn
})
// console.log("cons", result.data.list);
this.constraints = result.data.list
}
},
computed: { ...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }) },
beforeCreated() {
},
created() {
this.loadConstraintList();
},
mounted() {
},
beforeDestroy() {
},
destroy() {
},
validate({params}) {
return true;
},
head() {
return {};
},
props: ["nodes", "newTable", "deleteTable"],
watch: {},
directives: {},
components: {}
};
beforeCreated () {
},
watch: {},
created () {
this.loadConstraintList()
},
mounted () {
},
beforeDestroy () {
},
destroy () {
},
directives: {},
components: {},
validate ({ params }) {
return true
},
head () {
return {}
},
props: ['nodes', 'newTable', 'deleteTable']
}
</script>
<style scoped>

View File

@@ -1,65 +1,92 @@
<!-- eslint-disable -->
<template>
<v-skeleton-loader v-if="loading" type="text@3"></v-skeleton-loader>
<div class="caption text-left" v-else>{{ Array.isArray(value) ? '[' : '{' }}
<v-skeleton-loader v-if="loading" type="text@3" />
<div v-else class="caption text-left">
{{ Array.isArray(value) ? '[' : '{' }}
<ul>
<template v-if="Array.isArray(value)">
<li v-for="(v,i) in value">
<custom-acl :nodes="nodes" :table="table" v-model="value[i]"></custom-acl>
<custom-acl v-model="value[i]" :nodes="nodes" :table="table" />
</li>
<li class="caption add" @click="addConditionObj">
add +
</li>
<li @click="addConditionObj" class="caption add">add +</li>
</template>
<template v-else>
<li v-for="(key,i) in keys" :key="key" v-if="key !== 'relationType'" :class="{empty: !keys[i]}">
<li v-for="(key,i) in keys" v-if="key !== 'relationType'" :key="key" :class="{empty: !keys[i]}">
<div class="d-inline">
<!-- <span contenteditable v-text="key" class="key"></span>-->
<select class="caption" v-model="keys[i]" :ref="'keySelect'+i" @change="onKeyChange(i,key)">
<select :ref="'keySelect'+i" v-model="keys[i]" class="caption" @change="onKeyChange(i,key)">
<template v-if="table">
<optgroup label="columns" v-if="columns && columns.length">
<option v-for="col in columns" v-show="!keys.includes(col)" :data-value="col">{{ col }}</option>
<optgroup v-if="columns && columns.length" label="columns">
<option v-for="col in columns" v-show="!keys.includes(col)" :data-value="col">
{{ col }}
</option>
</optgroup>
<optgroup label="Has Many" v-if="hmList && hmList.length">
<option v-for="hm in hmList" v-show="!keys.includes(hm)" data-relation-type="hm" :data-table="hm">{{
<optgroup v-if="hmList && hmList.length" label="Has Many">
<option v-for="hm in hmList" v-show="!keys.includes(hm)" data-relation-type="hm" :data-table="hm">
{{
hm
}}
</option>
</optgroup>
<optgroup label="BelongsTo" v-if="btList && btList.length">
<option v-for="bt in btList" v-show="!keys.includes(bt)" data-relation-type="bt" :data-table="bt">{{
<optgroup v-if="btList && btList.length" label="BelongsTo">
<option v-for="bt in btList" v-show="!keys.includes(bt)" data-relation-type="bt" :data-table="bt">
{{
bt
}}
</option>
</optgroup>
<optgroup label="Logical Operators">
<option v-for="op in logicOp" data-logical-op="true" :data-op="op">{{ op }}</option>
<option v-for="op in logicOp" data-logical-op="true" :data-op="op">
{{ op }}
</option>
</optgroup>
</template>
<optgroup label="Comparison Operators" v-else>
<option v-for="op in compOp" v-show="!keys.includes(op)" :data-op="op">{{ op }}</option>
<optgroup v-else label="Comparison Operators">
<option v-for="op in compOp" v-show="!keys.includes(op)" :data-op="op">
{{ op }}
</option>
</optgroup>
</select>
<div class="delete-wrapper">
<x-icon color="red" icon-class="delete" v-if="typeof value[key] !== 'string'" x-small
@click="deleteCondition(key)">
<x-icon
v-if="typeof value[key] !== 'string'"
color="red"
icon-class="delete"
x-small
@click="deleteCondition(key)"
>
mdi-delete-outline
</x-icon>
</div>
<span class="separator"> : </span>
</div>
<template v-if="typeof value[key] === 'string'"><input type="text" v-model="value[key]"
class="value caption"/></template>
<template v-if="typeof value[key] === 'string'">
<input
v-model="value[key]"
type="text"
class="value caption"
>
</template>
<!-- @input="e => $set(value,key,e.target.innerHTML)" -->
<template v-else>
<span class="caption grey--text" v-if="value[key].relationType">
<span v-if="value[key].relationType" class="caption grey--text">
{{ `'${table}' ${value[key].relationType === 'bt' ? 'BelongsTo' : 'HasMany'} '${key}'` }}
</span>
<custom-acl :nodes="nodes" v-model="value[key]" :table="(value[key].relationType ? key : null)
|| (logicOp.includes(key) ? table : null)"
></custom-acl>
<custom-acl
v-model="value[key]"
:nodes="nodes"
:table="(value[key].relationType ? key : null)
|| (logicOp.includes(key) ? table : null)"
/>
</template>
</li>
<li v-if="table" @click="addConditionProp" class="caption add">add +</li>
<li v-if="table" class="caption add" @click="addConditionProp">
add +
</li>
</template>
</ul>
{{ Array.isArray(value) ? '] ,' : '} ,' }}
@@ -68,11 +95,16 @@
<script>
import {insertKey} from "../../../helpers/xutils";
import { insertKey } from '../../../helpers/xutils'
export default {
name: "custom-acl",
name: 'CustomAcl',
props: [
'value',
'table',
'column',
'nodes'
],
data: () => ({
columns: null,
hmList: null,
@@ -85,111 +117,102 @@ export default {
],
loading: false
}),
computed: {
keys () {
return Object.keys(this.value)
}
},
created () {
},
async mounted () {
await this.loadTableMetaDetails()
},
methods: {
onKeyChange(i, key) {
let value = JSON.parse(JSON.stringify(this.value));
onKeyChange (i, key) {
let value = JSON.parse(JSON.stringify(this.value))
const selected = this.$refs[`keySelect${i}`][0].selectedOptions;
let selectedVal = '';
if (selected && selected[0])
selectedVal = selected[0].dataset;
const selected = this.$refs[`keySelect${i}`][0].selectedOptions
let selectedVal = ''
if (selected && selected[0]) { selectedVal = selected[0].dataset }
if (selectedVal.value) {
delete value[key];
value = insertKey(this.keys[i], {eq: ""}, value, i);
delete value[key]
value = insertKey(this.keys[i], { eq: '' }, value, i)
} else if (selectedVal.relationType === 'hm') {
delete value[key];
delete value[key]
value = insertKey(selectedVal.table, {
relationType: "hm",
"": ""
}, value, i);
relationType: 'hm',
'': ''
}, value, i)
} else if (selectedVal.relationType === 'bt') {
delete value[key];
delete value[key]
value = insertKey(selectedVal.table, {
relationType: "bt",
"": ""
}, value, i);
relationType: 'bt',
'': ''
}, value, i)
} else if (selectedVal.op) {
const oldVal = value[key];
delete value[key];
const oldVal = value[key]
delete value[key]
if (selectedVal.logicalOp) {
if (selectedVal.op === '_not') {
value = insertKey(selectedVal.op, {
"": ""
}, value, i);
'': ''
}, value, i)
} else {
value = insertKey(selectedVal.op, [{
"": ""
}], value, i);
'': ''
}], value, i)
}
} else {
value[selectedVal.op] = oldVal;
value[selectedVal.op] = oldVal
}
}
this.$emit('input', value);
this.$emit('input', value)
},
addConditionProp() {
const value = JSON.parse(JSON.stringify(this.value));
value[""] = "";
this.$emit('input', value);
addConditionProp () {
const value = JSON.parse(JSON.stringify(this.value))
value[''] = ''
this.$emit('input', value)
},
addConditionObj() {
const value = JSON.parse(JSON.stringify(this.value));
addConditionObj () {
const value = JSON.parse(JSON.stringify(this.value))
value.push({
"": ""
'': ''
})
this.$emit('input', value);
this.$emit('input', value)
},
deleteCondition(key) {
const value = JSON.parse(JSON.stringify(this.value));
delete value[key];
this.$emit('input', value);
deleteCondition (key) {
const value = JSON.parse(JSON.stringify(this.value))
delete value[key]
this.$emit('input', value)
},
async loadTableMetaDetails() {
async loadTableMetaDetails () {
if (this.table) {
this.loading = true;
this.loading = true
try {
const meta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'tableXcModelGet', {
tn: this.table
}]);
}])
const metaObj = JSON.parse(meta.meta)
this.columns = metaObj.columns.map(v => v.cn);
this.columns = metaObj.columns.map(v => v.cn)
console.log(metaObj)
this.hmList = metaObj.hasMany.map(v => v.tn)
this.btList = metaObj.belongsTo.map(v => v.rtn)
} catch (e) {
console.log('load meta', this.table, e);
console.log('load meta', this.table, e)
} finally {
this.loading = false;
this.loading = false
}
}
}
},
props: [
'value',
'table',
'column',
'nodes'
],
created() {
},
async mounted() {
await this.loadTableMetaDetails();
},
computed: {
keys() {
return Object.keys(this.value);
}
}
}
</script>
<style scoped lang="scss">
.key, .value {
min-width: 40px;
border-bottom: 1px dotted #bbbbbb;
@@ -246,12 +269,10 @@ div:hover > .delete-wrapper .delete {
//opacity: 1;
}
select, input {
color: var(--v-primary-text);
}
</style>
<!--
/**

File diff suppressed because it is too large Load Diff

View File

@@ -1,228 +1,244 @@
<template>
<div class="graphql-logic">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: (nodes.tn || nodes.view_name) + ' (APIs)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: (nodes.tn || nodes.view_name) + ' (APIs)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn small outlined @click="showSwagger = !showSwagger" class="caption text-capitalize" color="primary">
<v-icon small color="primary"> {{showSwagger ?'mdi-eye-off-outline' : 'mdi-eye-outline'}}</v-icon> &nbsp;
<v-spacer />
<v-btn small outlined class="caption text-capitalize" color="primary" @click="showSwagger = !showSwagger">
<v-icon small color="primary">
{{ showSwagger ?'mdi-eye-off-outline' : 'mdi-eye-outline' }}
</v-icon> &nbsp;
{{ showSwagger ? 'Hide Schema' : 'Show Schema' }}</v-btn>
<x-btn outlined tooltip="Reload Rows"
color="primary"
small
v-ge="['rows','reload']"
@click="loadSchema(); loadResolvers();">
<v-icon small left>refresh</v-icon>
{{ showSwagger ? 'Hide Schema' : 'Show Schema' }}
</v-btn>
<x-btn
v-ge="['rows','reload']"
outlined
tooltip="Reload Rows"
color="primary"
small
@click="loadSchema(); loadResolvers();"
>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
</v-toolbar>
<v-row class="mx-0" align="stretch">
<v-col v-if="showSwagger">
<v-card class="flex-shrink-1">
<div class="text-center" style="padding: 3px">
<span class="title schema-card-title">Schema</span>
</div>
<div class="d-flex pa-3">
<v-spacer></v-spacer>
<v-spacer />
<x-btn outlined :tooltip="`Compare GQL schema history of ${nodes.tn}`"
color="primary"
x-small
:disabled="loading || !schemaHistory.length"
@click.prevent="schemaDiffDialog = true">
<v-icon small left>mdi-source-branch</v-icon>
History <span class="history-count" v-if="schemaHistory.length">({{ schemaHistory.length }})</span>
<x-btn
outlined
:tooltip="`Compare GQL schema history of ${nodes.tn}`"
color="primary"
x-small
:disabled="loading || !schemaHistory.length"
@click.prevent="schemaDiffDialog = true"
>
<v-icon small left>
mdi-source-branch
</v-icon>
History <span v-if="schemaHistory.length" class="history-count">({{ schemaHistory.length }})</span>
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
x-small
:disabled="loading"
v-ge="['rows','save']"
@click.prevent="saveSchema">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['rows','save']"
outlined
tooltip="Save Changes"
color="primary"
x-small
:disabled="loading"
@click.prevent="saveSchema"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</div>
<monaco-editor
v-model="schema"
theme=""
style="min-height:500px;"
>
</monaco-editor>
/>
</v-card>
</v-col>
<v-col>
<v-card class="flex-shrink-1" style="" v-if="filteredData.length">
<v-card v-if="filteredData.length" class="flex-shrink-1" style="">
<div class="text-center" style="padding: 3px">
<span class="title schema-card-title">Resolvers & Middlewares</span>
</div>
<v-simple-table v-if="resolvers" dense>
<!-- style="width: auto; min-width: 500px">-->
<thead>
<!-- <tr>-->
<!-- <tr>-->
<!-- <th colspan="3" class="text-center">-->
<!-- <th colspan="3" class="text-center">-->
<!-- <div class="text-center">-->
<!-- <div class="text-center">-->
<!-- <span class="title card-title">Resolvers</span>-->
<!-- </div>-->
<!-- <span class="title card-title">Resolvers</span>-->
<!-- </div>-->
<!-- </th>-->
<!-- </tr>-->
<tr>
<th colspan="3" class="text-center">
<v-text-field dense hide-details class="ma-2" :placeholder="`Search '${nodes.tn}' resolvers`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
</th>
</tr>
<!-- </th>-->
<!-- </tr>-->
<tr>
<th colspan="3" class="text-center">
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search '${nodes.tn}' resolvers`"
prepend-inner-icon="search"
outlined
/>
</th>
</tr>
</thead>
<tbody>
<template v-for="({resolver,title,functions},i) in filteredData">
<tr v-if="resolver" :key="i"
>
<td width="20" class="px-0">
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{ resolver }}</span>
</template>
<span>{{ resolver }}</span>
</v-tooltip>
</td>
<td
width="60" class="pa-1 text-center method-cell">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-hover v-slot:default="{ hover }">
<v-icon @click="showSourceCode(resolver,functions)" small
:color="hover ? 'primary':''" v-on="on">mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
</v-tooltip>
</td>
</tr>
<tr v-else :key="i"
>
<td width="20" class="px-0">
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on" class="">{{ title }} - Middleware</span>
</template>
<span>{{ title }} - Middleware</span>
</v-tooltip>
</td>
<td
width="60" class="pa-1 text-center method-cell">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-hover v-slot:default="{ hover }">
<v-icon @click="showMiddlewareSourceCode(title,functions)" small
:color="hover ? 'primary':''" v-on="on">mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
</v-tooltip>
</td>
</tr>
</template>
<template v-for="({resolver,title,functions},i) in filteredData">
<tr
v-if="resolver"
:key="i"
>
<td width="20" class="px-0" />
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ resolver }}</span>
</template>
<span>{{ resolver }}</span>
</v-tooltip>
</td>
<td
width="60"
class="pa-1 text-center method-cell"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-hover v-slot="{ hover }">
<v-icon
small
:color="hover ? 'primary':''"
@click="showSourceCode(resolver,functions)"
v-on="on"
>
mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
</v-tooltip>
</td>
</tr>
<tr
v-else
:key="i"
>
<td width="20" class="px-0" />
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span class="" v-on="on">{{ title }} - Middleware</span>
</template>
<span>{{ title }} - Middleware</span>
</v-tooltip>
</td>
<td
width="60"
class="pa-1 text-center method-cell"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-hover v-slot="{ hover }">
<v-icon
small
:color="hover ? 'primary':''"
@click="showMiddlewareSourceCode(title,functions)"
v-on="on"
>
mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
</v-tooltip>
</td>
</tr>
</template>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
</v-col>
</v-row>
<gql-handler-code-editor :nodes="nodes"
:functions="selectedFunctions"
:resolver="editResolver"
:is-middleware="isMiddleware"
v-model="showCodeEditor"></gql-handler-code-editor>
<gql-handler-code-editor
v-model="showCodeEditor"
:nodes="nodes"
:functions="selectedFunctions"
:resolver="editResolver"
:is-middleware="isMiddleware"
/>
<v-dialog v-model="schemaDiffDialog" scrollable min-width="600px">
<v-card>
<xc-diff
v-model="schema"
:history="schemaHistory"
></xc-diff>
/>
</v-card>
</v-dialog>
</div>
</template>
<script>
import GqlHandlerCodeEditor from "@/components/project/gqlHandlerCodeEditor";
import MonacoTsEditor from "@/components/monaco/MonacoTsEditor";
import MonacoEditor from "@/components/monaco/MonacoEditor";
import XcDiff from "@/components/xcDiff";
import GqlHandlerCodeEditor from '@/components/project/gqlHandlerCodeEditor'
import MonacoEditor from '@/components/monaco/MonacoEditor'
import XcDiff from '@/components/xcDiff'
export default {
name: "logic-gql",
components: {XcDiff, MonacoEditor, MonacoTsEditor, GqlHandlerCodeEditor},
name: 'LogicGql',
components: { XcDiff, MonacoEditor, GqlHandlerCodeEditor },
props: ['nodes'],
data: () => ({
showSwagger:false,
showSwagger: false,
selectedFunctions: null,
schema: '',
schemaHistory: [''],
@@ -236,22 +252,31 @@ export default {
schemaDiffDialog: false,
isMiddleware: false
}),
computed: {
filteredData () {
return this.resolvers.filter(({ resolver }) => !resolver || (!this.search || resolver.toLowerCase().includes(this.search.toLowerCase())))
}
},
async created () {
await this.loadResolvers()
await this.loadSchema()
},
methods: {
showSourceCode(resolver, functions) {
this.selectedFunctions = functions;
this.editResolver = resolver;
this.isMiddleware = false;
this.showCodeEditor = true;
showSourceCode (resolver, functions) {
this.selectedFunctions = functions
this.editResolver = resolver
this.isMiddleware = false
this.showCodeEditor = true
},
showMiddlewareSourceCode(table, functions) {
this.selectedFunctions = functions;
this.editResolver = table;
this.isMiddleware = true;
this.showCodeEditor = true;
showMiddlewareSourceCode (table, functions) {
this.selectedFunctions = functions
this.editResolver = table
this.isMiddleware = true
this.showCodeEditor = true
},
async saveSchema() {
this.edited = false;
async saveSchema () {
this.edited = false
try {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
@@ -259,43 +284,34 @@ export default {
}, 'xcModelSchemaSet', {
tn: this.nodes.tn || this.nodes.view_name,
schema: this.schema
}]);
this.$toast.success('Successfully updated validations').goAway(3000);
}])
this.$toast.success('Successfully updated validations').goAway(3000)
} catch (e) {
this.$toast.error('Failed to update validations').goAway(3000);
this.$toast.error('Failed to update validations').goAway(3000)
}
},
async loadSchema() {
async loadSchema () {
const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'tableXcModelGet', {
tn: this.nodes.tn || this.nodes.view_name
}]);
this.schema = tableMeta.schema;
}])
this.schema = tableMeta.schema
if (tableMeta.schema_previous) {
this.schemaHistory = JSON.parse(tableMeta.schema_previous).reverse();
this.schemaHistory = JSON.parse(tableMeta.schema_previous).reverse()
} else {
this.schemaHistory = [];
this.schemaHistory = []
}
},
async loadResolvers() {
async loadResolvers () {
this.resolvers = (await this.$store.dispatch('sqlMgr/ActSqlOp', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'xcResolverPolicyGet', {
tn: this.nodes.tn || this.nodes.view_name
}])).data.list;
}
},
async created() {
await this.loadResolvers();
await this.loadSchema();
},
computed: {
filteredData() {
return this.resolvers.filter(({resolver}) => !resolver || (!this.search || resolver.toLowerCase().indexOf(this.search.toLowerCase()) > -1));
}])).data.list
}
}
}
@@ -303,7 +319,6 @@ export default {
<style scoped lang="scss">
@import "~vuetify/src/styles/styles";
$text-field-outlined-fieldset-padding: 0px;

View File

@@ -1,181 +1,199 @@
<!-- eslint-disable -->
<template>
<div>
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn || nodes.view_name + ' (APIs)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn || nodes.view_name + ' (APIs)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn small outlined @click="showSwagger = !showSwagger" class="caption text-capitalize" color="primary">
<v-icon small color="primary"> {{showSwagger ?'mdi-eye-off-outline' : 'mdi-eye-outline'}}</v-icon> &nbsp;
<v-spacer />
<v-btn small outlined class="caption text-capitalize" color="primary" @click="showSwagger = !showSwagger">
<v-icon small color="primary">
{{ showSwagger ?'mdi-eye-off-outline' : 'mdi-eye-outline' }}
</v-icon> &nbsp;
{{ showSwagger ? 'Hide Protobuf' : 'Show Protobuf' }}</v-btn>
<x-btn outlined tooltip="Reload Rows"
color="primary"
small
v-ge="['rows','reload']"
@click="loadSchema(); loadRpcs();">
<v-icon small left>refresh</v-icon>
{{ showSwagger ? 'Hide Protobuf' : 'Show Protobuf' }}
</v-btn>
<x-btn
v-ge="['rows','reload']"
outlined
tooltip="Reload Rows"
color="primary"
small
@click="loadSchema(); loadRpcs();"
>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
</v-toolbar>
<v-container fluid>
<v-row>
<v-col v-if="showSwagger">
<v-card class="flex-shrink-1">
<div class="text-center" style="padding: 3px">
<span class="title schema-card-title">Protobuf</span>
</div>
<div class="d-flex pa-3">
<v-spacer></v-spacer>
<x-btn outlined :tooltip="`Compare GQL schema history of ${nodes.tn}`"
color="primary"
x-small
:disabled="loading || !schemaHistory.length"
@click.prevent="schemaDiffDialog = true">
<v-icon small left>mdi-source-branch</v-icon>
History <span class="history-count" v-if="schemaHistory.length">({{ schemaHistory.length }})</span>
<v-spacer />
<x-btn
outlined
:tooltip="`Compare GQL schema history of ${nodes.tn}`"
color="primary"
x-small
:disabled="loading || !schemaHistory.length"
@click.prevent="schemaDiffDialog = true"
>
<v-icon small left>
mdi-source-branch
</v-icon>
History <span v-if="schemaHistory.length" class="history-count">({{ schemaHistory.length }})</span>
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
x-small
:disabled="loading"
v-ge="['rows','save']"
@click.prevent="saveMessageAndRpc">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['rows','save']"
outlined
tooltip="Save Changes"
color="primary"
x-small
:disabled="loading"
@click.prevent="saveMessageAndRpc"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</div>
<p class="caption pa-1">Messages</p>
<p class="caption pa-1">
Messages
</p>
<monaco-editor
v-model="messages"
theme=""
style="min-height:250px;"
/>
<p class="caption pa-1 mt-2">rpcs</p>
<p class="caption pa-1 mt-2">
rpcs
</p>
<monaco-editor
v-model="services"
theme=""
style="min-height:250px;"
/>
</v-card>
</v-col>
<v-col>
<!-- <div class="d-flex justify-center">-->
<v-card class="flex-shrink-1" style="" v-if="rpcServices && filteredData.length">
<v-text-field dense hide-details class="ma-2 mt-2"
:placeholder="`Search ${nodes.tn || nodes.view_name} rpc services`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-card v-if="rpcServices && filteredData.length" class="flex-shrink-1" style="">
<v-text-field
v-model="search"
dense
hide-details
class="ma-2 mt-2"
:placeholder="`Search ${nodes.tn || nodes.view_name} rpc services`"
prepend-inner-icon="search"
outlined
/>
<v-simple-table v-if="rpcServices" dense style="width: auto; min-width: 500px">
<thead>
<tr>
<th colspan="2" class="text-center">
<div class="text-center">
<span class="title">RPC Services</span>
</div>
</th>
<th
width="60"
class="text-center">
<span class="title"></span>
</th>
</tr>
<tr>
<th colspan="2" class="text-center">
<div class="text-center">
<span class="title">RPC Services</span>
</div>
</th>
<th
width="60"
class="text-center"
>
<span class="title" />
</th>
</tr>
</thead>
<tbody>
<tr v-for="{service,...rest} in filteredData"
>
<td width="20" class="px-0">
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{ service }}</span>
</template>
<span>{{ service }}</span>
</v-tooltip>
</td>
<td
width="60" class="pa-1 text-center method-cell">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-hover v-slot:default="{ hover }">
<v-icon @click="showSourceCode(service,rest)" small
:color="hover ? 'primary':''" v-on="on">mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
</v-tooltip>
</td>
</tr>
<tr
v-for="{service,...rest} in filteredData"
>
<td width="20" class="px-0" />
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ service }}</span>
</template>
<span>{{ service }}</span>
</v-tooltip>
</td>
<td
width="60"
class="pa-1 text-center method-cell"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-hover v-slot="{ hover }">
<v-icon
small
:color="hover ? 'primary':''"
@click="showSourceCode(service,rest)"
v-on="on"
>
mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
</v-tooltip>
</td>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
</v-col>
<!-- </div>-->
</v-row>
</v-container>
<grpc-handler-code-editor :nodes="nodes"
:service="editService"
:serviceData="editServiceData"
v-model="showCodeEditor"></grpc-handler-code-editor>
<grpc-handler-code-editor
v-model="showCodeEditor"
:nodes="nodes"
:service="editService"
:service-data="editServiceData"
/>
<v-dialog v-model="schemaDiffDialog" scrollable min-width="600px">
<v-card>
<xc-diff
v-model="messages"
:history="schemaHistory"
></xc-diff>
/>
</v-card>
</v-dialog>
</div>
@@ -183,15 +201,15 @@
<script>
import GrpcHandlerCodeEditor from "@/components/project/grpcHandlerCodeEditor";
import MonacoEditor from "~/components/monaco/MonacoEditor";
import GrpcHandlerCodeEditor from '@/components/project/grpcHandlerCodeEditor'
import MonacoEditor from '~/components/monaco/MonacoEditor'
export default {
name: "logic-grpc",
components: {GrpcHandlerCodeEditor, MonacoEditor},
name: 'LogicGrpc',
components: { GrpcHandlerCodeEditor, MonacoEditor },
props: ['nodes'],
data: () => ({
showSwagger:false,
showSwagger: false,
editServiceData: null,
loading: false,
search: '',
@@ -200,11 +218,11 @@ export default {
'get', 'post', 'put', 'delete'
],
methodColor: {
'get': 'success',
'post': 'warning',
'put': 'info',
'patch': 'secondary',
'delete': 'error'
get: 'success',
post: 'warning',
put: 'info',
patch: 'secondary',
delete: 'error'
},
editService: '',
showCodeEditor: false,
@@ -213,38 +231,47 @@ export default {
schemaHistory: [],
schemaDiffDialog: false
}),
computed: {
filteredData () {
return this.rpcServices.filter(({ service }) => service && (!this.search || service.toLowerCase().includes(this.search.toLowerCase())))
}
},
async created () {
await this.loadRpcs()
await this.loadSchema()
},
methods: {
showSourceCode(service, serviceData) {
this.editService = service;
this.showCodeEditor = true;
this.editServiceData = serviceData;
showSourceCode (service, serviceData) {
this.editService = service
this.showCodeEditor = true
this.editServiceData = serviceData
},
async loadSchema() {
async loadSchema () {
const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'tableXcModelGet', {
tn: this.nodes.tn || this.nodes.view_name
}]);
this.messages = tableMeta.messages;
this.services = tableMeta.services;
}])
this.messages = tableMeta.messages
this.services = tableMeta.services
if (tableMeta.schema_previous) {
this.schemaHistory = JSON.parse(tableMeta.schema_previous).reverse();
this.schemaHistory = JSON.parse(tableMeta.schema_previous).reverse()
} else {
this.schemaHistory = [];
this.schemaHistory = []
}
},
async loadRpcs() {
async loadRpcs () {
this.rpcServices = (await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
dbAlias: this.nodes.dbAlias
}, 'xcRpcPolicyGet', {
tn: this.nodes.tn || this.nodes.view_name
}])).data.list;
}])).data.list
},
async saveMessageAndRpc() {
this.edited = false;
async saveMessageAndRpc () {
this.edited = false
try {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
@@ -252,21 +279,12 @@ export default {
}, 'xcModelMessagesAndServicesSet', {
tn: this.nodes.tn || this.nodes.view_name,
messages: this.messages,
services: this.services,
}]);
this.$toast.success('Successfully updated protobuf messages and rpcs').goAway(3000);
services: this.services
}])
this.$toast.success('Successfully updated protobuf messages and rpcs').goAway(3000)
} catch (e) {
this.$toast.error('Failed to update validations').goAway(3000);
this.$toast.error('Failed to update validations').goAway(3000)
}
},
},
async created() {
await this.loadRpcs();
await this.loadSchema();
},
computed: {
filteredData() {
return this.rpcServices.filter(({service}) => service && (!this.search || service.toLowerCase().indexOf(this.search.toLowerCase()) > -1));
}
}
}

View File

@@ -1,205 +1,221 @@
<!-- eslint-disable -->
<template>
<div>
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: name + ' (APIs)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: name + ' (APIs)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn small outlined @click="showSwagger = !showSwagger" class="caption text-capitalize" color="primary">
<v-icon small color="primary"> {{ showSwagger ? 'mdi-eye-off-outline' : 'mdi-eye-outline' }}</v-icon> &nbsp;
<v-btn small outlined class="caption text-capitalize" color="primary" @click="showSwagger = !showSwagger">
<v-icon small color="primary">
{{ showSwagger ? 'mdi-eye-off-outline' : 'mdi-eye-outline' }}
</v-icon> &nbsp;
{{ showSwagger ? 'Hide Swagger' : 'Show Swagger' }}
</v-btn>
<x-btn outlined tooltip="Reload Rows"
color="primary"
small
v-ge="['rows','reload']" btn.class="caption text-capitalize"
@click="loadSwaggerDoc(); loadRoutes();">
<v-icon small left>refresh</v-icon>
<x-btn
v-ge="['rows','reload']"
outlined
tooltip="Reload Rows"
color="primary"
small
btn.class="caption text-capitalize"
@click="loadSwaggerDoc(); loadRoutes();"
>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
</v-toolbar>
<v-text-field dense hide-details class="ma-2" :placeholder="`Search ${name} routes`"
prepend-inner-icon="search" v-model="search"
outlined></v-text-field>
<v-text-field
v-model="search"
dense
hide-details
class="ma-2"
:placeholder="`Search ${name} routes`"
prepend-inner-icon="search"
outlined
/>
<!-- <div class="d-flex justify-center">-->
<v-container fluid>
<v-row>
<v-col v-if="showSwagger">
<v-card>
<div class="text-center" style="padding: 3px">
<span class="title schema-card-title">Swagger JSON</span>
</div>
<div class="d-flex pa-3">
<v-spacer></v-spacer>
<x-btn outlined :tooltip="`Compare GQL schema history of ${nodes.tn}`"
color="primary"
x-small
:disabled="loading || !swaggerDocHistory.length"
@click.prevent="schemaDiffDialog = true">
<v-icon small left>mdi-source-branch</v-icon>
History <span class="history-count" v-if="swaggerDocHistory.length">({{
<v-spacer />
<x-btn
outlined
:tooltip="`Compare GQL schema history of ${nodes.tn}`"
color="primary"
x-small
:disabled="loading || !swaggerDocHistory.length"
@click.prevent="schemaDiffDialog = true"
>
<v-icon small left>
mdi-source-branch
</v-icon>
History <span v-if="swaggerDocHistory.length" class="history-count">({{
swaggerDocHistory.length
}})</span>
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
x-small
:disabled="loading"
v-ge="['rows','save']"
@click.prevent="saveSwaggerDoc">
<v-icon small left>save</v-icon>
<x-btn
v-ge="['rows','save']"
outlined
tooltip="Save Changes"
color="primary"
x-small
:disabled="loading"
@click.prevent="saveSwaggerDoc"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</div>
<monaco-json-editor
v-model="swaggerDoc"
theme=""
style="min-height:500px;"
/>
</v-card>
</v-col>
<v-col>
<v-card class="flex-shrink-1" style="" v-if="filteredGroupedData.length">
<v-skeleton-loader v-if="loading" type="table"></v-skeleton-loader>
<v-card v-if="filteredGroupedData.length" class="flex-shrink-1" style="">
<v-skeleton-loader v-if="loading" type="table" />
<v-simple-table v-else-if="routers" dense style="width: auto; min-width: 500px">
<thead>
<tr>
<th colspan="2" class="text-center">
<div class="d-flex justify-center">
<span class="title">Routes</span>
</div>
</th>
<template v-for="(method,i) in methods">
<th
width="60"
class=" method-cell caption px-1 text-center text-uppercase font-weight-bold" :key="`${method}`"
:class="`${methodColor[method]}--text`">{{ method }}
<tr>
<th colspan="2" class="text-center">
<div class="d-flex justify-center">
<span class="title">Routes</span>
</div>
</th>
</template>
</tr>
<template v-for="(method,i) in methods">
<th
:key="`${method}`"
width="60"
class=" method-cell caption px-1 text-center text-uppercase font-weight-bold"
:class="`${methodColor[method]}--text`"
>
{{ method }}
</th>
</template>
</tr>
</thead>
<tbody>
<tr v-for="[path,route] in filteredGroupedData"
>
<td width="20" class="px-0">
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(method,i) in methods">
<td
width="60" class="pa-1 text-center method-cell" :key="`${path}_${method}}`">
<tr
v-for="[path,route] in filteredGroupedData"
>
<td width="20" class="px-0" />
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<span v-on="on">{{ path }}</span>
</template>
<span>{{ path }}</span>
</v-tooltip>
</td>
<template v-for="(method,i) in methods">
<td
<v-hover v-if="route[method]" v-slot:default="{ hover }">
<v-icon @click="showSourceCode(route,method)" small
:color="hover ? methodColor[method]:''" v-on="on">mdi-pencil
:key="`${path}_${method}}`"
width="60"
class="pa-1 text-center method-cell"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-hover v-if="route[method]" v-slot="{ hover }">
<v-icon
small
:color="hover ? methodColor[method]:''"
@click="showSourceCode(route,method)"
v-on="on"
>
mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
</v-tooltip>
</td>
</template>
</tr>
<tr
v-for="mw in middlewares"
>
<td width="20" class="px-0" />
<td class="pl-0">
<v-tooltip bottom>
<template #activator="{ on }">
<span v-on="on">{{ mw.title }} - Middleware</span>
</template>
<span>{{ mw.title }} - Middleware</span>
</v-tooltip>
</td>
<td
:colspan="methods.length"
width="60"
class="pa-1 text-center method-cell"
>
<v-tooltip bottom>
<template #activator="{ on }">
<v-hover v-slot="{ hover }">
<v-icon
small
:color="hover ? methodColor[method]:''"
@click="showMiddlewareSourceCode(mw)"
v-on="on"
>
mdi-pencil
</v-icon>
</v-hover>
</template>
Edit business logic
Edit middleware business logic
</v-tooltip>
</td>
</template>
</tr>
<tr v-for="mw in middlewares"
>
<td width="20" class="px-0">
</td>
<td class="pl-0">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{ mw.title }} - Middleware</span>
</template>
<span>{{ mw.title }} - Middleware</span>
</v-tooltip>
</td>
<td
:colspan="methods.length"
width="60" class="pa-1 text-center method-cell">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-hover v-slot:default="{ hover }">
<v-icon small
@click="showMiddlewareSourceCode(mw)"
:color="hover ? methodColor[method]:''" v-on="on">mdi-pencil
</v-icon>
</v-hover>
</template>
Edit middleware business logic
</v-tooltip>
</td>
</tr>
</tr>
</tbody>
</v-simple-table>
<v-alert v-else outlined type="info">Permission file not found</v-alert>
<v-alert v-else outlined type="info">
Permission file not found
</v-alert>
</v-card>
<!-- </div>-->
</v-col>
@@ -207,32 +223,31 @@
</v-row>
</v-container>
<handler-code-editor
v-model="showCodeEditor"
:is-middleware="isMiddleware"
:nodes="nodes"
:route="editRoute"
:method="editMethod"
v-model="showCodeEditor"></handler-code-editor>
/>
<v-dialog v-model="schemaDiffDialog" scrollable min-width="600px">
<v-card>
<xc-diff
v-model="swaggerDoc"
:history="swaggerDocHistory"
></xc-diff>
/>
</v-card>
</v-dialog>
</div>
</template>
<script>
import HandlerCodeEditor from "@/components/project/restHandlerCodeEditor";
import MonacoJsonEditor from "@/components/monaco/MonacoJsonEditor";
import HandlerCodeEditor from '@/components/project/restHandlerCodeEditor'
import MonacoJsonEditor from '@/components/monaco/MonacoJsonEditor'
export default {
name: "logic-rest",
components: {MonacoJsonEditor, HandlerCodeEditor},
name: 'LogicRest',
components: { MonacoJsonEditor, HandlerCodeEditor },
props: ['nodes'],
data: () => ({
showSwagger: false,
@@ -245,11 +260,11 @@ export default {
'get', 'post', 'put', 'delete'
],
methodColor: {
'get': 'success',
'post': 'warning',
'put': 'info',
'patch': 'secondary',
'delete': 'error'
get: 'success',
post: 'warning',
put: 'info',
patch: 'secondary',
delete: 'error'
},
editRoute: '',
editMethod: '',
@@ -259,48 +274,62 @@ export default {
swaggerDoc: '',
schemaDiffDialog: false
}),
computed: {
filteredGroupedData () {
return Object.entries(this.groupedData)
.filter(([path]) => !this.search || path.toLowerCase().includes(this.search.toLowerCase()))
},
name () {
return this.nodes.tn || this.nodes.view_name
}
},
async created () {
await this.loadRoutes()
this.groupRoutes()
await this.loadSwaggerDoc()
},
methods: {
groupRoutes() {
const groupedData = {};
this.middlewares = [];
groupRoutes () {
const groupedData = {}
this.middlewares = []
for (const route of this.routers) {
if (route.path) {
groupedData[route.path] = groupedData[route.path] || {};
groupedData[route.path][route.type] = route;
groupedData[route.path] = groupedData[route.path] || {}
groupedData[route.path][route.type] = route
} else if (route.handler_type === 2) {
this.middlewares.push(route)
}
}
this.groupedData = groupedData;
this.groupedData = groupedData
},
showSourceCode(route, method) {
this.editRoute = route;
this.editMethod = method;
this.isMiddleware = false;
this.showCodeEditor = true;
showSourceCode (route, method) {
this.editRoute = route
this.editMethod = method
this.isMiddleware = false
this.showCodeEditor = true
},
showMiddlewareSourceCode(mw) {
this.editRoute = mw;
this.editMethod = null;
this.isMiddleware = true;
this.showCodeEditor = true;
showMiddlewareSourceCode (mw) {
this.editRoute = mw
this.editMethod = null
this.isMiddleware = true
this.showCodeEditor = true
},
async loadSwaggerDoc() {
async loadSwaggerDoc () {
const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'tableXcModelGet', {
tn: this.nodes.tn || this.nodes.view_name
}]);
this.swaggerDoc = JSON.stringify(JSON.parse(tableMeta.schema), 0, 2);
}])
this.swaggerDoc = JSON.stringify(JSON.parse(tableMeta.schema), 0, 2)
if (tableMeta.schema_previous) {
this.swaggerDocHistory = JSON.parse(tableMeta.schema_previous).reverse().map(o => JSON.stringify(o, null, 2));
this.swaggerDocHistory = JSON.parse(tableMeta.schema_previous).reverse().map(o => JSON.stringify(o, null, 2))
} else {
this.swaggerDocHistory = [];
this.swaggerDocHistory = []
}
},
async saveSwaggerDoc() {
this.edited = false;
async saveSwaggerDoc () {
this.edited = false
try {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
@@ -308,35 +337,21 @@ export default {
}, 'xcModelSwaggerDocSet', {
tn: this.nodes.tn || this.nodes.view_name,
swaggerDoc: JSON.parse(this.swaggerDoc)
}]);
this.$toast.success('Successfully updated swagger doc').goAway(3000);
}])
this.$toast.success('Successfully updated swagger doc').goAway(3000)
} catch (e) {
this.$toast.error('Failed to update swagger doc').goAway(3000);
this.$toast.error('Failed to update swagger doc').goAway(3000)
}
},
async loadRoutes() {
this.loading = true;
async loadRoutes () {
this.loading = true
this.routers = (await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
dbAlias: this.nodes.dbAlias
}, 'xcRoutesPolicyGet', {
tn: this.name
}])).data.list;
this.loading = false;
}
},
async created() {
await this.loadRoutes();
this.groupRoutes();
await this.loadSwaggerDoc();
},
computed: {
filteredGroupedData() {
return Object.entries(this.groupedData)
.filter(([path]) => !this.search || path.toLowerCase().indexOf(this.search.toLowerCase()) > -1);
},
name() {
return this.nodes.tn || this.nodes.view_name;
}])).data.list
this.loading = false
}
}
}

View File

@@ -1,260 +1,266 @@
<template>
<div class="">
<v-card style="">
<v-toolbar height="42" flat class="toolbar-border-bottom">
<v-toolbar height="42" flat class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes.tn + ' (mocks)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small>forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (mocks)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small>
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload Rows"
small
color="primary"
class="primary"
@click="loadColumnList"
v-ge="['mocks','load']"
<v-spacer />
<x-btn
v-ge="['mocks','load']"
outlined
tooltip="Reload Rows"
small
color="primary"
class="primary"
@click="loadColumnList"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn outlined tooltip="Load suggested faker function based on field name"
color="primary"
class="primary"
small
v-ge="['mocks','load-faker-fns']"
@click="mapFieldWithSuggestedFakerFn">
<v-icon left small>mdi-function</v-icon>
<x-btn
v-ge="['mocks','load-faker-fns']"
outlined
tooltip="Load suggested faker function based on field name"
color="primary"
class="primary"
small
@click="mapFieldWithSuggestedFakerFn"
>
<v-icon left small>
mdi-function
</v-icon>
Predict Faker Functions
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="success"
class="success"
v-ge="['mocks','save']"
small @click="saveFakeFunctions"
:disabled="disableSaveButton"
<x-btn
v-ge="['mocks','save']"
outlined
tooltip="Save Changes"
color="success"
class="success"
small
:disabled="disableSaveButton"
@click="saveFakeFunctions"
>
<v-icon small left
>save
<v-icon
small
left
>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-data-table
dense
:headers="colsHeader"
:items="cols"
class="column-table">
<template v-slot:item="{item,index}">
class="column-table"
>
<template #item="{item}">
<tr>
<td>
{{item.cn}}<span class="font-weight-thin"> - {{item.dt}}</span>
{{ item.cn }}<span class="font-weight-thin"> - {{ item.dt }}</span>
</td>
<td>
<span class="font-weight-thin" v-if="item.pk"> (Primary Key)</span>
<span class="font-weight-thin" v-else-if="item.rcn"> (Foreign Key)</span>
<span v-if="item.pk" class="font-weight-thin"> (Primary Key)</span>
<span v-else-if="item.rcn" class="font-weight-thin"> (Foreign Key)</span>
<v-autocomplete
v-else
v-model="item.fakerFunction"
v-ge="['mocks','change-faker-fn']"
:items="fakerFunctionList | filterFakerList(item.dt)"
item-text="name"
item-value="value"
v-model="item.fakerFunction"
@change="disableSaveButton = false"
v-ge="['mocks','change-faker-fn']"
>
<template v-slot:selection="{ item }">
<div class="body-2"> {{item.name}}<span class="font-weight-thin " v-if="item.type"> &nbsp;- {{item.type}}</span></div>
<template #selection="{ item }">
<div class="body-2">
{{ item.name }}<span v-if="item.type" class="font-weight-thin "> &nbsp;- {{ item.type }}</span>
</div>
</template>
<template v-slot:item="data">
<template #item="data">
<v-list-item-content>
<v-list-item-title v-html="data.item.name"></v-list-item-title>
<v-list-item-subtitle v-html="data.item.group"></v-list-item-subtitle>
<v-list-item-title v-html="data.item.name" />
<v-list-item-subtitle v-html="data.item.group" />
</v-list-item-content>
</template>
</v-autocomplete>
</td>
</tr>
</template>
</v-data-table>
</v-card>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import fakerFunctionList from '../../../helpers/fakerFunctionList'
import dataTypeMapping from '../../../helpers/findDataTypeMapping'
const levenshtein = require('fast-levenshtein');
import { mapGetters } from 'vuex'
import fakerFunctionList from '../../../helpers/fakerFunctionList'
import dataTypeMapping from '../../../helpers/findDataTypeMapping'
const levenshtein = require('fast-levenshtein')
export default {
components: {},
data() {
return {
colsHeader: [{text: 'Column Name'}, {text: 'Mapper Function'}],
cols: [],
primaryKeys: [],
fakerFunctionList,
disableSaveButton: true
};
},
methods: {
async handleKeyDown({metaKey, key, altKey, shiftKey, ctrlKey}) {
console.log(metaKey, key, altKey, shiftKey, ctrlKey)
// cmd + s -> save
// cmd + l -> reload
// cmd + n -> new
// cmd + d -> delete
// cmd + enter -> send api
export default {
components: {},
data () {
return {
colsHeader: [{ text: 'Column Name' }, { text: 'Mapper Function' }],
cols: [],
primaryKeys: [],
fakerFunctionList,
disableSaveButton: true
}
},
methods: {
async handleKeyDown ({ metaKey, key, altKey, shiftKey, ctrlKey }) {
console.log(metaKey, key, altKey, shiftKey, ctrlKey)
// cmd + s -> save
// cmd + l -> reload
// cmd + n -> new
// cmd + d -> delete
// cmd + enter -> send api
switch ([metaKey, key].join('_')) {
case 'true_s' :
await this.saveFakeFunctions();
break;
case 'true_l' :
await this.loadColumnList();
break;
switch ([metaKey, key].join('_')) {
case 'true_s' :
await this.saveFakeFunctions()
break
case 'true_l' :
await this.loadColumnList()
break
// case 'true_n' :
// this.addColumn();
// break;
// case 'true_d' :
// await this.deleteTable('showDialog');
// break;
}
},
}
},
async loadColumnList () {
this.disableSaveButton = true
const columnsList = await this.client.fakerColumnsList({
tn: this.nodes.tn,
seedsFolder: this.seedsFolder
})
console.log(columnsList)
this.cols = columnsList.data.list
async loadColumnList() {
this.disableSaveButton = true;
const columnsList = await this.client.fakerColumnsList({
tn: this.nodes.tn,
seedsFolder: this.seedsFolder
});
console.log(columnsList)
this.cols = columnsList.data.list;
// columnsList.data.list.map(({cn, dt, ai, pk}) => {
// if (pk) this.primaryKeys.push(cn);
// return {
// text: cn, value: cn, type: dt, ai
// }
// })
},
mapFieldWithSuggestedFakerFn () {
this.cols = this.cols.map((col) => {
let suggestion = null
let lScore = Infinity
const nativeType = dataTypeMapping(col.dt)
fakerFunctionList.forEach((fakerFn, i) => {
if (nativeType !== 'string' && nativeType !== fakerFn.type) { return }
// columnsList.data.list.map(({cn, dt, ai, pk}) => {
// if (pk) this.primaryKeys.push(cn);
// return {
// text: cn, value: cn, type: dt, ai
// }
// })
},
mapFieldWithSuggestedFakerFn() {
this.cols = this.cols.map(col => {
let suggestion = null;
let l_score = Infinity;
let nativeType = dataTypeMapping(col.dt);
fakerFunctionList.forEach((fakerFn, i) => {
if(nativeType !== 'string' && nativeType !== fakerFn.type) return;
if (i) {
const ls = levenshtein.get(col.cn.toLowerCase(), fakerFn.name.toLowerCase())
if (l_score > ls) {
l_score = ls;
suggestion = fakerFn;
}
} else {
suggestion = fakerFn;
l_score = levenshtein.get(col.cn.toLowerCase(), fakerFn.name.toLowerCase());
if (i) {
const ls = levenshtein.get(col.cn.toLowerCase(), fakerFn.name.toLowerCase())
if (lScore > ls) {
lScore = ls
suggestion = fakerFn
}
})
if (l_score < 3) {
this.disableSaveButton = false;
return {...col, fakerFunction: suggestion.value};
} else {
suggestion = fakerFn
lScore = levenshtein.get(col.cn.toLowerCase(), fakerFn.name.toLowerCase())
}
return col;
})
},
async saveFakeFunctions() {
try {
let result = await this.client.fakerColumnsCreate({fakerColumns: this.cols, seedsFolder: this.seedsFolder,tn:this.nodes.tn});
} catch (e) {
this.$toast.error('Saving changes to faker functions failed').goAway(3000);
throw e;
if (lScore < 3) {
this.disableSaveButton = false
return { ...col, fakerFunction: suggestion.value }
}
}
return col
})
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"})
},
beforeCreated() {
},
async created() {
async saveFakeFunctions () {
try {
this.seedsFolder = this.sqlMgr.projectGetFolder({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
});
this.client = await this.sqlMgr.projectGetSqlClient({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
});
await this.loadColumnList();
await this.client.fakerColumnsCreate({ fakerColumns: this.cols, seedsFolder: this.seedsFolder, tn: this.nodes.tn })
} catch (e) {
console.log(e);
this.$toast.error('Loading columns and faker functions failed').goAway(3000);
throw e;
}
},
mounted() {
},
beforeDestroy() {
},
destroy() {
},
validate({params}) {
return true;
},
head() {
return {};
},
props: ["nodes", "newTable", "mtdNewTableUpdate", "deleteTable"],
watch: {},
directives: {},
filters:{
filterFakerList(list,type){
const nativeType = dataTypeMapping(type);
return list.filter(item=> (nativeType === 'string') || (nativeType === item.type));
this.$toast.error('Saving changes to faker functions failed').goAway(3000)
throw e
}
}
};
},
computed: {
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' })
},
beforeCreated () {
},
watch: {},
async created () {
try {
this.seedsFolder = this.sqlMgr.projectGetFolder({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
})
this.client = await this.sqlMgr.projectGetSqlClient({
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
})
await this.loadColumnList()
} catch (e) {
console.log(e)
this.$toast.error('Loading columns and faker functions failed').goAway(3000)
throw e
}
},
mounted () {
},
beforeDestroy () {
},
destroy () {
},
directives: {},
filters: {
filterFakerList (list, type) {
const nativeType = dataTypeMapping(type)
return list.filter(item => (nativeType === 'string') || (nativeType === item.type))
}
},
validate ({ params }) {
return true
},
head () {
return {}
},
props: ['nodes', 'newTable', 'mtdNewTableUpdate', 'deleteTable']
}
</script>
<style scoped>

View File

@@ -1,17 +1,25 @@
<template>
<v-container>
<h1>Relation List</h1>
<v-btn @click="loadRelationList" color="primary"
<v-btn
color="primary"
@click="loadRelationList"
>
<v-icon left>refresh</v-icon>
<v-icon left>
refresh
</v-icon>
Reload
</v-btn
</v-btn>
<v-btn
small
class=" text-right"
@click="deleteTable('showDialog')"
>
<v-btn small @click="deleteTable('showDialog')" class=" text-right"
>Delete Table
</v-btn
>
<v-btn @click="throwError()">error</v-btn>
Delete Table
</v-btn>
<v-btn @click="throwError()">
error
</v-btn>
<v-row>
<v-col cols="6">
<v-data-table
@@ -19,7 +27,7 @@
:items="relations"
footer-props.items-per-page-options="30"
>
<template v-slot:item="props">
<template #item="props">
<td>{{ props.item.cstn }}</td>
<td>{{ props.item.rtn }}</td>
</template>
@@ -31,129 +39,124 @@
:items="columns"
footer-props.items-per-page-options="30"
>
<template v-slot:items="props">
<template #items="props">
<td>{{ props.item.cn }}</td>
</template>
</v-data-table>
</v-col>
</v-row>
</v-container
>
</v-container>
</template>
<script>
import {mapGetters, mapActions} from "vuex";
import { mapGetters } from 'vuex'
export default {
data() {
return {
relations: [],
columns: [],
relationHeaders: [
{
text: "Relation",
sortable: false
},
{
text: "Referenced Table",
sortable: false
}
],
columnHeaders: [
{
text: "Column",
sortable: false
}
],
max25chars: v => v.length <= 25 || "Input too long!"
};
export default {
data () {
return {
relations: [],
columns: [],
relationHeaders: [
{
text: 'Relation',
sortable: false
},
{
text: 'Referenced Table',
sortable: false
}
],
columnHeaders: [
{
text: 'Column',
sortable: false
}
],
max25chars: v => v.length <= 25 || 'Input too long!'
}
},
methods: {
throwError () {
throw new Error('new erroror ')
},
methods: {
throwError() {
throw new Error("new erroror ");
},
async loadRelationList() {
if (this.newTable) return;
async loadRelationList () {
if (this.newTable) { return }
// console.log("env: this.nodes.env", this.nodes.env, this.nodes.dbAlias);
// const client = await this.sqlMgr.projectGetSqlClient({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// });
// const result = await client.relationList({
// tn: this.nodes.tn
// });
//
// console.log("env: this.nodes.env", this.nodes.env, this.nodes.dbAlias);
// const client = await this.sqlMgr.projectGetSqlClient({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// });
// const result = await client.relationList({
// tn: this.nodes.tn
// });
//
// const result = await this.sqlMgr.sqlOp({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// }, 'relationList', { tn: this.nodes.tn})
// const result = await this.sqlMgr.sqlOp({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// }, 'relationList', { tn: this.nodes.tn})
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'relationList', { tn: this.nodes.tn }])
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'relationList', {tn: this.nodes.tn}])
// console.log(result, "relations");
this.relations = result.data.list;
// this.loadColumnList(result.data.list[0].rtn);
},
async loadColumnList(tn = "") {
// console.log("env: this.nodes.env", this.nodes.env, this.nodes.dbAlias);
// const client = await this.sqlMgr.projectGetSqlClient({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// });
// const result = await client.columnList({
// tn
// });
// const result = await this.sqlMgr.sqlOp({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// }, 'columnList', {
// tn
// });
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'columnList', {
tn
}]);
this.columns = result.data.list;
}
// console.log(result, "relations");
this.relations = result.data.list
// this.loadColumnList(result.data.list[0].rtn);
},
computed: {...mapGetters({sqlMgr: "sqlMgr/sqlMgr"})},
async loadColumnList (tn = '') {
// console.log("env: this.nodes.env", this.nodes.env, this.nodes.dbAlias);
// const client = await this.sqlMgr.projectGetSqlClient({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// });
// const result = await client.columnList({
// tn
// });
beforeCreated() {
},
created() {
this.loadRelationList();
},
mounted() {
},
beforeDestroy() {
},
destroy() {
},
validate({params}) {
return true;
},
head() {
return {};
},
props: ["nodes", "newTable", "deleteTable"],
watch: {},
directives: {},
components: {}
};
// const result = await this.sqlMgr.sqlOp({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// }, 'columnList', {
// tn
// });
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'columnList', {
tn
}])
this.columns = result.data.list
}
},
computed: { ...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }) },
beforeCreated () {
},
watch: {},
created () {
this.loadRelationList()
},
mounted () {
},
beforeDestroy () {
},
destroy () {
},
directives: {},
components: {},
validate ({ params }) {
return true
},
head () {
return {}
},
props: ['nodes', 'newTable', 'deleteTable']
}
</script>
<style scoped>

View File

@@ -1,61 +1,78 @@
<template>
<div class="">
<v-card class="elevation-0">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: (nodes.tn || nodes.view_name) + ' (rows)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: (nodes.tn || nodes.view_name) + ' (rows)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload Rows"
color="primary"
small
v-ge="['rows','reload']"
@click="refreshTable">
<v-icon small left>refresh</v-icon>
<v-spacer />
<x-btn
v-ge="['rows','reload']"
outlined
tooltip="Reload Rows"
color="primary"
small
@click="refreshTable"
>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
class="primary"
small
v-if="!isView"
:disabled="disableSaveButton"
v-ge="['rows','save']"
@click.prevent="saveChanges">
<v-icon small left>save</v-icon>
<x-btn
v-if="!isView"
v-ge="['rows','save']"
outlined
tooltip="Save Changes"
color="primary"
class="primary"
small
:disabled="disableSaveButton"
@click.prevent="saveChanges"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
<x-btn outlined tooltip="Add New Row"
color="primary"
class="primary"
small
v-if="!isView"
v-ge="['rows','new']"
@click="addNewRow">
<v-icon small left>mdi-plus</v-icon>
<x-btn
v-if="!isView"
v-ge="['rows','new']"
outlined
tooltip="Add New Row"
color="primary"
class="primary"
small
@click="addNewRow"
>
<v-icon small left>
mdi-plus
</v-icon>
New Row
</x-btn>
</v-toolbar>
<v-data-table
@@ -68,110 +85,107 @@
:options.sync="tableOptions"
class=" column-table"
:footer-props="{
itemsPerPageOptions:[5,10,25,50,100]
itemsPerPageOptions:[5,10,25,50,100]
}"
>
<template v-slot:header="{props:{headers}}">
<th class="py-2 px-1 text-center text-capitalize" v-for="header in headers" :key="header.title">
<template #header="{props:{headers}}">
<th v-for="header in headers" :key="header.title" class="py-2 px-1 text-center text-capitalize">
{{ header.text }}
</th>
</template>
<template v-slot:item="{item,index}">
<template #item="{item,index}">
<tr v-if="!item.isNewRow">
<td v-for="({text,type,ai:ai},i) in cols" :key="i" class="caption">
<span v-if="isView">
{{ item.data[text] }}
{{ item.data[text] }}
</span>
<template v-else-if="text">
<v-edit-dialog
v-ge="['rows','edit-save']"
:return-value.sync="item.data[text]"
@save="save(type,text,item)"
v-ge="['rows','edit-save']"
>
{{ item.data[text] }}
<template v-slot:input>
<template #input>
<v-text-field
class="mt-0 caption"
v-model="item.data[text]"
class="mt-0 caption"
label="Edit"
:type="getType(type)"
single-line
hide-details
dense
:disabled="ai"
></v-text-field>
/>
</template>
</v-edit-dialog>
</template>
<template v-else>
<x-icon small
color="error grey"
@click="deleteRow('showDialog', index, item)"
v-ge="['rows','delete']"
>mdi-delete-forever
</x-icon
<x-icon
v-ge="['rows','delete']"
small
color="error grey"
@click="deleteRow('showDialog', index, item)"
>
mdi-delete-forever
</x-icon>
</template>
</td>
</tr>
<tr v-else>
<td v-for="({text,type,ai:ai},i) in cols" :key="i">
<template v-if="text">
<v-text-field
v-model="item.data[text]"
v-ge="['rows','save']"
dense
hide-details
@input.passive="save(type,text,item)"
v-model="item.data[text]"
label="Edit"
:type="getType(type)"
:placeholder="text"
:disabled="ai"
single-line
v-ge="['rows','save']"
class="caption mt-n1 "
></v-text-field>
@input.passive="save(type,text,item)"
/>
</template>
<template v-else>
<x-icon small
color="error"
@click="deleteRow('showDialog',index ,item)"
v-ge="['rows','delete']"
>mdi-delete-forever
</x-icon
<x-icon
v-ge="['rows','delete']"
small
color="error"
@click="deleteRow('showDialog',index ,item)"
>
mdi-delete-forever
</x-icon>
</template>
</td>
</tr>
</template>
</v-data-table>
</v-card>
<dlgLabelSubmitCancel
type="primary"
v-if="rowDeleteDlg"
:dialogShow="rowDeleteDlg"
:actionsMtd="deleteRow"
type="primary"
:dialog-show="rowDeleteDlg"
:actions-mtd="deleteRow"
heading="Click Submit to Delete the Row"
/>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import dlgLabelSubmitCancel from "../../utils/dlgLabelSubmitCancel.vue";
import findDataTypeMapping from '../../../helpers/findDataTypeMapping.js';
import { mapGetters } from 'vuex'
import dlgLabelSubmitCancel from '../../utils/dlgLabelSubmitCancel.vue'
import findDataTypeMapping from '../../../helpers/findDataTypeMapping.js'
export default {
components: {
dlgLabelSubmitCancel: dlgLabelSubmitCancel
dlgLabelSubmitCancel
},
data() {
data () {
return {
rows: [],
cols: [],
@@ -184,10 +198,10 @@ export default {
totalCount: 0,
tableOptions: {},
disableSaveButton: true
};
}
},
methods: {
async handleKeyDown({metaKey, key, altKey, shiftKey, ctrlKey}) {
async handleKeyDown ({ metaKey, key, altKey, shiftKey, ctrlKey }) {
console.log(metaKey, key, altKey, shiftKey, ctrlKey)
// cmd + s -> save
// cmd + l -> reload
@@ -197,59 +211,53 @@ export default {
switch ([metaKey, key].join('_')) {
case 'true_s' :
await this.saveChanges();
break;
await this.saveChanges()
break
case 'true_l' :
await this.refreshTable();
break;
await this.refreshTable()
break
case 'true_n' :
this.addNewRow();
break;
this.addNewRow()
break
// case 'true_d' :
// await this.deleteTable('showDialog');
// break;
}
},
async refreshTable() {
async refreshTable () {
try {
await this.loadColumnList();
await this.loadRowList();
await this.loadColumnList()
await this.loadRowList()
} catch (e) {
this.$toast.error('error loading rows').goAway(4000);
throw e;
this.$toast.error('error loading rows').goAway(4000)
throw e
}
},
async loadColumnList() {
async loadColumnList () {
// const columnsList = await this.client.columnList({tn: this.nodes.tn});
// const columnsList = await this.sqlMgr.sqlOp({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// }, 'columnList', {tn: this.nodes.tn})
const columnsList = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'columnList', {tn: this.nodes.tn || this.nodes.view_name}])
}, 'columnList', { tn: this.nodes.tn || this.nodes.view_name }])
this.cols = columnsList.data.list.map(({cn, dt, ai, pk}) => {
if (pk) this.primaryKeys.push(cn);
this.cols = columnsList.data.list.map(({ cn, dt, ai, pk }) => {
if (pk) { this.primaryKeys.push(cn) }
return {
text: cn, value: cn, type: findDataTypeMapping(dt), ai
}
})
// column for action buttons
if (!this.isView)
this.cols.push({text: '', value: ''})
if (!this.isView) { this.cols.push({ text: '', value: '' }) }
},
async loadRowList() {
const {page, itemsPerPage} = this.tableOptions;
async loadRowList () {
const { page, itemsPerPage } = this.tableOptions
// const result = await this.sqlMgr.sqlOp({
// env: this.nodes.env,
@@ -261,7 +269,6 @@ export default {
// orderBy: this.primaryKeys.map(key => ({column: key, order: 'desc'}))
// })
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
@@ -269,10 +276,9 @@ export default {
tn: this.nodes.tn || this.nodes.view_name,
size: itemsPerPage,
page,
orderBy: this.primaryKeys.map(key => ({column: key, order: 'desc'}))
orderBy: this.primaryKeys.map(key => ({ column: key, order: 'desc' }))
}])
// const result = await this.client.list({
// tn: this.nodes.tn,
// size: itemsPerPage,
@@ -280,44 +286,42 @@ export default {
// orderBy: this.primaryKeys.map(key => ({column: key, order: 'desc'}))
// });
this.totalCount = result.data.count;
this.totalCount = result.data.count
this.rows = result.data.list.map(row => {
const keys = this.primaryKeys.reduce((obj, key) => Object.assign(obj, {[key]: row[key]}), {})
return {keys, data: {...row}, dataCopy: {...row}};
});
this.rows = result.data.list.map((row) => {
const keys = this.primaryKeys.reduce((obj, key) => Object.assign(obj, { [key]: row[key] }), {})
return { keys, data: { ...row }, dataCopy: { ...row } }
})
},
save(type, text, item) {
save (type, text, item) {
// console.log(type, text, item)
item.changed = true;
const {data, dataCopy} = item;
item.changed = true
const { data, dataCopy } = item
if (type === 'date') {
dataCopy[text] = new Date(data[text]);
dataCopy[text] = new Date(data[text])
} else {
dataCopy[text] = data[text];
dataCopy[text] = data[text]
}
},
getType(dt) {
if (dt == 'number')
return 'number';
getType (dt) {
if (dt === 'number') { return 'number' }
return 'text'
},
async deleteRow(action = "", index, row) {
async deleteRow (action = '', index, row) {
try {
if (action === "showDialog") {
this.rowDeleteDlg = true;
this.selectedRowForDelete = {index, row};
} else if (action === "hideDialog") {
this.rowDeleteDlg = false;
if (action === 'showDialog') {
this.rowDeleteDlg = true
this.selectedRowForDelete = { index, row }
} else if (action === 'hideDialog') {
this.rowDeleteDlg = false
} else {
this.rowDeleteDlg = false;
const {index, row} = this.selectedRowForDelete;
this.rowDeleteDlg = false
const { index, row } = this.selectedRowForDelete
if (row.isNewRow) {
this.rows.splice(index, 1);
this.rows.splice(index, 1)
} else {
// this.client.delete(this.nodes.tn, row.keys)
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
@@ -326,7 +330,6 @@ export default {
whereConditions: row.keys
}])
// await this.sqlMgr.sqlOp({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
@@ -335,31 +338,29 @@ export default {
// whereConditions: row.keys
// });
this.rows.splice(index, 1);
this.rows.splice(index, 1)
}
this.$toast.success('Row deleted successfully').goAway(3000);
this.$toast.success('Row deleted successfully').goAway(3000)
}
this.totalCount--;
this.totalCount--
} catch (e) {
this.$toast.error('Deleting row failed').goAway(3000);
throw e;
this.$toast.error('Deleting row failed').goAway(3000)
throw e
}
},
addNewRow() {
this.rows.unshift({isNewRow: true, data: {}, dataCopy: {}})
this.totalCount++;
addNewRow () {
this.rows.unshift({ isNewRow: true, data: {}, dataCopy: {} })
this.totalCount++
},
async saveChanges() {
async saveChanges () {
try {
const newRows = [];
this.rows.map(async item => {
const {keys, isNewRow, changed, dataCopy} = item;
const newRows = []
this.rows.map(async (item) => {
const { keys, isNewRow, changed, dataCopy } = item
if (isNewRow) {
newRows.push(dataCopy);
newRows.push(dataCopy)
} else if (changed) {
item.changed = false;
item.changed = false
// const res = await this.client.update(this.nodes.tn, dataCopy, keys);
const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
@@ -369,79 +370,76 @@ export default {
tn: this.nodes.tn,
data: dataCopy,
whereConditions: keys
}]);
}])
console.log(res);
this.$toast.success('Row updated successfully').goAway(3000);
console.log(res)
this.$toast.success('Row updated successfully').goAway(3000)
}
});
})
if (newRows.length) {
console.log(newRows);
console.log(newRows)
// todo : multiargs to single object
// const res = await this.client.insert(this.nodes.tn, newRows)
const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'insert', {
tn: this.nodes.tn,
data: newRows
}]);
}])
console.log(res)
}
this.$toast.success('Row saved successfully').goAway(3000);
this.$toast.success('Row saved successfully').goAway(3000)
this.loadRowList()
} catch (e) {
console.log(e);
this.$toast.error('Saving changes to table failed').goAway(3000);
throw e;
console.log(e)
this.$toast.error('Saving changes to table failed').goAway(3000)
throw e
}
}
},
computed: {
...mapGetters({sqlMgr: "sqlMgr/sqlMgr"}),
isView() {
...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }),
isView () {
return !!this.nodes.view_name
}
},
beforeCreated() {
beforeCreated () {
},
async created() {
await this.refreshTable();
},
mounted() {
},
beforeDestroy() {
},
destroy() {
},
validate({params}) {
return true;
},
head() {
return {};
},
props: ["nodes", "newTable", "mtdNewTableUpdate", "deleteTable"],
watch: {
tableOptions: {
handler() {
this.loadRowList();
handler () {
this.loadRowList()
},
deep: true,
deep: true
},
rows: {
handler() {
handler () {
this.disableSaveButton = !this.rows.some(row => row.isNewRow || row.changed)
},
deep: true
}
},
directives: {}
};
async created () {
await this.refreshTable()
},
mounted () {
},
beforeDestroy () {
},
destroy () {
},
directives: {},
validate ({ params }) {
return true
},
head () {
return {}
},
props: ['nodes', 'newTable', 'mtdNewTableUpdate', 'deleteTable']
}
</script>
<style scoped>

View File

@@ -1,213 +1,264 @@
<template>
<div class="h-100">
<v-card style="" class="h-100">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: (nodes._tn || nodes.view_name|| nodes.tn) + ' (RBAC)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: (nodes._tn || nodes.view_name|| nodes.tn) + ' (RBAC)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload RBAC"
color="primary"
small
@click="load"
v-ge="['acl','reload']"
<v-spacer />
<x-btn
v-ge="['acl','reload']"
outlined
tooltip="Reload RBAC"
color="primary"
small
@click="load"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
class="primary"
small
v-ge="['acl','save']"
@click="save"
:disabled="!edited"
<x-btn
v-ge="['acl','save']"
outlined
tooltip="Save Changes"
color="primary"
class="primary"
small
:disabled="!edited"
@click="save"
>
<v-icon small left>save</v-icon>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<p class="title mt-6 mb-6">
Table : <span class="text-capitalize">{{ nodes._tn || nodes.view_name }}</span><br>
<span class="font-weight-thin">Role Based Access Control (RBAC) For Columns</span>
</p>
<p class="title mt-6 mb-6"> Table : <span class="text-capitalize">{{ nodes._tn || nodes.view_name }}</span><br>
<span class="font-weight-thin">Role Based Access Control (RBAC) For Columns</span></p>
<v-skeleton-loader v-if="loading || !data" type="table">
</v-skeleton-loader>
<div v-else class="pb-10">
<v-overlay absolute v-model="showCustomAcl">
<template v-slot:default>
<v-card :light="!$store.state.windows.darkTheme" class="ma-2" style="max-height: 100%; overflow: auto; min-height:70vh">
<v-skeleton-loader v-if="loading || !data" type="table" />
<div v-else class="pb-10">
<v-overlay v-model="showCustomAcl" absolute>
<template #default>
<v-card
:light="!$store.state.windows.darkTheme"
class="ma-2"
style="max-height: 100%; overflow: auto; min-height:70vh"
>
<v-card-actions>
<p class="body-1 mb-0 mr-2">Custom Rules : {{ custoAclTitle[0] }}<span class="grey--text caption">(table)</span>
<p class="body-1 mb-0 mr-2">
Custom Rules : {{ custoAclTitle[0] }}<span class="grey--text caption">(table)</span>
> {{ custoAclTitle[1] }}<span class="grey--text caption">(role)</span> > {{ custoAclTitle[2] }}<span
class="grey--text caption">(operation)</span></p>
<v-spacer>
</v-spacer>
class="grey--text caption"
>(operation)</span>
</p>
<v-spacer />
<v-btn
outlined
small
@click="showCustomAcl=false"
>Cancel
>
Cancel
</v-btn>
<v-btn
outlined
color="primary"
small
@click="save"
>Save
>
Save
</v-btn>
</v-card-actions>
<v-card-text>
<custom-acl :nodes="nodes" :table="nodes.tn || nodes.view_name"
v-model="customAcl"></custom-acl>
<custom-acl
v-model="customAcl"
:nodes="nodes"
:table="nodes.tn || nodes.view_name"
/>
</v-card-text>
</v-card>
</template>
</v-overlay>
<v-simple-table dense v-slot:default class=" mx-10">
<v-simple-table dense class=" mx-10">
<thead>
<tr>
<th rowspan="2" class="text-center">
<!-- <div class="d-flex justify-center align-end">-->
<!-- <v-checkbox dense v-model="aclAll"></v-checkbox>-->
<tr>
<th rowspan="2" class="text-center">
<!-- <div class="d-flex justify-center align-end">-->
<!-- <v-checkbox dense v-model="aclAll"></v-checkbox>-->
<span>Columns x RBAC</span>
<!-- <v-text-field dense hide-details class="my-2 caption font-weight-regular"
flat
outlined
:placeholder="`Search columns in '${nodes._tn|| nodes.view_name}'`"
prepend-inner-icon="search" v-model="search"
></v-text-field>-->
<span>Columns x RBAC</span>
<!-- <v-text-field dense hide-details class="my-2 caption font-weight-regular"
flat
outlined
:placeholder="`Search columns in '${nodes._tn|| nodes.view_name}'`"
prepend-inner-icon="search" v-model="search"
></v-text-field>-->
<!-- </div>-->
</th>
<th :colspan="operations.length" class="text-center"
v-for="role in roles" :key="role" v-if="role !== 'creator'">{{ role }}
</th>
</tr>
<tr>
<template v-for="role in roles" v-if="role !== 'creator'">
<th v-for="op in operations" :class="` pa-0 caption text-capitalize ${op.color}--text`"
class="permission role-op-header"
:key="`${role}-${op.name}--11`"
>
</th>
<template v-for="role in roles">
<th
v-if="role !== 'creator'"
:key="role"
:colspan="operations.length"
class="text-center"
>
{{ role }}
</th>
</template>
</tr>
<tr>
<template v-for="role in roles">
<template v-if="role !== 'creator'">
<th
v-for="op in operations"
:key="`${role}-${op.name}--11`"
:class="` pa-0 caption text-capitalize ${op.color}--text`"
class="permission role-op-header"
>
<v-tooltip bottom>
<template #activator="{on}">
<div class="d-flex flex-column justify-center align-center d-100 h-100 mt-0" v-on="on">
<v-checkbox
v-model="aclRoleOpAll[`${role}___${op.name}`]"
hide-details
class="mt-n1"
dense
:color="op.color"
@change="onAclRoleOpAllChange(aclRoleOpAll[`${role}___${op.name}`],`${role}___${op.name}`)"
/>
<span>{{ op.name }}</span>
</div>
</template>
<span class="caption"><span
class="text-capitalize"
>{{
role
}}</span> {{ aclRoleOpAll[`${role}___${op.name}`] ? 'can' : 'can\'t' }} {{
op.name
}} '{{ nodes.tn }}' rows</span>
</v-tooltip>
<v-tooltip bottom>
<template v-slot:activator="{on}">
<div class="d-flex flex-column justify-center align-center d-100 h-100 mt-0" v-on="on">
<v-checkbox
hide-details
class="mt-n1" dense v-model="aclRoleOpAll[`${role}___${op.name}`]"
@change="onAclRoleOpAllChange(aclRoleOpAll[`${role}___${op.name}`],`${role}___${op.name}`)"
:color="op.color"></v-checkbox>
<span>{{ op.name }}</span>
</div>
</template>
<span class="caption"><span
class="text-capitalize">{{
role
}}</span> {{ aclRoleOpAll[`${role}___${op.name}`] ? 'can' : 'can\'t' }} {{
op.name
}} '{{ nodes.tn }}' rows</span>
</v-tooltip>
<v-icon
v-if="op.name !== 'delete'"
x-small
class="custom-acl"
@click="(
showCustomAcl = true,
custoAclTitle = [nodes.tn,role,op.name],
customAcl=(data[role] && data[role][op.name] && data[role][op.name].custom ) || {}
)"
>
mdi-pencil-outline
</v-icon>
<v-icon v-if="op.name !== 'delete'" x-small class="custom-acl"
@click="(
showCustomAcl = true,
custoAclTitle = [nodes.tn,role,op.name],
customAcl=(data[role] && data[role][op.name] && data[role][op.name].custom ) || {}
)">
mdi-pencil-outline
</v-icon>
<v-icon
x-small
class="custom-acl-found"
v-if="data[role]
<v-icon
v-if="data[role]
&& data[role][op.name]
&& data[role][op.name].custom
&& Object.keys(data[role][op.name].custom).length"
>mdi-filter-menu
</v-icon>
</th>
</template>
</tr>
x-small
class="custom-acl-found"
>
mdi-filter-menu
</v-icon>
</th>
</template>
</template>
</tr>
</thead>
<tbody>
<tr class="caption" v-for="column in columns"
<tr
v-for="column in columns"
v-show="column._cn.toLowerCase().indexOf(search.toLowerCase()) > -1"
:key="column._cn"
v-show="column._cn.toLowerCase().indexOf(search.toLowerCase()) > -1">
<td
>{{ column._cn }}
</td>
<template v-for="role in roles" v-if="role !== 'creator'">
<td
class="pa-0"
style="position: relative"
v-for="op in operations" :class="`caption text-capitalize`"
:key="`${role}-${op.name}`">
<v-tooltip bottom :disabled="op.ignore">
<template v-slot:activator="{on}">
<div class="d-100 h-100 d-flex align-center justify-center permission-checkbox-container" v-on="on">
<v-checkbox class="mt-n1" v-if="!op.ignore" dense
color="primary lighten-2"
@change="onPermissionChange(aclObj[`${column._cn}___${role}___${op.name}`],`${role}___${op.name}`)"
v-model="aclObj[`${column._cn}___${role}___${op.name}`]"
style=""></v-checkbox>
<v-checkbox class="mt-n1" v-else dense
color="primary lighten-2"
disabled
style=""></v-checkbox>
</div>
</template>
<span class="caption"><span
class="text-capitalize">{{
role
}}</span> {{ aclObj[`${column._cn}___${role}___${op.name}`] ? 'can' : 'can\'t' }} {{
op.name
}} column '{{ column._cn }}'</span>
</v-tooltip>
class="caption"
>
<td>
{{ column._cn }}
</td>
</template>
</tr>
<template v-for="role in roles">
<template
v-if="role !== 'creator'"
>
<td
v-for="op in operations"
:key="`${role}-${op.name}`"
class="pa-0"
style="position: relative"
:class="`caption text-capitalize`"
>
<v-tooltip bottom :disabled="op.ignore">
<template #activator="{on}">
<div
class="d-100 h-100 d-flex align-center justify-center permission-checkbox-container"
v-on="on"
>
<v-checkbox
v-if="!op.ignore"
v-model="aclObj[`${column._cn}___${role}___${op.name}`]"
class="mt-n1"
dense
color="primary lighten-2"
style=""
@change="onPermissionChange(aclObj[`${column._cn}___${role}___${op.name}`],`${role}___${op.name}`)"
/>
<v-checkbox
v-else
class="mt-n1"
dense
color="primary lighten-2"
disabled
style=""
/>
</div>
</template>
<span class="caption"><span
class="text-capitalize"
>{{
role
}}</span> {{ aclObj[`${column._cn}___${role}___${op.name}`] ? 'can' : 'can\'t' }} {{
op.name
}} column '{{ column._cn }}'</span>
</v-tooltip>
</td>
</template>
</template>
</tr>
</tbody>
</v-simple-table>
</div>
</v-card>
</div>
</template>
@@ -217,11 +268,11 @@
// import debounce from "@/helpers/debounce";
// import CustomAcl from "./customAcl";
import CustomAcl from "./customAcl";
import CustomAcl from './customAcl'
export default {
name: "tableAcl",
components: {CustomAcl},
name: 'TableAcl',
components: { CustomAcl },
// components: {CustomAcl},
props: ['nodes'],
data: () => ({
@@ -252,80 +303,78 @@ export default {
loading: false
}),
computed: {
roles() {
roles () {
return (this.data ? Object.keys(this.data) : []).filter(r => r !== 'guest')
},
operations() {
return this.allOperations.filter(({name}) => this.nodes.tn || name === 'read')
operations () {
return this.allOperations.filter(({ name }) => this.nodes.tn || name === 'read')
}
},
async created() {
await this.load();
async created () {
await this.load()
},
methods: {
async load() {
await this.loadColumnList();
await this.loadAcl();
this.generateAclObj();
this.edited = false;
async load () {
await this.loadColumnList()
await this.loadAcl()
this.generateAclObj()
this.edited = false
},
generateAclObj() {
const aclObj = {};
const aclRoleOpAll = {};
generateAclObj () {
const aclObj = {}
const aclRoleOpAll = {}
console.log(this.data)
// generate aclObj with merged key value
for (const [role, roleObj] of Object.entries(this.data)) {
for (const [operation, acl] of Object.entries(roleObj)) {
aclRoleOpAll[`${role}___${operation}`] = false;
for (const {_cn: cn} of this.columns) {
aclRoleOpAll[`${role}___${operation}`] = false
for (const { _cn: cn } of this.columns) {
if (typeof acl === 'boolean') {
aclObj[`${cn}___${role}___${operation}`] = acl;
aclObj[`${cn}___${role}___${operation}`] = acl
} else if (acl) {
aclObj[`${cn}___${role}___${operation}`] = acl.columns[cn];
aclObj[`${cn}___${role}___${operation}`] = acl.columns[cn]
}
if (!aclRoleOpAll[`${role}___${operation}`] && aclObj[`${cn}___${role}___${operation}`]) {
aclRoleOpAll[`${role}___${operation}`] = true;
aclRoleOpAll[`${role}___${operation}`] = true
}
}
}
}
this.aclRoleOpAll = aclRoleOpAll;
this.$nextTick(() => this.aclObj = aclObj);
this.aclRoleOpAll = aclRoleOpAll
this.$nextTick(() => {
this.aclObj = aclObj
})
},
async save() {
const obj = {};
async save () {
const obj = {}
for (const [key, isAllowed] of Object.entries(this.aclObj)) {
const [column, role, operation] = key.split('___');
const [column, role, operation] = key.split('___')
if (operation !== 'delete') {
obj[role] = obj[role] || {};
obj[role][operation] = obj[role][operation] || {};
obj[role][operation].columns = obj[role][operation].columns || {};
obj[role][operation].columns[column] = isAllowed;
obj[role] = obj[role] || {}
obj[role][operation] = obj[role][operation] || {}
obj[role][operation].columns = obj[role][operation].columns || {}
obj[role][operation].columns[column] = isAllowed
}
if (this.data[role] && this.data[role][operation] && this.data[role][operation].custom) {
obj[role][operation].custom = this.data[role][operation].custom;
obj[role][operation].custom = this.data[role][operation].custom
}
}
for (const [key, isAllowed] of Object.entries(this.aclRoleOpAll)) {
const [role, operation] = key.split('___');
const [role, operation] = key.split('___')
if (operation === 'delete' || !isAllowed) {
obj[role] = obj[role] || {};
obj[role][operation] = isAllowed;
obj[role] = obj[role] || {}
obj[role][operation] = isAllowed
}
}
if (this.showCustomAcl && this.custoAclTitle) {
if (!obj[this.custoAclTitle[1]][this.custoAclTitle[2]] || typeof obj[this.custoAclTitle[1]][this.custoAclTitle[2]] !== 'object')
obj[this.custoAclTitle[1]][this.custoAclTitle[2]] = {};
obj[this.custoAclTitle[1]][this.custoAclTitle[2]].custom = this.customAcl;
if (!obj[this.custoAclTitle[1]][this.custoAclTitle[2]] || typeof obj[this.custoAclTitle[1]][this.custoAclTitle[2]] !== 'object') {
obj[this.custoAclTitle[1]][this.custoAclTitle[2]] = {}
}
obj[this.custoAclTitle[1]][this.custoAclTitle[2]].custom = this.customAcl
}
console.log(obj)
try {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
@@ -334,20 +383,21 @@ export default {
}, 'xcAclSave', {
tn: this.nodes.tn || this.nodes.view_name,
acl: obj
}]);
}])
if (this.showCustomAcl) {
this.$toast.success('Custom RBAC saved successfully').goAway(3000);
this.showCustomAcl = false;
} else
this.$toast.success('RBAC saved successfully').goAway(3000);
this.edited = false;
this.$toast.success('Custom RBAC saved successfully').goAway(3000)
this.showCustomAcl = false
} else {
this.$toast.success('RBAC saved successfully').goAway(3000)
}
this.edited = false
await this.load()
} catch (e) {
this.$toast.error(e.message).goAway(3000);
this.$toast.error(e.message).goAway(3000)
}
},
async loadColumnList() {
async loadColumnList () {
// const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
@@ -360,46 +410,45 @@ export default {
dbAlias: this.nodes.dbAlias
}, 'tableXcModelGet', {
tn: this.nodes.tn || this.nodes.view_name
}]);
}])
const meta = JSON.parse(result.meta);
this.columns = meta.columns;
const meta = JSON.parse(result.meta)
this.columns = meta.columns
},
async loadAcl() {
this.loading = true;
async loadAcl () {
this.loading = true
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'xcAclGet', {
tn: this.nodes.tn || this.nodes.view_name
}]);
this.data = JSON.parse(result.acl);
}])
this.data = JSON.parse(result.acl)
this.loading = false;
this.loading = false
},
onAclRoleOpAllChange(isEnabled, name) {
this.edited = true;
const obj = {};
onAclRoleOpAllChange (isEnabled, name) {
this.edited = true
const obj = {}
for (const key in this.aclObj) {
if (key.split('___').slice(1).join('___') === name) {
obj[key] = isEnabled;
obj[key] = isEnabled
} else {
obj[key] = this.aclObj[key];
obj[key] = this.aclObj[key]
}
}
this.aclObj = obj;
this.aclObj = obj
},
onPermissionChange(isEnabled, name) {
this.edited = true;
onPermissionChange (isEnabled, name) {
this.edited = true
if (isEnabled && !this.aclRoleOpAll[name]) {
this.$set(this.aclRoleOpAll, name, true);
this.$set(this.aclRoleOpAll, name, true)
}
if (!isEnabled && this.aclRoleOpAll[name]) {
this.$set(this.aclRoleOpAll, name, Object.entries(this.aclObj).some(([key, enabled]) => key.endsWith(name) && enabled));
this.$set(this.aclRoleOpAll, name, Object.entries(this.aclObj).some(([key, enabled]) => key.endsWith(name) && enabled))
}
}
},
}
// watch: {
// aclRoleOpAll: {
// // todo: fix toggle issue
@@ -418,9 +467,10 @@ export default {
</script>
<style scoped lang="scss">
tr,th{
tr, th {
border: 1px solid grey;
}
td, th {
border-left: 1px solid grey;
}
@@ -448,7 +498,6 @@ th.permission {
// filter: none;
//}
::v-deep {
.permission-checkbox-container .v-input {
transform: scale(.8);
@@ -459,7 +508,8 @@ th.permission {
display: flex;
align-items: center;
}
table{
table {
border-collapse: collapse;
}
}

View File

@@ -1,68 +1,81 @@
<template>
<v-row class="pa-0 ma-0">
<v-overlay absolute v-if="isMetaTable">
<v-alert type="info">Meta tables are not editable</v-alert>
<v-overlay v-if="isMetaTable" absolute>
<v-alert type="info">
Meta tables are not editable
</v-alert>
</v-overlay>
<v-col cols="12 pa-0">
<v-card class="elevation-0">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: this.nodes.env,
disabled: true,
href: '#'
},{
text: this.nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: this.nodes._tn + ' (table)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes._tn + ' (table)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Load Triggers"
small
class="primary"
color="primary"
@click="loadTriggerList"
v-ge="['triggers','load']"
<v-spacer />
<x-btn
v-ge="['triggers','load']"
outlined
tooltip="Load Triggers"
small
class="primary"
color="primary"
@click="loadTriggerList"
>
<v-icon small left>refresh</v-icon>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn outlined tooltip="Create New Trigger"
small
@click="showTriggerDlg()"
class="primary"
color="primary"
icon="mdi-plus"
v-ge="['triggers','new']"
>New Trigger
</x-btn>
<x-btn outlined tooltip="Delete Table"
small
icon="mdi-delete-outline"
@click="deleteTable('showDialog')"
class="error text-right"
color="error "
v-ge="['triggers','delete']"
>Delete Table
</x-btn
<x-btn
v-ge="['triggers','new']"
outlined
tooltip="Create New Trigger"
small
class="primary"
color="primary"
icon="mdi-plus"
@click="showTriggerDlg()"
>
</v-toolbar
>
<v-skeleton-loader type="table" v-if="loading"></v-skeleton-loader>
New Trigger
</x-btn>
<x-btn
v-ge="['triggers','delete']"
outlined
tooltip="Delete Table"
small
icon="mdi-delete-outline"
class="error text-right"
color="error "
@click="deleteTable('showDialog')"
>
Delete Table
</x-btn>
</v-toolbar>
<v-skeleton-loader v-if="loading" type="table" />
<v-data-table
v-else
dense
@@ -70,7 +83,7 @@
:items="triggers"
footer-props.items-per-page-options="30"
>
<template v-slot:item="props">
<template #item="props">
<tr>
<td>{{ props.item.trigger }}</td>
<td>{{ props.item.event }}</td>
@@ -78,17 +91,21 @@
<td>{{ props.item.statement }}</td>
<td>
<div>
<x-icon color="primary" @click="showTriggerDlg(props.item)"
small
v-ge="['triggers','edit']"
>mdi-square-edit-outline
<x-icon
v-ge="['triggers','edit']"
color="primary"
small
@click="showTriggerDlg(props.item)"
>
mdi-square-edit-outline
</x-icon>
<x-icon
v-ge="['triggers','delete']"
small
color="error"
@click="deleteTrigger('showDialog', props.item)"
v-ge="['triggers','delete']"
>mdi-delete-forever
>
mdi-delete-forever
</x-icon>
</div>
</td>
@@ -99,17 +116,17 @@
<triggerAddEditDlg
v-if="dialogShow"
:nodes="nodes"
:triggerObject="selectedTrigger"
:newTrigger="!selectedTrigger.trigger"
:dialogShow="dialogShow"
:mtdDialogSubmit="mtdTriggerDlgSubmit"
:mtdDialogCancel="mtdTriggerDlgCancel"
:trigger-object="selectedTrigger"
:new-trigger="!selectedTrigger.trigger"
:dialog-show="dialogShow"
:mtd-dialog-submit="mtdTriggerDlgSubmit"
:mtd-dialog-cancel="mtdTriggerDlgCancel"
/>
<dlgLabelSubmitCancel
type="error"
v-if="showTriggerDeleteDialog"
:dialogShow="showTriggerDeleteDialog"
:actionsMtd="deleteTrigger"
type="error"
:dialog-show="showTriggerDeleteDialog"
:actions-mtd="deleteTrigger"
heading="Click Submit to Delete the Trigger"
/>
</v-col>
@@ -117,34 +134,34 @@
</template>
<script>
import {mapGetters, mapActions} from "vuex";
import { mapGetters } from 'vuex'
import triggerAddEditDlg from "../dlgs/dlgTriggerAddEdit";
import dlgLabelSubmitCancel from "../../utils/dlgLabelSubmitCancel";
import triggerAddEditDlg from '../dlgs/dlgTriggerAddEdit'
import dlgLabelSubmitCancel from '../../utils/dlgLabelSubmitCancel'
export default {
components: {triggerAddEditDlg, dlgLabelSubmitCancel},
data() {
components: { triggerAddEditDlg, dlgLabelSubmitCancel },
data () {
return {
loading: false,
showTriggerDeleteDialog: false,
triggers: [],
headers: [
{
text: "Trigger Name",
text: 'Trigger Name',
sortable: false
},
{text: "Event", sortable: false},
{text: "Timing", sortable: false},
{text: "Statement", sortable: false},
{text: "", sortable: false}
{ text: 'Event', sortable: false },
{ text: 'Timing', sortable: false },
{ text: 'Statement', sortable: false },
{ text: '', sortable: false }
],
dialogShow: false,
selectedTrigger: null
};
}
},
methods: {
async handleKeyDown({metaKey, key, altKey, shiftKey, ctrlKey}) {
async handleKeyDown ({ metaKey, key, altKey, shiftKey, ctrlKey }) {
console.log(metaKey, key, altKey, shiftKey, ctrlKey)
// cmd + s -> save
// cmd + l -> reload
@@ -157,25 +174,24 @@ export default {
// await this.applyChanges();
// break;
case 'true_l' :
await this.loadTriggerList();
break;
await this.loadTriggerList()
break
case 'true_n' :
this.showTriggerDlg();
break;
this.showTriggerDlg()
break
case 'true_d' :
await this.deleteTable('showDialog');
break;
await this.deleteTable('showDialog')
break
}
},
async loadTriggerList() {
this.loading = true;
async loadTriggerList () {
this.loading = true
try {
this.$store.commit('notification/MutToggleProgressBar', true);
this.$store.commit('notification/MutToggleProgressBar', true)
if (this.newTable) {
this.$store.commit('notification/MutToggleProgressBar', false);
return;
this.$store.commit('notification/MutToggleProgressBar', false)
return
}
// const client = await this.sqlMgr.projectGetSqlClient({
// env: this.nodes.env,
@@ -200,122 +216,116 @@ export default {
tn: this.nodes.tn
}])
console.log("triggers", result.data.list);
this.triggers = result.data.list;
console.log('triggers', result.data.list)
this.triggers = result.data.list
} catch (e) {
console.log(e);
console.log(e)
} finally {
this.$store.commit('notification/MutToggleProgressBar', false);
this.$store.commit('notification/MutToggleProgressBar', false)
}
this.loading = false;
this.loading = false
},
showTriggerDlg(trigger) {
if (trigger) this.selectedTrigger = trigger;
else this.selectedTrigger = {};
this.dialogShow = true;
showTriggerDlg (trigger) {
if (trigger) { this.selectedTrigger = trigger } else { this.selectedTrigger = {} }
this.dialogShow = true
},
async mtdTriggerDlgSubmit(triggerObject, newTrigger) {
async mtdTriggerDlgSubmit (triggerObject, newTrigger) {
try {
// const client = await this.sqlMgr.projectGetSqlClient({
// env: this.nodes.env,
// dbAlias: this.nodes.dbAlias
// });
if (newTrigger) {
let result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [
const result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
},
"triggerCreate",
'triggerCreate',
triggerObject
]);
console.log("triggerCreate result: ", result);
this.$toast.success('Trigger created successfully').goAway(3000);
])
console.log('triggerCreate result: ', result)
this.$toast.success('Trigger created successfully').goAway(3000)
} else {
let result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [
const result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
},
"triggerUpdate",
'triggerUpdate',
{
...triggerObject,
oldStatement: this.selectedTrigger.statement
}]);
}])
console.log("triggerUpdate result: ", result);
this.$toast.success('Trigger updated successfully').goAway(3000);
console.log('triggerUpdate result: ', result)
this.$toast.success('Trigger updated successfully').goAway(3000)
}
await this.loadTriggerList();
this.selectedTrigger = null;
this.dialogShow = false;
await this.loadTriggerList()
this.selectedTrigger = null
this.dialogShow = false
} catch (error) {
console.error("triggerCreate error: ", error);
console.error('triggerCreate error: ', error)
}
},
mtdTriggerDlgCancel() {
this.dialogShow = false;
this.selectedTrigger = null;
mtdTriggerDlgCancel () {
this.dialogShow = false
this.selectedTrigger = null
},
async deleteTrigger(action = "", trigger) {
if (action === "showDialog") {
this.showTriggerDeleteDialog = true;
this.selectedTriggerForDelete = trigger;
} else if (action === "hideDialog") {
this.showTriggerDeleteDialog = false;
this.selectedTriggerForDelete = null;
async deleteTrigger (action = '', trigger) {
if (action === 'showDialog') {
this.showTriggerDeleteDialog = true
this.selectedTriggerForDelete = trigger
} else if (action === 'hideDialog') {
this.showTriggerDeleteDialog = false
this.selectedTriggerForDelete = null
} else {
let result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [
const result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
},
"triggerDelete",
'triggerDelete',
{
...this.selectedTriggerForDelete,
tn: this.nodes.tn,
oldStatement: this.selectedTriggerForDelete.statement
}]);
}])
console.log("triggerDelete result ", result);
await this.loadTriggerList();
this.showTriggerDeleteDialog = false;
this.selectedTriggerForDelete = null;
console.log('triggerDelete result ', result)
await this.loadTriggerList()
this.showTriggerDeleteDialog = false
this.selectedTriggerForDelete = null
this.$toast.success('Trigger deleted successfully').goAway(3000);
this.$toast.success('Trigger deleted successfully').goAway(3000)
}
}
},
computed: {...mapGetters({sqlMgr: "sqlMgr/sqlMgr"})},
computed: { ...mapGetters({ sqlMgr: 'sqlMgr/sqlMgr' }) },
beforeCreated() {
beforeCreated () {
},
created() {
this.loadTriggerList();
},
mounted() {
},
beforeDestroy() {
console.log("triggerlist before destroy");
},
destroy() {
},
validate({params}) {
return true;
},
head() {
return {};
},
props: ["nodes", "newTable", "deleteTable", 'isMetaTable'],
watch: {},
directives: {}
};
created () {
this.loadTriggerList()
},
mounted () {
},
beforeDestroy () {
console.log('triggerlist before destroy')
},
destroy () {
},
directives: {},
validate ({ params }) {
return true
},
head () {
return {}
},
props: ['nodes', 'newTable', 'deleteTable', 'isMetaTable']
}
</script>
<style scoped>

View File

@@ -1,36 +1,51 @@
<template>
<v-card class="pb-2">
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-spacer></v-spacer>
<x-btn outlined tooltip="Validation Documentation"
color="primary"
small
href="https://docs.nocodb.com/en/v0.5/database/database-model-validation" target="_blank">
<v-icon small left>mdi-book-open-variant</v-icon>
<v-spacer />
<x-btn
outlined
tooltip="Validation Documentation"
color="primary"
small
href="https://docs.nocodb.com/en/v0.5/database/database-model-validation"
target="_blank"
>
<v-icon small left>
mdi-book-open-variant
</v-icon>
Validation Docs
</x-btn>
<x-btn outlined tooltip="Reload validation"
color="primary"
small
@click="loadTableModelMeta">
<v-icon small left>refresh</v-icon>
<x-btn
outlined
tooltip="Reload validation"
color="primary"
small
@click="loadTableModelMeta"
>
<v-icon small left>
refresh
</v-icon>
Reload
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
small
:disabled="!edited || loading"
@click.prevent="saveValidations">
<v-icon small left>save</v-icon>
<x-btn
outlined
tooltip="Save Changes"
color="primary"
small
:disabled="!edited || loading"
@click.prevent="saveValidations"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<template v-if="columns">
<p class="title mt-6 mb-6"> Table : <span class="text-capitalize">{{ columns._tn }}</span><br>
<span class="font-weight-thin">Write Validations For Columns</span></p>
<p class="title mt-6 mb-6">
Table : <span class="text-capitalize">{{ columns._tn }}</span><br>
<span class="font-weight-thin">Write Validations For Columns</span>
</p>
<!-- <v-row justify="center">
<v-col cols="4" class="d-flex align-center">
@@ -41,45 +56,51 @@
</v-col>
</v-row>-->
<v-simple-table style="max-width:800px; margin:0 auto" class="mb-10" dense v-slot:default>
<thead>
<tr>
<th>Column Name</th>
<th>Alias</th>
<th>Validators</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,i) in columns.columns">
<td>{{ item.cn }}</td>
<td>
<v-edit-dialog lazy>
<span> {{ item._cn }}</span>
<template v-slot:input>
<v-text-field
@input="edited=true"
v-model="item._cn"
label="Edit"
single-line
></v-text-field>
</template>
</v-edit-dialog>
</td>
<td>
<x-btn
@click="editOrAddValidation(item)"
:tooltip="`Edit/Add validation for '${item.cn}' column`" color="primary"
style="text-transform:capitalize" x-small outlined>
<v-icon class=" mr-1" x-small>mdi-lead-pencil</v-icon>
Validation ({{ item.validate.func.length }})
</x-btn>
</td>
</tr>
</tbody>
<v-simple-table style="max-width:800px; margin:0 auto" class="mb-10" dense>
<template #default>
<thead>
<tr>
<th>Column Name</th>
<th>Alias</th>
<th>Validators</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,i) in columns.columns" :key="i">
<td>{{ item.cn }}</td>
<td>
<v-edit-dialog lazy>
<span> {{ item._cn }}</span>
<template #input>
<v-text-field
v-model="item._cn"
label="Edit"
single-line
@input="edited=true"
/>
</template>
</v-edit-dialog>
</td>
<td>
<x-btn
:tooltip="`Edit/Add validation for '${item.cn}' column`"
color="primary"
style="text-transform:capitalize"
x-small
outlined
@click="editOrAddValidation(item)"
>
<v-icon class=" mr-1" x-small>
mdi-lead-pencil
</v-icon>
Validation ({{ item.validate.func.length }})
</x-btn>
</td>
</tr>
</tbody>
</template>
</v-simple-table>
<!-- <v-expansion-panels accordion style="width: 70%; min-width: 500px; margin:5px auto">-->
<!-- <v-expansion-panel-->
<!-- v-for="(item,i) in columns.columns"-->
@@ -158,7 +179,6 @@
<!-- &lt;!&ndash; </v-edit-dialog>&ndash;&gt;-->
<!-- &lt;!&ndash; </td>&ndash;&gt;-->
<!-- <td>-->
<!-- <x-icon-->
<!-- small-->
@@ -180,98 +200,115 @@
<!-- </v-expansion-panels>-->
</template>
<v-dialog v-model="validatorEditDialog" max-width="700px">
<v-card v-if="clickedItem">
<v-card-title class="headline justify-center mb-5">Validations for '{{ clickedItem.cn }}({{ clickedItem.dt }})'
<v-card-title class="headline justify-center mb-5">
Validations for '{{ clickedItem.cn }}({{ clickedItem.dt }})'
</v-card-title>
<v-card-text>
<div class="d-flex">
<v-spacer>
</v-spacer>
<v-spacer />
<v-btn outlined x-small @click="validatorEditDialog = false">Cancel</v-btn>
<x-btn outlined tooltip="Add new validation"
color="primary"
x-small
@click.prevent="(clickedItem.validate.func.push(''),edited=true,scrollAndFocusLastRowInModal())">
<v-icon small left>mdi-plus</v-icon>
<v-btn outlined x-small @click="validatorEditDialog = false">
Cancel
</v-btn>
<x-btn
outlined
tooltip="Add new validation"
color="primary"
x-small
@click.prevent="(clickedItem.validate.func.push(''),edited=true,scrollAndFocusLastRowInModal())"
>
<v-icon small left>
mdi-plus
</v-icon>
Add Validation
</x-btn>
<v-btn outlined color="primary" x-small @click.prevent="saveValidationForColumn(clickedItem)">Save</v-btn>
<v-btn outlined color="primary" x-small @click.prevent="saveValidationForColumn(clickedItem)">
Save
</v-btn>
</div>
<v-simple-table v-if="clickedItem.validate.func && clickedItem.validate.func.length" dense>
<thead>
<tr>
<th class="caption font-weight-normal">Validation Function</th>
<th class="caption font-weight-normal">Error Message</th>
<!-- <th class="caption font-weight-normal">Optional Argument</th>-->
<th></th>
</tr>
<tr>
<th class="caption font-weight-normal">
Validation Function
</th>
<th class="caption font-weight-normal">
Error Message
</th>
<!-- <th class="caption font-weight-normal">Optional Argument</th>-->
<th />
</tr>
</thead>
<tbody>
<tr v-for="(func,i) in clickedItem.validate.func">
<td>
<v-edit-dialog
lazy
>
<span>{{ func }}</span>
<template v-slot:input>
<tr v-for="(func,i) in clickedItem.validate.func" :key="i">
<td>
<v-edit-dialog
lazy
>
<span>{{ func }}</span>
<template #input>
<v-autocomplete
v-model="clickedItem.validate.func[i]"
dense
:items="fnList"
item-text="func"
@change="onFunctionChange(i,clickedItem)"
/>
</template>
</v-edit-dialog>
</td>
<td>
<v-edit-dialog lazy>
<span> {{ clickedItem.validate.msg[i] }}</span>
<template #input>
<v-text-field
v-model="clickedItem.validate.msg[i]"
label="Edit"
single-line
@input="edited=true"
/>
</template>
</v-edit-dialog>
</td>
<!-- <td>-->
<!-- <v-edit-dialog lazy>-->
<!-- <span> {{ clickedItem.validate.args[i] }}</span>-->
<!-- <template v-slot:input>-->
<!-- <v-text-field-->
<!-- @input="edited=true"-->
<!-- v-model="clickedItem.validate.args[i]"-->
<!-- label="Edit"-->
<!-- single-line-->
<!-- ></v-text-field>-->
<!-- </template>-->
<!-- </v-edit-dialog>-->
<!-- </td>-->
<v-autocomplete
@change="onFunctionChange(i,clickedItem)"
dense
v-model="clickedItem.validate.func[i]"
:items="fnList"
item-text="func"
></v-autocomplete>
</template>
</v-edit-dialog>
</td>
<td>
<v-edit-dialog lazy>
<span> {{ clickedItem.validate.msg[i] }}</span>
<template v-slot:input>
<v-text-field
@input="edited=true"
v-model="clickedItem.validate.msg[i]"
label="Edit"
single-line
></v-text-field>
</template>
</v-edit-dialog>
</td>
<!-- <td>-->
<!-- <v-edit-dialog lazy>-->
<!-- <span> {{ clickedItem.validate.args[i] }}</span>-->
<!-- <template v-slot:input>-->
<!-- <v-text-field-->
<!-- @input="edited=true"-->
<!-- v-model="clickedItem.validate.args[i]"-->
<!-- label="Edit"-->
<!-- single-line-->
<!-- ></v-text-field>-->
<!-- </template>-->
<!-- </v-edit-dialog>-->
<!-- </td>-->
<td>
<x-icon
small
color="error" tooltip="Delete role" @click="deleteValidation(clickedItem,i)">
mdi-delete-forever
</x-icon>
</td>
</tr>
<td>
<x-icon
small
color="error"
tooltip="Delete role"
@click="deleteValidation(clickedItem,i)"
>
mdi-delete-forever
</x-icon>
</td>
</tr>
</tbody>
</v-simple-table>
<div v-else class="d-flex justify-center">
<v-alert dense outlined type="info" color="grey lighten-1" icon="mdi-information-outline"
class="caption mt-4"
style="width:auto">
<v-alert
dense
outlined
type="info"
color="grey lighten-1"
icon="mdi-information-outline"
class="caption mt-4"
style="width:auto"
>
No validation for '{{ clickedItem.cn }}'
</v-alert>
</div>
@@ -282,175 +319,171 @@
<!-- </v-card-actions>-->
</v-card>
</v-dialog>
</v-card>
</template>
<script>
import {SqlUI} from "@/helpers/SqlUiFactory";
const validatorFnList = [{"func": "contains", "args": '', msg: 'Error contains'}, {
"func": "equals",
"args": '',
const validatorFnList = [{ func: 'contains', args: '', msg: 'Error contains' }, {
func: 'equals',
args: '',
msg: 'Error equals'
}, {
"func": "isAfter",
"args": '',
func: 'isAfter',
args: '',
msg: 'Error isAfter'
}, {"func": "isAlpha", "args": '', msg: 'Error isAlpha'}, {
"func": "isAlphanumeric",
"args": '',
}, { func: 'isAlpha', args: '', msg: 'Error isAlpha' }, {
func: 'isAlphanumeric',
args: '',
msg: 'Error isAlphanumeric'
}, {
"func": "isAscii",
"args": '',
func: 'isAscii',
args: '',
msg: 'Error isAscii'
}, {"func": "isBase32", "args": '', msg: 'Error isBase32'}, {"func": "isBase64", "args": '', msg: 'Error isBase64'}, {
"func": "isBefore",
"args": '',
}, { func: 'isBase32', args: '', msg: 'Error isBase32' }, { func: 'isBase64', args: '', msg: 'Error isBase64' }, {
func: 'isBefore',
args: '',
msg: 'Error isBefore'
}, {"func": "isBIC", "args": '', msg: 'Error isBIC'}, {"func": "isBoolean", "args": '', msg: 'Error isBoolean'}, {
"func": "isBtcAddress",
"args": '',
}, { func: 'isBIC', args: '', msg: 'Error isBIC' }, { func: 'isBoolean', args: '', msg: 'Error isBoolean' }, {
func: 'isBtcAddress',
args: '',
msg: 'Error isBtcAddress'
}, {"func": "isByteLength", "args": '', msg: 'Error isByteLength'}, {
"func": "isCreditCard",
"args": '',
}, { func: 'isByteLength', args: '', msg: 'Error isByteLength' }, {
func: 'isCreditCard',
args: '',
msg: 'Error isCreditCard'
}, {
"func": "isCurrency",
"args": '',
func: 'isCurrency',
args: '',
msg: 'Error isCurrency'
}, {"func": "isDataURI", "args": '', msg: 'Error isDataURI'}, {"func": "isDate", "args": '', msg: 'Error isDate'}, {
"func": "isDecimal",
"args": '',
}, { func: 'isDataURI', args: '', msg: 'Error isDataURI' }, { func: 'isDate', args: '', msg: 'Error isDate' }, {
func: 'isDecimal',
args: '',
msg: 'Error isDecimal'
}, {"func": "isDivisibleBy", "args": '', msg: 'Error isDivisibleBy'}, {
"func": "isEAN",
"args": '',
}, { func: 'isDivisibleBy', args: '', msg: 'Error isDivisibleBy' }, {
func: 'isEAN',
args: '',
msg: 'Error isEAN'
}, {
"func": "isEmail",
"args": '',
func: 'isEmail',
args: '',
msg: 'Error isEmail'
}, {"func": "isEmpty", "args": '', msg: 'Error isEmpty'}, {
"func": "isEthereumAddress",
"args": '',
}, { func: 'isEmpty', args: '', msg: 'Error isEmpty' }, {
func: 'isEthereumAddress',
args: '',
msg: 'Error isEthereumAddress'
}, {
"func": "isFloat",
"args": '',
func: 'isFloat',
args: '',
msg: 'Error isFloat'
}, {"func": "isFQDN", "args": '', msg: 'Error isFQDN'}, {"func": "isFullWidth", "args": '', msg: 'Error isFullWidth'}, {
"func": "isHalfWidth",
"args": '',
}, { func: 'isFQDN', args: '', msg: 'Error isFQDN' }, { func: 'isFullWidth', args: '', msg: 'Error isFullWidth' }, {
func: 'isHalfWidth',
args: '',
msg: 'Error isHalfWidth'
}, {"func": "isHash", "args": '', msg: 'Error isHash'}, {
"func": "isHexadecimal",
"args": '',
}, { func: 'isHash', args: '', msg: 'Error isHash' }, {
func: 'isHexadecimal',
args: '',
msg: 'Error isHexadecimal'
}, {
"func": "isHexColor",
"args": '',
func: 'isHexColor',
args: '',
msg: 'Error isHexColor'
}, {"func": "isHSL", "args": '', msg: 'Error isHSL'}, {"func": "isIBAN", "args": '', msg: 'Error isIBAN'}, {
"func": "isIdentityCard",
"args": '',
}, { func: 'isHSL', args: '', msg: 'Error isHSL' }, { func: 'isIBAN', args: '', msg: 'Error isIBAN' }, {
func: 'isIdentityCard',
args: '',
msg: 'Error isIdentityCard'
}, {"func": "isIMEI", "args": '', msg: 'Error isIMEI'}, {
"func": "isIn",
"args": '',
}, { func: 'isIMEI', args: '', msg: 'Error isIMEI' }, {
func: 'isIn',
args: '',
msg: 'Error isIn'
}, {"func": "isInt", "args": '', msg: 'Error isInt'}, {
"func": "isIP",
"args": '',
}, { func: 'isInt', args: '', msg: 'Error isInt' }, {
func: 'isIP',
args: '',
msg: 'Error isIP'
}, {"func": "isIPRange", "args": '', msg: 'Error isIPRange'}, {"func": "isISBN", "args": '', msg: 'Error isISBN'}, {
"func": "isISIN",
"args": '',
}, { func: 'isIPRange', args: '', msg: 'Error isIPRange' }, { func: 'isISBN', args: '', msg: 'Error isISBN' }, {
func: 'isISIN',
args: '',
msg: 'Error isISIN'
}, {"func": "isISO8601", "args": '', msg: 'Error isISO8601'}, {
"func": "isISO31661Alpha2",
"args": '',
}, { func: 'isISO8601', args: '', msg: 'Error isISO8601' }, {
func: 'isISO31661Alpha2',
args: '',
msg: 'Error isISO31661Alpha2'
}, {
"func": "isISO31661Alpha3",
"args": '',
func: 'isISO31661Alpha3',
args: '',
msg: 'Error isISO31661Alpha3'
}, {"func": "isISRC", "args": '', msg: 'Error isISRC'}, {
"func": "isISSN",
"args": '',
}, { func: 'isISRC', args: '', msg: 'Error isISRC' }, {
func: 'isISSN',
args: '',
msg: 'Error isISSN'
}, {"func": "isJSON", "args": '', msg: 'Error isJSON'}, {
"func": "isJWT",
"args": '',
}, { func: 'isJSON', args: '', msg: 'Error isJSON' }, {
func: 'isJWT',
args: '',
msg: 'Error isJWT'
}, {"func": "isLatLong", "args": '', msg: 'Error isLatLong'}, {"func": "isLength", "args": '', msg: 'Error isLength'}, {
"func": "isLocale",
"args": '',
}, { func: 'isLatLong', args: '', msg: 'Error isLatLong' }, { func: 'isLength', args: '', msg: 'Error isLength' }, {
func: 'isLocale',
args: '',
msg: 'Error isLocale'
}, {"func": "isLowercase", "args": '', msg: 'Error isLowercase'}, {
"func": "isMACAddress",
"args": '',
}, { func: 'isLowercase', args: '', msg: 'Error isLowercase' }, {
func: 'isMACAddress',
args: '',
msg: 'Error isMACAddress'
}, {
"func": "isMagnetURI",
"args": '',
func: 'isMagnetURI',
args: '',
msg: 'Error isMagnetURI'
}, {"func": "isMD5", "args": '', msg: 'Error isMD5'}, {"func": "isMimeType", "args": '', msg: 'Error isMimeType'}, {
"func": "isMobilePhone",
"args": '',
}, { func: 'isMD5', args: '', msg: 'Error isMD5' }, { func: 'isMimeType', args: '', msg: 'Error isMimeType' }, {
func: 'isMobilePhone',
args: '',
msg: 'Error isMobilePhone'
}, {"func": "isMongoId", "args": '', msg: 'Error isMongoId'}, {
"func": "isMultibyte",
"args": '',
}, { func: 'isMongoId', args: '', msg: 'Error isMongoId' }, {
func: 'isMultibyte',
args: '',
msg: 'Error isMultibyte'
}, {
"func": "isNumeric",
"args": '',
func: 'isNumeric',
args: '',
msg: 'Error isNumeric'
}, {"func": "isOctal", "args": '', msg: 'Error isOctal'}, {
"func": "isPassportNumber",
"args": '',
}, { func: 'isOctal', args: '', msg: 'Error isOctal' }, {
func: 'isPassportNumber',
args: '',
msg: 'Error isPassportNumber'
}, {
"func": "isPort",
"args": '',
func: 'isPort',
args: '',
msg: 'Error isPort'
}, {"func": "isPostalCode", "args": '', msg: 'Error isPostalCode'}, {
"func": "isRFC3339",
"args": '',
}, { func: 'isPostalCode', args: '', msg: 'Error isPostalCode' }, {
func: 'isRFC3339',
args: '',
msg: 'Error isRFC3339'
}, {
"func": "isRgbColor",
"args": '',
func: 'isRgbColor',
args: '',
msg: 'Error isRgbColor'
}, {"func": "isSemVer", "args": '', msg: 'Error isSemVer'}, {
"func": "isSurrogatePair",
"args": '',
}, { func: 'isSemVer', args: '', msg: 'Error isSemVer' }, {
func: 'isSurrogatePair',
args: '',
msg: 'Error isSurrogatePair'
}, {
"func": "isUppercase",
"args": '',
func: 'isUppercase',
args: '',
msg: 'Error isUppercase'
}, {"func": "isSlug", "args": '', msg: 'Error isSlug'}, {
"func": "isTaxID",
"args": '',
}, { func: 'isSlug', args: '', msg: 'Error isSlug' }, {
func: 'isTaxID',
args: '',
msg: 'Error isTaxID'
}, {"func": "isURL", "args": '', msg: 'Error isURL'}, {
"func": "isUUID",
"args": '',
}, { func: 'isURL', args: '', msg: 'Error isURL' }, {
func: 'isUUID',
args: '',
msg: 'Error isUUID'
}, {"func": "isVariableWidth", "args": '', msg: 'Error isVariableWidth'}, {
"func": "isWhitelisted",
"args": '',
}, { func: 'isVariableWidth', args: '', msg: 'Error isVariableWidth' }, {
func: 'isWhitelisted',
args: '',
msg: 'Error isWhitelisted'
}, {"func": "matches", "args": '', msg: 'Error matches'}];
}, { func: 'matches', args: '', msg: 'Error matches' }]
export default {
name: "validation",
name: 'Validation',
props: ['nodes'],
data: () => ({
fnList: validatorFnList,
@@ -462,14 +495,23 @@ export default {
clickedItem: null,
validatorEditDialog: false
}),
async created () {
// try {
// await this.loadColumnList();
await this.loadTableModelMeta()
// } catch (e) {
// throw e
// } finally {
//
// }
},
methods: {
editOrAddValidation(item) {
this.clickedItem = JSON.parse(JSON.stringify(item));
this.validatorEditDialog = true;
editOrAddValidation (item) {
this.clickedItem = JSON.parse(JSON.stringify(item))
this.validatorEditDialog = true
},
// async loadColumnList() {
// const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
// env: this.nodes.env,
@@ -480,45 +522,45 @@ export default {
// console.log("table ", result.data.list);
// this.columns = result.data.list;
// },
async loadTableModelMeta() {
this.edited = false;
async loadTableModelMeta () {
this.edited = false
this.tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'tableXcModelGet', {
tn: this.nodes.tn
}]);
}])
this.columns = JSON.parse(this.tableMeta.meta)
},
scrollAndFocusLastRow() {
scrollAndFocusLastRow () {
this.$nextTick(() => {
const menuActivator = this.$el && this.$el.querySelector('.v-expansion-panel--active table tr:last-child .v-small-dialog__activator__content');
const menuActivator = this.$el && this.$el.querySelector('.v-expansion-panel--active table tr:last-child .v-small-dialog__activator__content')
if (menuActivator) {
menuActivator.click();
menuActivator.click()
this.$nextTick(() => {
const inputField = document.querySelector('.menuable__content__active input');
const inputField = document.querySelector('.menuable__content__active input')
inputField && inputField.select()
})
}
})
},
scrollAndFocusLastRowInModal() {
scrollAndFocusLastRowInModal () {
this.$nextTick(() => {
const modal = document.querySelector('.v-dialog--active');
modal.scrollTop = 99999;
const menuActivator = modal.querySelector('table tr:last-child .v-small-dialog__activator__content');
const modal = document.querySelector('.v-dialog--active')
modal.scrollTop = 99999
const menuActivator = modal.querySelector('table tr:last-child .v-small-dialog__activator__content')
if (menuActivator) {
menuActivator.click();
menuActivator.click()
this.$nextTick(() => {
const inputField = document.querySelector('.menuable__content__active input');
const inputField = document.querySelector('.menuable__content__active input')
inputField && inputField.select()
})
}
})
},
async saveValidations() {
this.edited = false;
async saveValidations () {
this.edited = false
try {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
@@ -526,47 +568,35 @@ export default {
}, 'xcModelSet', {
tn: this.nodes.tn,
meta: this.columns
}]);
this.$toast.success('Successfully updated validations').goAway(3000);
}])
this.$toast.success('Successfully updated validations').goAway(3000)
} catch (e) {
this.$toast.error('Failed to update validations').goAway(3000);
this.$toast.error('Failed to update validations').goAway(3000)
}
},
async saveValidationForColumn(clickedItem) {
async saveValidationForColumn (clickedItem) {
if (clickedItem) {
const item = this.columns.columns.find(it => it.cn === clickedItem.cn);
const item = this.columns.columns.find(it => it.cn === clickedItem.cn)
if (item) {
Object.assign(item, clickedItem);
await this.saveValidations();
this.validatorEditDialog = false;
this.clickedItem = null;
Object.assign(item, clickedItem)
await this.saveValidations()
this.validatorEditDialog = false
this.clickedItem = null
}
}
},
onFunctionChange(i, item) {
this.edited = true;
const fn = validatorFnList.find(({func}) => func === item.validate.func[i]);
item.validate.msg[i] = `Validation failed : ${item.validate.func[i]}(${this.nodes.tn}.${item.cn})`;
item.validate.args[i] = fn.args;
onFunctionChange (i, item) {
this.edited = true
const fn = validatorFnList.find(({ func }) => func === item.validate.func[i])
item.validate.msg[i] = `Validation failed : ${item.validate.func[i]}(${this.nodes.tn}.${item.cn})`
item.validate.args[i] = fn.args
},
deleteValidation(item, i) {
this.edited = true;
item.validate.func.splice(i, 1);
item.validate.args.splice(i, 1);
item.validate.msg.splice(i, 1);
deleteValidation (item, i) {
this.edited = true
item.validate.func.splice(i, 1)
item.validate.args.splice(i, 1)
item.validate.msg.splice(i, 1)
}
},
async created() {
try {
// await this.loadColumnList();
await this.loadTableModelMeta();
} catch (e) {
throw e;
} finally {
}
}
}
</script>

View File

@@ -1,96 +1,102 @@
<template>
<div>
<div class="d-flex">
<v-select
v-model="api.method"
outlined
dense
class="caption"
v-model="api.method"
:items="Object.keys(apiMethodMeta)" style="max-width:100px;"></v-select>
:items="Object.keys(apiMethodMeta)"
style="max-width:100px;"
/>
<v-text-field
v-model="api.path"
outlined
placeholder="http://example.com"
v-model="api.path"
dense class="flex-grow-1 ml-2 caption"></v-text-field>
dense
class="flex-grow-1 ml-2 caption"
/>
</div>
<v-tabs
v-model="tab"
class="req-tabs"
height="24"
>
<v-tab v-ge="['api-client','params']" class="caption"><span class="text-capitalize"> Params&nbsp;<b
v-if="paramsCount"
class="green--text">({{ paramsCount }})</b></span>
<v-tab v-ge="['api-client','params']" class="caption">
<span class="text-capitalize"> Params&nbsp;<b
v-if="paramsCount"
class="green--text"
>({{ paramsCount }})</b></span>
</v-tab>
<v-tab class="caption" v-ge="['api-client','headers']"><span class="text-capitalize">Headers&nbsp;<b
v-if="headersCount"
class="green--text">({{
<v-tab v-ge="['api-client','headers']" class="caption">
<span class="text-capitalize">Headers&nbsp;<b
v-if="headersCount"
class="green--text"
>({{
headersCount
}})</b></span>
</v-tab>
<v-tab class="caption" v-ge="['api-client','body']"><span class="text-capitalize">Body</span></v-tab>
<v-tab class="caption" v-ge="['api-client','auth']"><span class="text-capitalize">Auth</span></v-tab>
<v-tab v-ge="['api-client','body']" class="caption">
<span class="text-capitalize">Body</span>
</v-tab>
<v-tab v-ge="['api-client','auth']" class="caption">
<span class="text-capitalize">Auth</span>
</v-tab>
<v-tab-item>
<params v-model="api.parameters"
:env.sync="selectedEnv"
></params>
<params
v-model="api.parameters"
:env.sync="selectedEnv"
/>
</v-tab-item>
<v-tab-item>
<headers v-model="api.headers"
:env.sync="selectedEnv"
></headers>
<headers
v-model="api.headers"
:env.sync="selectedEnv"
/>
</v-tab-item>
<v-tab-item>
<monaco-json-editor
style="height: 250px"
class="editor card text-left"
theme="vs-dark"
v-model="api.body"
lang="json"
:options="{validate:true,documentFormattingEdits:true,foldingRanges:true}"
>
</monaco-json-editor>
</v-tab-item>
<v-tab-item>
<monaco-json-editor
style="height: 250px"
class="editor card text-left"
theme="vs-dark"
v-model="api.auth"
lang="json"
:options="{validate:true,documentFormattingEdits:true,foldingRanges:true}"
>
</monaco-json-editor>
/>
</v-tab-item>
<v-tab-item>
<monaco-json-editor
v-model="api.auth"
style="height: 250px"
class="editor card text-left"
theme="vs-dark"
lang="json"
:options="{validate:true,documentFormattingEdits:true,foldingRanges:true}"
/>
<span class="caption grey--text">For more about auth option refer <a href="https://github.com/axios/axios#request-config" target="_blank">axios docs</a>.</span>
</v-tab-item>
</v-tabs>
</div>
</template>
<script>
import params from "../../../apiClient/params";
import headers from "../../../apiClient/headers";
import params from '../../../apiClient/params'
import headers from '../../../apiClient/headers'
import {MonacoJsonEditor} from "../../../monaco/index";
import { MonacoJsonEditor } from '../../../monaco/index'
export default {
tab:0,
props: {
value: Object
},
tab: 0,
name: 'HttpWebhook',
components: {
params,
headers,
MonacoJsonEditor
},
name: "httpWebhook",
props: {
value: Object
},
data: () => ({
apiMethodMeta: {
GET: {
@@ -110,7 +116,7 @@ export default {
},
PATCH: {
color: 'info'
},
}
},
selectedEnv: 'dev',
environmentList: ['dev'],
@@ -125,32 +131,31 @@ export default {
response: {},
perf: {},
meta: {}
},
}
}),
created() {
this.api = this.value || this.api;
computed: {
paramsCount () {
return this.api.parameters && this.api.parameters.filter(p => p.name && p.enabled).length
},
headersCount () {
return this.api.headers && this.api.headers.filter(h => h.name && h.enabled).length
}
},
watch: {
value() {
value () {
if (this.api !== this.value) {
this.api = this.value || this.api;
this.api = this.value || this.api
}
},
api: {
handler() {
handler () {
this.$emit('input', this.api)
}
}
},
computed: {
paramsCount() {
return this.api.parameters && this.api.parameters.filter(p => p.name && p.enabled).length;
},
headersCount() {
return this.api.headers && this.api.headers.filter(h => h.name && h.enabled).length;
},
created () {
this.api = this.value || this.api
}
}
</script>

View File

@@ -1,42 +1,41 @@
<template>
<v-select
v-model="hookEvent"
class="caption"
outlined
dense
v-model="hookEvent"
label="Event"
required
:items="eventList"
:item-text="v => v.text.join(' ')"
:item-value="v => v.value.join(' ')"
:rules="[v => !!v || 'Event Required']"
>
</v-select>
/>
</template>
<script>
export default {
name: "webhookEvent",
name: 'WebhookEvent',
props: ['operation', 'event'],
data: () => ({
eventList: [
// {text: ["Before", "Insert"], value: ['before', 'insert']},
{text: ["After", "Insert"], value: ['after', 'insert']},
{ text: ['After', 'Insert'], value: ['after', 'insert'] },
// {text: ["Before", "Update"], value: ['before', 'update']},
{text: ["After", "Update"], value: ['after', 'update']},
{ text: ['After', 'Update'], value: ['after', 'update'] },
// {text: ["Before", "Delete"], value: ['before', 'delete']},
{text: ["After", "Delete"], value: ['after', 'delete']},
],
{ text: ['After', 'Delete'], value: ['after', 'delete'] }
]
}),
computed: {
hookEvent: {
get() {
get () {
return `${this.event} ${this.operation}`
}, set(v) {
const [event, operation] = v.split(' ');
this.$emit('update:event', event);
this.$emit('update:operation', operation);
},
set (v) {
const [event, operation] = v.split(' ')
this.$emit('update:event', event)
this.$emit('update:operation', operation)
}
}
}

View File

@@ -1,48 +1,55 @@
<template>
<div>
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (Webhooks)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes.tn + ' (Webhooks)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<x-btn outlined tooltip="Save Changes"
color="primary"
small
:disabled="loading || !valid || !hook.event"
<x-btn
v-ge="['rows','save']"
outlined
tooltip="Save Changes"
color="primary"
small
v-ge="['rows','save']"
@click.prevent="saveHooks">
<v-icon small left>save</v-icon>
:disabled="loading || !valid || !hook.event"
@click.prevent="saveHooks"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-toolbar>
<v-form
ref="form"
v-model="valid"
class="mx-auto"
ref="form"
lazy-validation
>
<v-card>
@@ -50,29 +57,33 @@
<v-row>
<v-col cols="6">
<v-radio-group v-model="hook.event" @change="onEventChange">
<template v-slot:default>
<template #default>
<v-simple-table dense>
<template v-slot:default>
<template #default>
<thead>
<tr>
<th></th>
<th>Operation</th>
<th>Event</th>
</tr>
<tr>
<th />
<th>Operation</th>
<th>Event</th>
</tr>
</thead>
<tbody>
<tr v-for="(e,i) in eventList" :key="i"
:class="{'primary lighten-4 black--text': e.value === hook.event}">
<td>
<v-radio :value="e.value"></v-radio>
</td>
<td> {{ e.text[1] }}
</td>
<td> {{ e.text[0] }}
</td>
</tr>
<tr
v-for="(e,i) in eventList"
:key="i"
:class="{'primary lighten-4 black--text': e.value === hook.event}"
>
<td>
<v-radio :value="e.value" />
</td>
<td>
{{ e.text[1] }}
</td>
<td>
{{ e.text[0] }}
</td>
</tr>
</tbody>
</template>
</v-simple-table>
@@ -83,7 +94,6 @@
<!-- <v-radio v-for="(e,i) in eventList" :key="i" :label="e.text" :value="e.v"></v-radio>-->
<!-- </v-radio-group>-->
<!-- <v-select-->
<!-- v-model="hook.event"-->
<!-- :items="['Before','After']"-->
@@ -103,21 +113,20 @@
<v-card-title>Webhook</v-card-title>
<v-card-text>
<v-text-field
:disabled="!hook.event"
v-model="hook.title"
:disabled="!hook.event"
label="Title"
required
:rules="[v => !!v || 'Title Required']"
></v-text-field>
/>
<v-text-field
:disabled="!hook.event"
v-model="hook.url"
:disabled="!hook.event"
label="URL"
required
type="url"
:rules="urlRules"
></v-text-field>
/>
<!-- <v-textarea-->
<!-- v-model="hook.header"-->
@@ -131,35 +140,33 @@
</v-container>
</v-card>
</v-form>
</div>
</template>
<script>
export default {
name: "webhooks",
name: 'Webhooks',
props: ['nodes'],
data: () => ({
valid: false,
loading: false,
eventList: [
{text: ["Before", "Insert"], value: ['before', 'insert']},
{text: ["After", "Insert"], value: ['after', 'insert']},
{text: ["Before", "Update"], value: ['before', 'update']},
{text: ["After", "Update"], value: ['after', 'update']},
{text: ["Before", "Delete"], value: ['before', 'delete']},
{text: ["After", "Delete"], value: ['after', 'delete']},
{ text: ['Before', 'Insert'], value: ['before', 'insert'] },
{ text: ['After', 'Insert'], value: ['after', 'insert'] },
{ text: ['Before', 'Update'], value: ['before', 'update'] },
{ text: ['After', 'Update'], value: ['after', 'update'] },
{ text: ['Before', 'Delete'], value: ['before', 'delete'] },
{ text: ['After', 'Delete'], value: ['after', 'delete'] }
],
hook: {},
urlRules: [
v => !v || !v.trim() || /^https?:\/\/.{1,}/.test(v) || 'Not a valid URL',
v => !v || !v.trim() || /^https?:\/\/.{1,}/.test(v) || 'Not a valid URL'
]
}),
methods: {
async onEventChange() {
this.loading = true;
async onEventChange () {
this.loading = true
this.hook = {
...this.hook,
url: '',
@@ -169,16 +176,16 @@ export default {
const result = (await this.$store.dispatch('sqlMgr/ActSqlOp', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
dbAlias: this.nodes.dbAlias
}, 'tableXcHooksGet', {
tn: this.nodes.tn,
data: {
event: this.hook.event[0],
operation: this.hook.event[1],
operation: this.hook.event[1]
}
}
]));
const hooksDetails = result && result.data.list && result.data.list[0];
]))
const hooksDetails = result && result.data.list && result.data.list[0]
if (hooksDetails) {
this.hook = {
@@ -187,28 +194,28 @@ export default {
title: hooksDetails.title
}
}
this.loading = false;
this.loading = false
},
async saveHooks() {
async saveHooks () {
if (!this.valid || !this.hook.event) {
return;
return
}
this.loading = true;
let hooksDetails = (await this.$store.dispatch('sqlMgr/ActSqlOp', [
this.loading = true
await this.$store.dispatch('sqlMgr/ActSqlOp', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
dbAlias: this.nodes.dbAlias
}, 'tableXcHooksSet', {
tn: this.nodes.tn,
data: {
...this.hook,
event: this.hook.event[0],
operation: this.hook.event[1],
operation: this.hook.event[1]
}
}
])).data;
this.$toast.success('Webhook details updated successfully').goAway(3000);
this.loading = false;
])
this.$toast.success('Webhook details updated successfully').goAway(3000)
this.loading = false
}
}
}

View File

@@ -1,47 +1,59 @@
<template>
<div>
<v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes._tn + ' (Webhooks)',
disabled: true,
href: '#'
}]" divider=">" small>
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
<v-breadcrumbs
:items="[{
text: nodes.env,
disabled: true,
href: '#'
},{
text: nodes.dbAlias,
disabled: true,
href: '#'
},
{
text: nodes._tn + ' (Webhooks)',
disabled: true,
href: '#'
}]"
divider=">"
small
>
<template #divider>
<v-icon small color="grey lighten-2">
forward
</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<x-btn outlined tooltip="Reload hooks"
color="primary"
small
v-ge="['hooks','reload']"
@click.prevent="loadHooksList">
<v-icon small left>mdi-reload</v-icon>
<x-btn
v-ge="['hooks','reload']"
outlined
tooltip="Reload hooks"
color="primary"
small
@click.prevent="loadHooksList"
>
<v-icon small left>
mdi-reload
</v-icon>
Reload
</x-btn>
<x-btn outlined tooltip="Save Changes"
color="primary"
small
v-ge="['hooks','add new']"
@click.prevent="addNewHook">
<v-icon small left>mdi-plus</v-icon>
<x-btn
v-ge="['hooks','add new']"
outlined
tooltip="Save Changes"
color="primary"
small
@click.prevent="addNewHook"
>
<v-icon small left>
mdi-plus
</v-icon>
Add New
</x-btn>
@@ -55,163 +67,171 @@
<v-icon small left>save</v-icon>
Save
</x-btn>-->
</v-toolbar>
<v-form
ref="form"
v-model="valid"
class="mx-auto"
ref="form"
lazy-validation
>
<v-card>
<v-container fluid>
<v-row>
<v-col cols="7">
<v-radio-group v-model="selectedHook" @change="onEventChange" v-slot:default>
<v-simple-table dense v-slot:default>
<thead>
<tr>
<th>
</th>
<th>Title</th>
<th>Event</th>
<th>Condition</th>
<th>Notify Via</th>
<th>Action</th>
</tr>
</thead>
<v-radio-group v-model="selectedHook" @change="onEventChange">
<template #default>
<v-simple-table dense>
<template #default>
<thead>
<tr>
<th />
<th>Title</th>
<th>Event</th>
<th>Condition</th>
<th>Notify Via</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<template v-if="hooks && hooks.length">
<tr v-for="(item,i) in hooks">
<td>
<v-radio :value="i"></v-radio>
</td>
<td>{{ item.title }}</td>
<td>{{ item.event }} {{ item.operation }}</td>
<td>
<v-icon v-if="item.condition" color="success" small>mdi-check-bold</v-icon>
</td>
<td>{{ item.notification && item.notification.type }}</td>
<td>
<x-icon small color="error" @click.stop="deleteHook(item, i)">mdi-delete</x-icon>
<!-- <x-icon small :color="loading || !valid || !hook.event ? 'grey' : 'primary'"
@click.stop="(!loading && valid && hook.event) && saveHooks()">save
</x-icon>-->
</td>
</tr>
</template>
<tr v-else>
<td colspan="6" class="text-center py-5">
<x-btn outlined tooltip="Save Changes"
color="primary"
small
v-ge="['hooks','add new']"
@click.prevent="addNewHook">
<v-icon small left>mdi-plus</v-icon>
Add New Webhook
</x-btn>
</td>
</tr>
</tbody>
</v-simple-table>
<tbody>
<template v-if="hooks && hooks.length">
<tr v-for="(item,i) in hooks" :key="i">
<td>
<v-radio :value="i" />
</td>
<td>{{ item.title }}</td>
<td>{{ item.event }} {{ item.operation }}</td>
<td>
<v-icon v-if="item.condition" color="success" small>
mdi-check-bold
</v-icon>
</td>
<td>{{ item.notification && item.notification.type }}</td>
<td>
<x-icon small color="error" @click.stop="deleteHook(item, i)">
mdi-delete
</x-icon>
<!-- <x-icon small :color="loading || !valid || !hook.event ? 'grey' : 'primary'"
@click.stop="(!loading && valid && hook.event) && saveHooks()">save
</x-icon>-->
</td>
</tr>
</template>
<tr v-else>
<td colspan="6" class="text-center py-5">
<x-btn
v-ge="['hooks','add new']"
outlined
tooltip="Save Changes"
color="primary"
small
@click.prevent="addNewHook"
>
<v-icon small left>
mdi-plus
</v-icon>
Add New Webhook
</x-btn>
</td>
</tr>
</tbody>
</template>
</v-simple-table>
</template>
</v-radio-group>
</v-col>
<v-col cols="5">
<v-card class="" v-if="hook">
<v-card v-if="hook" class="">
<v-card-title>
Webhook
<v-spacer></v-spacer>
<x-btn outlined tooltip="Save"
color="primary"
small
:disabled="loading || !valid || !hook.event"
<v-spacer />
<x-btn
v-ge="['hooks','save']"
outlined
tooltip="Save"
color="primary"
small
v-ge="['hooks','save']"
@click.prevent="saveHooks">
<v-icon small left>save</v-icon>
:disabled="loading || !valid || !hook.event"
@click.prevent="saveHooks"
>
<v-icon small left>
save
</v-icon>
Save
</x-btn>
</v-card-title>
<v-card-text>
<v-text-field
v-model="hook.title"
class="caption"
outlined
dense
v-model="hook.title"
label="Title"
required
:rules="[v => !!v || 'Title Required']"
></v-text-field>
<webhook-event :event.sync="hook.event"
:operation.sync="hook.operation"
></webhook-event>
/>
<webhook-event
:event.sync="hook.event"
:operation.sync="hook.operation"
/>
<v-card class="mb-8">
<v-card-text>
<v-checkbox
v-model="enableCondition"
dense
@change="checkConditionAvail"
hide-details
class="mt-1"
label="On Condition"
v-model="enableCondition"
></v-checkbox>
@change="checkConditionAvail"
/>
<column-filter v-if="enableCondition && _isEE"
:field-list="fieldList"
v-model="hook.condition"
dense style="max-width: 100%">
</column-filter>
<column-filter
v-if="enableCondition && _isEE"
v-model="hook.condition"
:field-list="fieldList"
dense
style="max-width: 100%"
/>
</v-card-text>
</v-card>
<v-select
v-model="hook.notification.type"
outlined
dense
v-model="hook.notification.type"
label="Notification"
required
:items="notificationList"
:rules="[v => !!v || 'Title Required']"
class="caption"
@change="onNotTypeChange"
:prepend-inner-icon="notificationIcon[hook.notification.type]"
@change="onNotTypeChange"
>
<template v-slot:item="{item}">
<template #item="{item}">
<v-list-item-icon>
<v-icon small>{{ notificationIcon[item] }}</v-icon>
<v-icon small>
{{ notificationIcon[item] }}
</v-icon>
</v-list-item-icon>
<v-list-item-title>
{{ item }}
</v-list-item-title>
</template>
</v-select>
<template v-if="hook.notification.type === 'URL'">
<http-webhook v-model="notification"></http-webhook>
<http-webhook v-model="notification" />
</template>
<template v-if="hook.notification.type === 'Slack'">
<v-combobox
v-if="slackChannels"
:rules="[v => !!v || 'Required']"
v-model="notification.channels"
:rules="[v => !!v || 'Required']"
:items="slackChannels"
item-text="channel"
label="Select Slack channels"
@@ -219,14 +239,13 @@
outlined
dense
class="caption"
>
</v-combobox>
/>
</template>
<template v-if="hook.notification.type === 'Microsoft Teams'">
<v-combobox
v-if="teamsChannels"
:rules="[v => !!v || 'Required']"
v-model="notification.channels"
:rules="[v => !!v || 'Required']"
:items="teamsChannels"
item-text="channel"
label="Select Teams channels"
@@ -234,14 +253,13 @@
outlined
dense
class="caption"
>
</v-combobox>
/>
</template>
<template v-if="hook.notification.type === 'Discord'">
<v-combobox
v-if="discordChannels"
:rules="[v => !!v || 'Required']"
v-model="notification.channels"
:rules="[v => !!v || 'Required']"
:items="discordChannels"
item-text="channel"
label="Select Discord channels"
@@ -249,14 +267,13 @@
outlined
dense
class="caption"
>
</v-combobox>
/>
</template>
<template v-if="hook.notification.type === 'Mattermost'">
<v-combobox
v-if="mattermostChannels"
:rules="[v => !!v || 'Required']"
v-model="notification.channels"
:rules="[v => !!v || 'Required']"
:items="mattermostChannels"
item-text="channel"
label="Select Mattermost channels"
@@ -264,73 +281,80 @@
outlined
dense
class="caption"
>
</v-combobox>
/>
</template>
<template v-if="inputs[hook.notification.type] && notification">
<template v-for="input in inputs[hook.notification.type]">
<v-textarea class="caption" :key="input.key"
dense outlined v-if="input.type === 'LongText'"
:label="input.label"
v-model="notification[input.key]"
:rules="[v => !input.required || !!v || 'Required']"
></v-textarea>
<v-text-field class="caption" :key="input.key"
dense outlined v-else :label="input.label"
v-model="notification[input.key]"
:rules="[v => !input.required || !!v || 'Required']"
></v-text-field>
<v-textarea
v-if="input.type === 'LongText'"
:key="input.key"
v-model="notification[input.key]"
class="caption"
dense
outlined
:label="input.label"
:rules="[v => !input.required || !!v || 'Required']"
/>
<v-text-field
v-else
:key="input.key"
v-model="notification[input.key]"
class="caption"
dense
outlined
:label="input.label"
:rules="[v => !input.required || !!v || 'Required']"
/>
</template>
</template>
</v-card-text>
<v-card-text>
<span class="caption grey--text">
<em>Available context variables are <strong>data, user, payload and env</strong></em>
<v-tooltip top>
<template #activator="{on}">
<v-icon small
v-on="on"
color="grey"
class="ml-2">mdi-information</v-icon>
</template>
<span class="caption">
<strong>data</strong> : Row data <br>
<strong>user</strong> : User information<br>
<strong>payload</strong> : Plugin settings payload<br>
<strong>env</strong> : Environment values (process.env)
<v-icon
small
color="grey"
class="ml-2"
v-on="on"
>mdi-information</v-icon>
</template>
<span class="caption">
<strong>data</strong> : Row data <br>
<strong>user</strong> : User information<br>
<strong>payload</strong> : Plugin settings payload<br>
<strong>env</strong> : Environment values (process.env)
</span>
</span>
</v-tooltip>
</span>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
</v-card>
</v-form>
</div>
</template>
<script>
import ColumnFilter from "~/components/project/spreadsheet/components/columnFilter";
import FormInput from "~/components/project/appStore/FormInput";
import WebhookEvent from "~/components/project/tableTabs/webhookEvent";
import HttpWebhook from "./webhook/httpWebhook";
import HttpWebhook from './webhook/httpWebhook'
import ColumnFilter from '~/components/project/spreadsheet/components/columnFilter'
// import FormInput from '~/components/project/appStore/FormInput'
import WebhookEvent from '~/components/project/tableTabs/webhookEvent'
export default {
name: "webhooks",
components: {HttpWebhook, WebhookEvent, FormInput, ColumnFilter},
name: 'Webhooks',
components: {
HttpWebhook,
WebhookEvent,
// FormInput,
ColumnFilter
},
props: ['nodes'],
data: () => ({
slackChannels: null,
@@ -350,23 +374,23 @@ export default {
'Mattermost',
'Twilio',
'Whatsapp Twilio',
'URL',
'URL'
],
filters: [],
hook: null,
notification: {},
notificationIcon: {
'URL': 'mdi-link',
'Email': 'mdi-email',
'Slack': 'mdi-slack',
URL: 'mdi-link',
Email: 'mdi-email',
Slack: 'mdi-slack',
'Microsoft Teams': 'mdi-microsoft-teams',
'Discord': 'mdi-discord',
'Mattermost': 'mdi-chat',
Discord: 'mdi-discord',
Mattermost: 'mdi-chat',
'Whatsapp Twilio': 'mdi-whatsapp',
'Twilio': 'mdi-cellphone-message',
Twilio: 'mdi-cellphone-message'
},
urlRules: [
v => !v || !v.trim() || /^https?:\/\/.{1,}/.test(v) || 'Not a valid URL',
v => !v || !v.trim() || /^https?:\/\/.{1,}/.test(v) || 'Not a valid URL'
],
fieldList: [],
inputs: {
@@ -391,35 +415,40 @@ export default {
type: 'LongText',
required: true
}
], Slack: [{
],
Slack: [{
key: 'body',
label: 'Body',
placeholder: 'Body',
type: 'LongText',
required: true
}
], 'Microsoft Teams': [{
],
'Microsoft Teams': [{
key: 'body',
label: 'Body',
placeholder: 'Body',
type: 'LongText',
required: true
}
], Discord: [{
],
Discord: [{
key: 'body',
label: 'Body',
placeholder: 'Body',
type: 'LongText',
required: true
}
], Mattermost: [{
],
Mattermost: [{
key: 'body',
label: 'Body',
placeholder: 'Body',
type: 'LongText',
required: true
}
], 'Twilio': [{
],
Twilio: [{
key: 'body',
label: 'Body',
placeholder: 'Body',
@@ -445,86 +474,95 @@ export default {
type: 'LongText',
required: true
}]
},
}
}),
async created () {
await this.loadMeta()
await this.loadHooksList()
this.selectedHook = 0
this.onEventChange()
},
methods: {
checkConditionAvail() {
checkConditionAvail () {
if (!process.env.EE) {
this.enableCondition = false;
this.enableCondition = false
this.$toast.info('For webhook condition : Upgrade to Enterprise Edition').goAway(3000)
}
this.hook.condition = []
},
async onNotTypeChange() {
this.notification = {};
async onNotTypeChange () {
this.notification = {}
if (this.hook.notification.type === 'Slack') {
const plugin = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginRead', {
title: 'Slack'
}]);
this.slackChannels = JSON.parse(plugin.input) || [];
}])
this.slackChannels = JSON.parse(plugin.input) || []
}
if (this.hook.notification.type === 'Microsoft Teams') {
const plugin = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginRead', {
title: 'Microsoft Teams'
}]);
this.teamsChannels = JSON.parse(plugin.input) || [];
}])
this.teamsChannels = JSON.parse(plugin.input) || []
}
if (this.hook.notification.type === 'Discord') {
const plugin = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginRead', {
title: 'Discord'
}]);
this.discordChannels = JSON.parse(plugin.input) || [];
}])
this.discordChannels = JSON.parse(plugin.input) || []
}
if (this.hook.notification.type === 'Mattermost') {
const plugin = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginRead', {
title: 'Mattermost'
}]);
this.mattermostChannels = JSON.parse(plugin.input) || [];
}])
this.mattermostChannels = JSON.parse(plugin.input) || []
}
},
async onEventChange() {
if (!this.hooks || !this.hooks.length) return
const {notification: {payload, type}, ...hook} = this.hooks[this.selectedHook];
async onEventChange () {
if (!this.hooks || !this.hooks.length) {
return
}
const { notification: { payload, type }, ...hook } = this.hooks[this.selectedHook]
this.hook = {
...hook,
notification: {
type
}
};
}
this.enableCondition = !!this.hook.condition
await this.onNotTypeChange();
this.notification = payload;
await this.onNotTypeChange()
this.notification = payload
if (this.hook.notification.type === 'Slack') {
this.notification.webhook_url = this.notification.webhook_url
&& this.notification.webhook_url.map(v => this.slackChannels.find(s => v.webhook_url === s.webhook_url))
this.notification.webhook_url = this.notification.webhook_url &&
this.notification.webhook_url.map(v => this.slackChannels.find(s => v.webhook_url === s.webhook_url))
}
if (this.hook.notification.type === 'Microsoft Teams') {
this.notification.webhook_url = this.notification.webhook_url
&& this.notification.webhook_url.map(v => this.teamsChannels.find(s => v.webhook_url === s.webhook_url))
this.notification.webhook_url = this.notification.webhook_url &&
this.notification.webhook_url.map(v => this.teamsChannels.find(s => v.webhook_url === s.webhook_url))
}
if (this.hook.notification.type === 'Discord') {
this.notification.webhook_url = this.notification.webhook_url
&& this.notification.webhook_url.map(v => this.discordChannels.find(s => v.webhook_url === s.webhook_url))
this.notification.webhook_url = this.notification.webhook_url &&
this.notification.webhook_url.map(v => this.discordChannels.find(s => v.webhook_url === s.webhook_url))
}
if (this.hook.notification.type === 'Mattermost') {
this.notification.webhook_url = this.notification.webhook_url
&& this.notification.webhook_url.map(v => this.mattermostChannels.find(s => v.webhook_url === s.webhook_url))
this.notification.webhook_url = this.notification.webhook_url &&
this.notification.webhook_url.map(v => this.mattermostChannels.find(s => v.webhook_url === s.webhook_url))
}
if (this.hook.notification.type === 'URL') {
// eslint-disable-next-line no-self-assign
this.notification.api = this.notification.api
}
},
async saveHooks() {
async saveHooks () {
if (!this.$refs.form.validate() || !this.valid || !this.hook.event) {
return;
return
}
this.loading = true;
this.loading = true
try {
const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [
{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
dbAlias: this.nodes.dbAlias
}, 'tableXcHooksSet', {
tn: this.nodes.tn,
data: {
@@ -535,58 +573,58 @@ export default {
}
}
}
]);
])
if (!this.hook.id && res) {
this.hook.id = Array.isArray(res) ? res[0] : res;
this.hook.id = Array.isArray(res) ? res[0] : res
}
this.$toast.success('Webhook details updated successfully').goAway(3000);
this.$toast.success('Webhook details updated successfully').goAway(3000)
} catch (e) {
this.$toast.error(e.message).goAway(3000);
this.$toast.error(e.message).goAway(3000)
}
this.loading = false;
await this.loadHooksList();
this.loading = false
await this.loadHooksList()
},
async loadMeta() {
this.loadingMeta = true;
async loadMeta () {
this.loadingMeta = true
const tableMeta = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'tableXcModelGet', {
tn: this.nodes.tn
}]);
this.meta = JSON.parse(tableMeta.meta);
this.fieldList = this.meta.columns.map(c => c.cn);
this.loadingMeta = false;
}])
this.meta = JSON.parse(tableMeta.meta)
this.fieldList = this.meta.columns.map(c => c.cn)
this.loadingMeta = false
},
async loadHooksList() {
this.loading = true;
async loadHooksList () {
this.loading = true
const hooks = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
env: this.nodes.env,
dbAlias: this.nodes.dbAlias
}, 'tableXcHooksList', {
tn: this.nodes.tn
}]);
this.hooks = hooks.data.list.map(h => {
h.notification = h.notification && JSON.parse(h.notification);
h.condition = h.condition && JSON.parse(h.condition);
}])
this.hooks = hooks.data.list.map((h) => {
h.notification = h.notification && JSON.parse(h.notification)
h.condition = h.condition && JSON.parse(h.condition)
return h;
});
this.loading = false;
return h
})
this.loading = false
},
addNewHook() {
this.selectedHook = this.hooks.length;
addNewHook () {
this.selectedHook = this.hooks.length
this.hooks.push({
notification: {
// type:'Email'
}
});
this.onEventChange();
})
this.onEventChange()
this.$refs.form.resetValidation()
},
async deleteHook(item, i) {
async deleteHook (item, i) {
try {
if (item.id) {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
@@ -596,25 +634,19 @@ export default {
id: item.id,
title: item.title,
tn: this.nodes.tn
}]);
this.hooks.splice(i, 1);
}])
this.hooks.splice(i, 1)
} else {
this.hooks.splice(i, 1);
this.hooks.splice(i, 1)
}
this.$toast.success('Hook deleted successfully').goAway(3000);
this.$toast.success('Hook deleted successfully').goAway(3000)
if (!this.hooks.length) {
this.hook = null;
this.hook = null
}
} catch (e) {
this.$toast.error(e.message).goAway(3000);
this.$toast.error(e.message).goAway(3000)
}
}
},
async created() {
await this.loadMeta();
await this.loadHooksList();
this.selectedHook = 0;
this.onEventChange();
}
}
</script>