mirror of
https://github.com/Fabio286/antares.git
synced 2025-02-12 01:30:46 +01:00
Merge pull request #890 from antares-sql/feat/mysql-check-support
Feat: MySQL check support
This commit is contained in:
commit
ccbcffc7f0
@ -55,6 +55,7 @@ export const defaults: Customizations = {
|
||||
tableArray: false,
|
||||
tableRealCount: false,
|
||||
tableDuplicate: false,
|
||||
tableCheck: false,
|
||||
viewSettings: false,
|
||||
triggerSettings: false,
|
||||
triggerFunctionSettings: false,
|
||||
|
@ -47,6 +47,7 @@ export const customizations: Customizations = {
|
||||
tableTruncateDisableFKCheck: true,
|
||||
tableDuplicate: true,
|
||||
tableDdl: true,
|
||||
tableCheck: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
routineAdd: true,
|
||||
|
@ -159,6 +159,13 @@ export interface TableForeign {
|
||||
oldName?: string;
|
||||
}
|
||||
|
||||
export interface TableCheck {
|
||||
// eslint-disable-next-line camelcase
|
||||
_antares_id?: string;
|
||||
name: string;
|
||||
clause: string;
|
||||
}
|
||||
|
||||
export interface CreateTableParams {
|
||||
/** Connection UID */
|
||||
uid?: string;
|
||||
@ -166,6 +173,7 @@ export interface CreateTableParams {
|
||||
fields: TableField[];
|
||||
foreigns: TableForeign[];
|
||||
indexes: TableIndex[];
|
||||
checks: TableCheck[];
|
||||
options: TableOptions;
|
||||
}
|
||||
|
||||
@ -193,6 +201,11 @@ export interface AlterTableParams {
|
||||
changes: TableForeign[];
|
||||
deletions: TableForeign[];
|
||||
};
|
||||
checkChanges: {
|
||||
additions: TableCheck[];
|
||||
changes: TableCheck[];
|
||||
deletions: TableCheck[];
|
||||
};
|
||||
options: TableOptions;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ export interface Customizations {
|
||||
tableArray?: boolean;
|
||||
tableRealCount?: boolean;
|
||||
tableTruncateDisableFKCheck?: boolean;
|
||||
tableCheck?: boolean;
|
||||
tableDdl?: boolean;
|
||||
viewAdd?: boolean;
|
||||
viewSettings?: boolean;
|
||||
|
@ -87,6 +87,19 @@ export default (connections: Record<string, antares.Client>) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-table-checks', async (event, params) => {
|
||||
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
|
||||
|
||||
try {
|
||||
const result = await connections[params.uid].getTableChecks(params);
|
||||
|
||||
return { status: 'success', response: result };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-table-ddl', async (event, params) => {
|
||||
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
|
||||
|
||||
|
@ -189,6 +189,10 @@ export abstract class BaseClient {
|
||||
throw new Error('Method "dropSchema" not implemented');
|
||||
}
|
||||
|
||||
getTableChecks (...args: any) {
|
||||
throw new Error('Method "getTableDll" not implemented');
|
||||
}
|
||||
|
||||
getTableDll (...args: any) {
|
||||
throw new Error('Method "getTableDll" not implemented');
|
||||
}
|
||||
|
@ -161,6 +161,8 @@ export class MySQLClient extends BaseClient {
|
||||
|
||||
this._ssh = new SSH2Promise({
|
||||
...this._params.ssh,
|
||||
reconnect: true,
|
||||
reconnectTries: 3,
|
||||
debug: process.env.NODE_ENV !== 'production' ? (s) => console.log(s) : null
|
||||
});
|
||||
|
||||
@ -689,6 +691,34 @@ export class MySQLClient extends BaseClient {
|
||||
return rows.length ? rows[0].count : 0;
|
||||
}
|
||||
|
||||
async getTableChecks ({ schema, table }: { schema: string; table: string }): Promise<antares.TableCheck[]> {
|
||||
const { rows } = await this.raw(`
|
||||
SELECT
|
||||
CONSTRAINT_NAME as name,
|
||||
CHECK_CLAUSE as clausole
|
||||
FROM information_schema.CHECK_CONSTRAINTS
|
||||
WHERE CONSTRAINT_SCHEMA = "${schema}"
|
||||
AND CONSTRAINT_NAME IN (
|
||||
SELECT
|
||||
CONSTRAINT_NAME
|
||||
FROM
|
||||
information_schema.TABLE_CONSTRAINTS
|
||||
WHERE
|
||||
TABLE_SCHEMA = "${schema}"
|
||||
AND TABLE_NAME = "${table}"
|
||||
AND CONSTRAINT_TYPE = 'CHECK'
|
||||
)
|
||||
`);
|
||||
|
||||
if (rows.length) {
|
||||
return rows.map(row => ({
|
||||
name: row.name,
|
||||
clause: row.clausole
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
async getTableOptions ({ schema, table }: { schema: string; table: string }) {
|
||||
/* eslint-disable camelcase */
|
||||
interface TableOptionsResult {
|
||||
@ -865,11 +895,13 @@ export class MySQLClient extends BaseClient {
|
||||
fields,
|
||||
foreigns,
|
||||
indexes,
|
||||
checks,
|
||||
options
|
||||
} = params;
|
||||
const newColumns: string[] = [];
|
||||
const newIndexes: string[] = [];
|
||||
const newForeigns: string[] = [];
|
||||
const newChecks: string[] = [];
|
||||
|
||||
let sql = `CREATE TABLE \`${schema}\`.\`${options.name}\``;
|
||||
|
||||
@ -910,7 +942,13 @@ export class MySQLClient extends BaseClient {
|
||||
newForeigns.push(`CONSTRAINT \`${foreign.constraintName}\` FOREIGN KEY (\`${foreign.field}\`) REFERENCES \`${foreign.refTable}\` (\`${foreign.refField}\`) ON UPDATE ${foreign.onUpdate} ON DELETE ${foreign.onDelete}`);
|
||||
});
|
||||
|
||||
sql = `${sql} (${[...newColumns, ...newIndexes, ...newForeigns].join(', ')}) COMMENT='${options.comment}', COLLATE='${options.collation}', ENGINE=${options.engine}`;
|
||||
// ADD TABLE CHECKS
|
||||
checks.forEach(check => {
|
||||
if (!check.clause.trim().length) return;
|
||||
newChecks.push(`${check.name ? `CONSTRAINT \`${check.name}\` ` : ''}CHECK (${check.clause})`);
|
||||
});
|
||||
|
||||
sql = `${sql} (${[...newColumns, ...newIndexes, ...newForeigns, ...newChecks].join(', ')}) COMMENT='${options.comment}', COLLATE='${options.collation}', ENGINE=${options.engine}`;
|
||||
|
||||
return await this.raw(sql);
|
||||
}
|
||||
@ -924,6 +962,7 @@ export class MySQLClient extends BaseClient {
|
||||
changes,
|
||||
indexChanges,
|
||||
foreignChanges,
|
||||
checkChanges,
|
||||
options
|
||||
} = params;
|
||||
|
||||
@ -931,6 +970,7 @@ export class MySQLClient extends BaseClient {
|
||||
const alterColumnsAdd: string[] = [];
|
||||
const alterColumnsChange: string[] = [];
|
||||
const alterColumnsDrop: string[] = [];
|
||||
const alterQueryes: string[] = [];
|
||||
|
||||
// OPTIONS
|
||||
if ('comment' in options) alterColumnsChange.push(`COMMENT='${options.comment}'`);
|
||||
@ -976,6 +1016,12 @@ export class MySQLClient extends BaseClient {
|
||||
alterColumnsAdd.push(`ADD CONSTRAINT \`${addition.constraintName}\` FOREIGN KEY (\`${addition.field}\`) REFERENCES \`${addition.refTable}\` (\`${addition.refField}\`) ON UPDATE ${addition.onUpdate} ON DELETE ${addition.onDelete}`);
|
||||
});
|
||||
|
||||
// ADD TABLE CHECKS
|
||||
checkChanges.additions.forEach(addition => {
|
||||
if (!addition.clause.trim().length) return;
|
||||
alterColumnsAdd.push(`ADD ${addition.name ? `CONSTRAINT \`${addition.name}\` ` : ''}CHECK (${addition.clause})`);
|
||||
});
|
||||
|
||||
// CHANGE FIELDS
|
||||
changes.forEach(change => {
|
||||
const typeInfo = this.getTypeInfo(change.type);
|
||||
@ -987,9 +1033,9 @@ export class MySQLClient extends BaseClient {
|
||||
${change.zerofill ? 'ZEROFILL' : ''}
|
||||
${change.nullable ? 'NULL' : 'NOT NULL'}
|
||||
${change.autoIncrement ? 'AUTO_INCREMENT' : ''}
|
||||
${change.collation ? `COLLATE ${change.collation}` : ''}
|
||||
${change.default !== null ? `DEFAULT ${change.default || '\'\''}` : ''}
|
||||
${change.comment ? `COMMENT '${change.comment}'` : ''}
|
||||
${change.collation ? `COLLATE ${change.collation}` : ''}
|
||||
${change.onUpdate ? `ON UPDATE ${change.onUpdate}` : ''}
|
||||
${change.after ? `AFTER \`${change.after}\`` : 'FIRST'}`);
|
||||
});
|
||||
@ -1020,6 +1066,13 @@ export class MySQLClient extends BaseClient {
|
||||
alterColumnsChange.push(`ADD CONSTRAINT \`${change.constraintName}\` FOREIGN KEY (\`${change.field}\`) REFERENCES \`${change.refTable}\` (\`${change.refField}\`) ON UPDATE ${change.onUpdate} ON DELETE ${change.onDelete}`);
|
||||
});
|
||||
|
||||
// CHANGE CHECK TABLE
|
||||
checkChanges.changes.forEach(change => {
|
||||
if (!change.clause.trim().length) return;
|
||||
alterQueryes.push(`${sql} DROP CONSTRAINT \`${change.name}\``);
|
||||
alterQueryes.push(`${sql} ADD ${change.name ? `CONSTRAINT \`${change.name}\` ` : ''}CHECK (${change.clause})`);
|
||||
});
|
||||
|
||||
// DROP FIELDS
|
||||
deletions.forEach(deletion => {
|
||||
alterColumnsDrop.push(`DROP COLUMN \`${deletion.name}\``);
|
||||
@ -1038,7 +1091,11 @@ export class MySQLClient extends BaseClient {
|
||||
alterColumnsDrop.push(`DROP FOREIGN KEY \`${deletion.constraintName}\``);
|
||||
});
|
||||
|
||||
const alterQueryes = [];
|
||||
// DROP CHECK TABLE
|
||||
checkChanges.deletions.forEach(deletion => {
|
||||
alterQueryes.push(`${sql} DROP CONSTRAINT \`${deletion.name}\``);
|
||||
});
|
||||
|
||||
if (alterColumnsAdd.length) alterQueryes.push(sql+alterColumnsAdd.join(', '));
|
||||
if (alterColumnsChange.length) alterQueryes.push(sql+alterColumnsChange.join(', '));
|
||||
if (alterColumnsDrop.length) alterQueryes.push(sql+alterColumnsDrop.join(', '));
|
||||
|
@ -168,6 +168,8 @@ export class PostgreSQLClient extends BaseClient {
|
||||
try {
|
||||
this._ssh = new SSH2Promise({
|
||||
...this._params.ssh,
|
||||
reconnect: true,
|
||||
reconnectTries: 3,
|
||||
debug: process.env.NODE_ENV !== 'production' ? (s) => console.log(s) : null
|
||||
});
|
||||
|
||||
|
@ -72,6 +72,19 @@
|
||||
/>
|
||||
<span>{{ t('database.foreignKeys') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-dark btn-sm ml-2 mr-0"
|
||||
:disabled="isSaving || !localFields.length"
|
||||
:title="t('database.manageTableChecks')"
|
||||
@click="showTableChecksModal"
|
||||
>
|
||||
<BaseIcon
|
||||
class="mr-1"
|
||||
icon-name="mdiTableCheck"
|
||||
:size="24"
|
||||
/>
|
||||
<span>{{ t('database.tableChecks') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="workspace-query-info">
|
||||
<div class="d-flex" :title="t('database.schema')">
|
||||
@ -183,11 +196,19 @@
|
||||
@hide="hideForeignModal"
|
||||
@foreigns-update="foreignsUpdate"
|
||||
/>
|
||||
<WorkspaceTabPropsTableChecksModal
|
||||
v-if="isTableChecksModal"
|
||||
:local-checks="localTableChecks"
|
||||
table="new"
|
||||
:workspace="workspace"
|
||||
@hide="hideTableChecksModal"
|
||||
@checks-update="checksUpdate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ConnectionParams, TableField, TableForeign, TableIndex, TableOptions } from 'common/interfaces/antares';
|
||||
import { ConnectionParams, TableCheck, TableField, TableForeign, TableIndex, TableOptions } from 'common/interfaces/antares';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@ -198,6 +219,7 @@ import BaseIcon from '@/components/BaseIcon.vue';
|
||||
import BaseLoader from '@/components/BaseLoader.vue';
|
||||
import BaseSelect from '@/components/BaseSelect.vue';
|
||||
import WorkspaceTabNewTableEmptyState from '@/components/WorkspaceTabNewTableEmptyState.vue';
|
||||
import WorkspaceTabPropsTableChecksModal from '@/components/WorkspaceTabPropsTableChecksModal.vue';
|
||||
import WorkspaceTabPropsTableFields from '@/components/WorkspaceTabPropsTableFields.vue';
|
||||
import WorkspaceTabPropsTableForeignModal from '@/components/WorkspaceTabPropsTableForeignModal.vue';
|
||||
import WorkspaceTabPropsTableIndexesModal from '@/components/WorkspaceTabPropsTableIndexesModal.vue';
|
||||
@ -236,12 +258,16 @@ const isLoading = ref(false);
|
||||
const isSaving = ref(false);
|
||||
const isIndexesModal = ref(false);
|
||||
const isForeignModal = ref(false);
|
||||
const isTableChecksModal = ref(false);
|
||||
|
||||
const originalFields: Ref<TableField[]> = ref([]);
|
||||
const localFields: Ref<TableField[]> = ref([]);
|
||||
const originalKeyUsage: Ref<TableForeign[]> = ref([]);
|
||||
const localKeyUsage: Ref<TableForeign[]> = ref([]);
|
||||
const originalIndexes: Ref<TableIndex[]> = ref([]);
|
||||
const localIndexes: Ref<TableIndex[]> = ref([]);
|
||||
const originalTableChecks: Ref<TableCheck[]> = ref([]);
|
||||
const localTableChecks: Ref<TableCheck[]> = ref([]);
|
||||
const tableOptions: Ref<TableOptions> = ref(null);
|
||||
const localOptions: Ref<TableOptions> = ref(null);
|
||||
const newFieldsCounter = ref(0);
|
||||
@ -274,6 +300,7 @@ const isChanged = computed(() => {
|
||||
return JSON.stringify(originalFields.value) !== JSON.stringify(localFields.value) ||
|
||||
JSON.stringify(originalKeyUsage.value) !== JSON.stringify(localKeyUsage.value) ||
|
||||
JSON.stringify(originalIndexes.value) !== JSON.stringify(localIndexes.value) ||
|
||||
JSON.stringify(originalTableChecks.value) !== JSON.stringify(localTableChecks.value) ||
|
||||
JSON.stringify(tableOptions.value) !== JSON.stringify(localOptions.value);
|
||||
});
|
||||
|
||||
@ -291,6 +318,7 @@ const saveChanges = async () => {
|
||||
fields: localFields.value,
|
||||
foreigns: localKeyUsage.value,
|
||||
indexes: localIndexes.value,
|
||||
checks: localTableChecks.value,
|
||||
options: localOptions.value
|
||||
};
|
||||
|
||||
@ -326,6 +354,7 @@ const clearChanges = () => {
|
||||
localFields.value = JSON.parse(JSON.stringify(originalFields.value));
|
||||
localIndexes.value = JSON.parse(JSON.stringify(originalIndexes.value));
|
||||
localKeyUsage.value = JSON.parse(JSON.stringify(originalKeyUsage.value));
|
||||
localTableChecks.value = JSON.parse(JSON.stringify(originalTableChecks.value));
|
||||
|
||||
tableOptions.value = {
|
||||
name: '',
|
||||
@ -446,10 +475,22 @@ const hideForeignModal = () => {
|
||||
isForeignModal.value = false;
|
||||
};
|
||||
|
||||
const showTableChecksModal = () => {
|
||||
isTableChecksModal.value = true;
|
||||
};
|
||||
|
||||
const hideTableChecksModal = () => {
|
||||
isTableChecksModal.value = false;
|
||||
};
|
||||
|
||||
const foreignsUpdate = (foreigns: TableForeign[]) => {
|
||||
localKeyUsage.value = foreigns;
|
||||
};
|
||||
|
||||
const checksUpdate = (checks: TableCheck[]) => {
|
||||
localTableChecks.value = checks;
|
||||
};
|
||||
|
||||
const saveContentListener = () => {
|
||||
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||
|
@ -62,7 +62,7 @@
|
||||
<button
|
||||
class="btn btn-dark btn-sm mr-0"
|
||||
:disabled="isSaving"
|
||||
:title="t('database.manageIndexes')"
|
||||
:title="t('database.manageForeignKeys')"
|
||||
@click="showForeignModal"
|
||||
>
|
||||
<BaseIcon
|
||||
@ -72,6 +72,19 @@
|
||||
/>
|
||||
<span>{{ t('database.foreignKeys') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-dark btn-sm ml-2 mr-0"
|
||||
:disabled="isSaving"
|
||||
:title="t('database.manageTableChecks')"
|
||||
@click="showTableChecksModal"
|
||||
>
|
||||
<BaseIcon
|
||||
class="mr-1"
|
||||
icon-name="mdiTableCheck"
|
||||
:size="24"
|
||||
/>
|
||||
<span>{{ t('database.tableChecks') }}</span>
|
||||
</button>
|
||||
|
||||
<div class="divider-vert py-3" />
|
||||
|
||||
@ -218,11 +231,19 @@
|
||||
:workspace="workspace"
|
||||
@hide="hideDdlModal"
|
||||
/>
|
||||
<WorkspaceTabPropsTableChecksModal
|
||||
v-if="isTableChecksModal"
|
||||
:local-checks="localTableChecks"
|
||||
:table="table"
|
||||
:workspace="workspace"
|
||||
@hide="hideTableChecksModal"
|
||||
@checks-update="checksUpdate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AlterTableParams, TableField, TableForeign, TableIndex, TableInfos, TableOptions } from 'common/interfaces/antares';
|
||||
import { AlterTableParams, TableCheck, TableField, TableForeign, TableIndex, TableInfos, TableOptions } from 'common/interfaces/antares';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@ -232,6 +253,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import BaseIcon from '@/components/BaseIcon.vue';
|
||||
import BaseLoader from '@/components/BaseLoader.vue';
|
||||
import BaseSelect from '@/components/BaseSelect.vue';
|
||||
import WorkspaceTabPropsTableChecksModal from '@/components/WorkspaceTabPropsTableChecksModal.vue';
|
||||
import WorkspaceTabPropsTableDdlModal from '@/components/WorkspaceTabPropsTableDdlModal.vue';
|
||||
import WorkspaceTabPropsTableFields from '@/components/WorkspaceTabPropsTableFields.vue';
|
||||
import WorkspaceTabPropsTableForeignModal from '@/components/WorkspaceTabPropsTableForeignModal.vue';
|
||||
@ -273,13 +295,17 @@ const isLoading = ref(false);
|
||||
const isSaving = ref(false);
|
||||
const isIndexesModal = ref(false);
|
||||
const isForeignModal = ref(false);
|
||||
const isTableChecksModal = ref(false);
|
||||
const isDdlModal = ref(false);
|
||||
|
||||
const originalFields: Ref<TableField[]> = ref([]);
|
||||
const localFields: Ref<TableField[]> = ref([]);
|
||||
const originalKeyUsage: Ref<TableForeign[]> = ref([]);
|
||||
const localKeyUsage: Ref<TableForeign[]> = ref([]);
|
||||
const originalIndexes: Ref<TableIndex[]> = ref([]);
|
||||
const localIndexes: Ref<TableIndex[]> = ref([]);
|
||||
const originalTableChecks: Ref<TableCheck[]> = ref([]);
|
||||
const localTableChecks: Ref<TableCheck[]> = ref([]);
|
||||
const tableOptions: Ref<TableOptions> = ref(null);
|
||||
const localOptions: Ref<TableOptions> = ref({} as TableOptions);
|
||||
const lastTable = ref(null);
|
||||
@ -307,6 +333,7 @@ const isChanged = computed(() => {
|
||||
return JSON.stringify(originalFields.value) !== JSON.stringify(localFields.value) ||
|
||||
JSON.stringify(originalKeyUsage.value) !== JSON.stringify(localKeyUsage.value) ||
|
||||
JSON.stringify(originalIndexes.value) !== JSON.stringify(localIndexes.value) ||
|
||||
JSON.stringify(originalTableChecks.value) !== JSON.stringify(localTableChecks.value) ||
|
||||
JSON.stringify(tableOptions.value) !== JSON.stringify(localOptions.value);
|
||||
});
|
||||
|
||||
@ -430,6 +457,27 @@ const getFieldsData = async () => {
|
||||
addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
if (workspace.value.customizations.tableCheck) {
|
||||
try { // Table checks
|
||||
const { status, response } = await Tables.getTableChecks(params);
|
||||
|
||||
if (status === 'success') {
|
||||
originalTableChecks.value = response.map((check: TableCheck) => {
|
||||
return {
|
||||
_antares_id: uidGen(),
|
||||
...check
|
||||
};
|
||||
});
|
||||
localTableChecks.value = JSON.parse(JSON.stringify(originalTableChecks.value));
|
||||
}
|
||||
else
|
||||
addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
};
|
||||
|
||||
@ -527,6 +575,33 @@ const saveChanges = async () => {
|
||||
// Foreigns Deletions
|
||||
foreignChanges.deletions = originalKeyUsage.value.filter(foreign => !localForeignIDs.includes(foreign._antares_id));
|
||||
|
||||
// CHECKS
|
||||
const checkChanges = {
|
||||
additions: [] as TableCheck[],
|
||||
changes: [] as TableCheck[],
|
||||
deletions: [] as TableCheck[]
|
||||
};
|
||||
const originalCheckIDs = originalTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
const localCheckIDs = localTableChecks.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||
|
||||
// Check Additions
|
||||
checkChanges.additions = localTableChecks.value.filter(check => !originalCheckIDs.includes(check._antares_id));
|
||||
|
||||
// Check Changes
|
||||
originalTableChecks.value.forEach(originalCheck => {
|
||||
const lI = localTableChecks.value.findIndex(localCheck => localCheck._antares_id === originalCheck._antares_id);
|
||||
if (JSON.stringify(originalCheck) !== JSON.stringify(localTableChecks.value[lI])) {
|
||||
if (localTableChecks.value[lI]) {
|
||||
checkChanges.changes.push({
|
||||
...localTableChecks.value[lI]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check Deletions
|
||||
checkChanges.deletions = originalTableChecks.value.filter(check => !localCheckIDs.includes(check._antares_id));
|
||||
|
||||
// ALTER
|
||||
const params = {
|
||||
uid: props.connection.uid,
|
||||
@ -543,6 +618,7 @@ const saveChanges = async () => {
|
||||
deletions,
|
||||
indexChanges,
|
||||
foreignChanges,
|
||||
checkChanges,
|
||||
options
|
||||
} as unknown as AlterTableParams;
|
||||
|
||||
@ -583,6 +659,7 @@ const clearChanges = () => {
|
||||
localFields.value = JSON.parse(JSON.stringify(originalFields.value));
|
||||
localIndexes.value = JSON.parse(JSON.stringify(originalIndexes.value));
|
||||
localKeyUsage.value = JSON.parse(JSON.stringify(originalKeyUsage.value));
|
||||
localTableChecks.value = JSON.parse(JSON.stringify(originalTableChecks.value));
|
||||
localOptions.value = JSON.parse(JSON.stringify(tableOptions.value));
|
||||
newFieldsCounter.value = 0;
|
||||
};
|
||||
@ -702,6 +779,14 @@ const hideForeignModal = () => {
|
||||
isForeignModal.value = false;
|
||||
};
|
||||
|
||||
const showTableChecksModal = () => {
|
||||
isTableChecksModal.value = true;
|
||||
};
|
||||
|
||||
const hideTableChecksModal = () => {
|
||||
isTableChecksModal.value = false;
|
||||
};
|
||||
|
||||
const showDdlModal = () => {
|
||||
isDdlModal.value = true;
|
||||
};
|
||||
@ -714,6 +799,10 @@ const foreignsUpdate = (foreigns: TableForeign[]) => {
|
||||
localKeyUsage.value = foreigns;
|
||||
};
|
||||
|
||||
const checksUpdate = (checks: TableCheck[]) => {
|
||||
localTableChecks.value = checks;
|
||||
};
|
||||
|
||||
const saveContentListener = () => {
|
||||
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||
|
268
src/renderer/components/WorkspaceTabPropsTableChecksModal.vue
Normal file
268
src/renderer/components/WorkspaceTabPropsTableChecksModal.vue
Normal file
@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<ConfirmModal
|
||||
:confirm-text="t('general.confirm')"
|
||||
size="medium"
|
||||
class="options-modal"
|
||||
@confirm="confirmChecksChange"
|
||||
@hide="$emit('hide')"
|
||||
>
|
||||
<template #header>
|
||||
<div class="d-flex">
|
||||
<BaseIcon
|
||||
class="mr-1"
|
||||
icon-name="mdiTableCheck"
|
||||
:size="24"
|
||||
/>
|
||||
<span class="cut-text">{{ t('database.tableChecks') }} "{{ table }}"</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="columns col-gapless">
|
||||
<div class="column col-5">
|
||||
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||
<div class="panel-header pt-0 pl-0">
|
||||
<div class="d-flex">
|
||||
<button class="btn btn-dark btn-sm d-flex" @click="addCheck">
|
||||
<BaseIcon
|
||||
class="mr-1"
|
||||
icon-name="mdiCheckboxMarkedCirclePlusOutline"
|
||||
:size="24"
|
||||
/>
|
||||
<span>{{ t('general.add') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||
:title="t('database.clearChanges')"
|
||||
:disabled="!isChanged"
|
||||
@click.prevent="clearChanges"
|
||||
>
|
||||
<BaseIcon
|
||||
class="mr-1"
|
||||
icon-name="mdiDeleteSweep"
|
||||
:size="24"
|
||||
/>
|
||||
<span>{{ t('general.clear') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="checksPanel" class="panel-body p-0 pr-1">
|
||||
<div
|
||||
v-for="check in checksProxy"
|
||||
:key="check._antares_id"
|
||||
class="tile tile-centered c-hand mb-1 p-1"
|
||||
:class="{'selected-element': selectedCheckID === check._antares_id}"
|
||||
@click="selectCheck($event, check._antares_id)"
|
||||
>
|
||||
<div class="tile-icon">
|
||||
<div>
|
||||
<BaseIcon
|
||||
class="mt-2 column-key"
|
||||
icon-name="mdiCheckboxMarkedCircleOutline"
|
||||
:size="24"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-content">
|
||||
<div class="tile-title">
|
||||
{{ check.name }}
|
||||
</div>
|
||||
<small class="tile-subtitle text-gray d-inline-block cut-text" style="width: 100%;">{{ check.clause }}</small>
|
||||
</div>
|
||||
<div class="tile-action">
|
||||
<button
|
||||
class="btn btn-link remove-field p-0 mr-2"
|
||||
:title="t('general.delete')"
|
||||
@click.prevent="removeCheck(check._antares_id)"
|
||||
>
|
||||
<BaseIcon
|
||||
icon-name="mdiClose"
|
||||
:size="18"
|
||||
class="mt-2"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column col-7 pl-2 editor-col">
|
||||
<form
|
||||
v-if="selectedCheckObj"
|
||||
:style="{ height: modalInnerHeight + 'px'}"
|
||||
class="form-horizontal"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label class="form-label col-3">
|
||||
{{ t('general.name') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<input
|
||||
v-model="selectedCheckObj.name"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label col-3">
|
||||
{{ t('database.checkClause') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<textarea
|
||||
v-model="selectedCheckObj.clause"
|
||||
class="form-input"
|
||||
style="resize: vertical;"
|
||||
rows="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div v-if="!checksProxy.length" class="empty">
|
||||
<div class="empty-icon">
|
||||
<BaseIcon
|
||||
class="mr-1"
|
||||
icon-name="mdiCheckboxMarkedCircleOutline"
|
||||
:size="48"
|
||||
/>
|
||||
</div>
|
||||
<p class="empty-title h5">
|
||||
{{ t('database.thereAreNoTableChecks') }}
|
||||
</p>
|
||||
<div class="empty-action">
|
||||
<button class="btn btn-primary" @click="addCheck">
|
||||
{{ t('database.createNewCheck') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { TableCheck } from 'common/interfaces/antares';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import { computed, onMounted, onUnmounted, Ref, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||
import BaseIcon from '@/components/BaseIcon.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
localChecks: Array,
|
||||
table: String,
|
||||
workspace: Object
|
||||
});
|
||||
|
||||
const emit = defineEmits(['hide', 'checks-update']);
|
||||
|
||||
const checksPanel: Ref<HTMLDivElement> = ref(null);
|
||||
const checksProxy: Ref<TableCheck[]> = ref([]);
|
||||
const selectedCheckID = ref('');
|
||||
const modalInnerHeight = ref(400);
|
||||
|
||||
const selectedCheckObj = computed(() => checksProxy.value.find(index => index._antares_id === selectedCheckID.value));
|
||||
const isChanged = computed(() => JSON.stringify(props.localChecks) !== JSON.stringify(checksProxy.value));
|
||||
|
||||
const confirmChecksChange = () => {
|
||||
const filteredChecks = checksProxy.value.filter(check => check.clause.trim().length);
|
||||
emit('checks-update', filteredChecks);
|
||||
};
|
||||
|
||||
const selectCheck = (event: MouseEvent, id: string) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if (selectedCheckID.value !== id && !(event.target as any).classList.contains('remove-field'))
|
||||
selectedCheckID.value = id;
|
||||
};
|
||||
|
||||
const getModalInnerHeight = () => {
|
||||
const modalBody = document.querySelector('.modal-body');
|
||||
if (modalBody)
|
||||
modalInnerHeight.value = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||
};
|
||||
|
||||
const addCheck = () => {
|
||||
const uid = uidGen();
|
||||
checksProxy.value = [...checksProxy.value, {
|
||||
_antares_id: uid,
|
||||
name: `CHK_${uid.substring(0, 4)}`,
|
||||
clause: ''
|
||||
}];
|
||||
|
||||
if (checksProxy.value.length === 1)
|
||||
resetSelectedID();
|
||||
|
||||
setTimeout(() => {
|
||||
checksPanel.value.scrollTop = checksPanel.value.scrollHeight + 60;
|
||||
selectedCheckID.value = uid;
|
||||
}, 20);
|
||||
};
|
||||
|
||||
const removeCheck = (id: string) => {
|
||||
checksProxy.value = checksProxy.value.filter(index => index._antares_id !== id);
|
||||
|
||||
if (selectedCheckID.value === id && checksProxy.value.length)
|
||||
resetSelectedID();
|
||||
};
|
||||
|
||||
const clearChanges = () => {
|
||||
checksProxy.value = JSON.parse(JSON.stringify(props.localChecks));
|
||||
if (!checksProxy.value.some(index => index._antares_id === selectedCheckID.value))
|
||||
resetSelectedID();
|
||||
};
|
||||
|
||||
const resetSelectedID = () => {
|
||||
selectedCheckID.value = checksProxy.value.length ? checksProxy.value[0]._antares_id : '';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
checksProxy.value = JSON.parse(JSON.stringify(props.localChecks));
|
||||
|
||||
if (checksProxy.value.length)
|
||||
resetSelectedID();
|
||||
|
||||
getModalInnerHeight();
|
||||
window.addEventListener('resize', getModalInnerHeight);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', getModalInnerHeight);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
border-radius: $border-radius;
|
||||
opacity: 0.5;
|
||||
transition: background 0.2s;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
.tile-action {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.tile-action {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected-element {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.fields-list {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.remove-field svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
@ -126,6 +126,7 @@ export const enUS = {
|
||||
insert: 'Insert',
|
||||
indexes: 'Indexes',
|
||||
foreignKeys: 'Foreign keys',
|
||||
tableChecks: 'Table checks',
|
||||
length: 'Length',
|
||||
unsigned: 'Unsigned',
|
||||
default: 'Default',
|
||||
@ -190,12 +191,15 @@ export const enUS = {
|
||||
addNewField: 'Add new field',
|
||||
manageIndexes: 'Manage indexes',
|
||||
manageForeignKeys: 'Manage foreign keys',
|
||||
manageTableChecks: 'Manage table checks',
|
||||
allowNull: 'Allow NULL',
|
||||
zeroFill: 'Zero fill',
|
||||
customValue: 'Custom value',
|
||||
onUpdate: 'On update',
|
||||
deleteField: 'Delete field',
|
||||
createNewIndex: 'Create new index',
|
||||
createNewCheck: 'Create new check',
|
||||
checkClause: 'Check clause',
|
||||
addToIndex: 'Add to index',
|
||||
createNewTable: 'Create new table',
|
||||
emptyTable: 'Empty table',
|
||||
@ -205,6 +209,7 @@ export const enUS = {
|
||||
emptyConfirm: 'Do you confirm to empty',
|
||||
thereAreNoIndexes: 'There are no indexes',
|
||||
thereAreNoForeign: 'There are no foreign keys',
|
||||
thereAreNoTableChecks: 'There are no table checks',
|
||||
createNewForeign: 'Create new foreign key',
|
||||
referenceTable: 'Ref. table',
|
||||
referenceField: 'Ref. field',
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@ export default class {
|
||||
return ipcRenderer.invoke('get-table-indexes', unproxify(params));
|
||||
}
|
||||
|
||||
static getTableChecks (params: { uid: string; schema: string; table: string }): Promise<IpcResponse> {
|
||||
return ipcRenderer.invoke('get-table-checks', unproxify(params));
|
||||
}
|
||||
|
||||
static getTableDll (params: { uid: string; schema: string; table: string }): Promise<IpcResponse<string>> {
|
||||
return ipcRenderer.invoke('get-table-ddl', unproxify(params));
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export interface QueryLog {
|
||||
}
|
||||
|
||||
export interface DebugLog {
|
||||
level: 'log' | 'info' | 'warn' | 'error';
|
||||
level: 'log' | 'info' | 'warn' | 'error' | string;
|
||||
process: 'renderer' | 'main' | 'worker';
|
||||
message: string;
|
||||
date: Date;
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { useConsoleStore } from './console';
|
||||
|
||||
export interface Notification {
|
||||
uid: string;
|
||||
status: string;
|
||||
@ -15,6 +17,13 @@ export const useNotificationsStore = defineStore('notifications', {
|
||||
addNotification (payload: { status: string; message: string }) {
|
||||
const notification: Notification = { uid: uidGen('N'), ...payload };
|
||||
this.notifications.unshift(notification);
|
||||
|
||||
useConsoleStore().putLog('debug', {
|
||||
level: notification.status,
|
||||
process: 'renderer',
|
||||
message: notification.message,
|
||||
date: new Date()
|
||||
});
|
||||
},
|
||||
removeNotification (uid: string) {
|
||||
this.notifications = (this.notifications as Notification[]).filter(item => item.uid !== uid);
|
||||
|
Loading…
x
Reference in New Issue
Block a user