mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
dictionary descriptions saved to app
This commit is contained in:
parent
ae861ef1ae
commit
9a486c47b0
@ -186,13 +186,19 @@ module.exports = {
|
||||
} catch (err) {
|
||||
res.virtualReferences = [];
|
||||
}
|
||||
try {
|
||||
res.dictionaryDescriptions = JSON.parse(
|
||||
await fs.readFile(path.join(dir, 'dictionary-descriptions.config.json'), { encoding: 'utf-8' })
|
||||
);
|
||||
} catch (err) {
|
||||
res.dictionaryDescriptions = [];
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
saveVfk_meta: true,
|
||||
async saveVfk({ appFolder, schemaName, pureName, refSchemaName, refTableName, columns }) {
|
||||
const file = path.join(appdir(), appFolder, 'virtual-references.config.json');
|
||||
async saveConfigFile(appFolder, filename, filterFunc, newItem) {
|
||||
const file = path.join(appdir(), appFolder, filename);
|
||||
|
||||
let json;
|
||||
try {
|
||||
@ -201,33 +207,57 @@ module.exports = {
|
||||
json = [];
|
||||
}
|
||||
|
||||
if (columns.length == 1) {
|
||||
json = json.filter(
|
||||
x =>
|
||||
!(
|
||||
x.schemaName == schemaName &&
|
||||
x.pureName == pureName &&
|
||||
x.columns.length == 1 &&
|
||||
x.columns[0].columnName == columns[0].columnName
|
||||
)
|
||||
);
|
||||
if (filterFunc) {
|
||||
json = json.filter(filterFunc);
|
||||
}
|
||||
|
||||
json = [
|
||||
...json,
|
||||
json = [...json, newItem];
|
||||
|
||||
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
|
||||
|
||||
socket.emitChanged(`app-files-changed-${appFolder}`);
|
||||
socket.emitChanged('used-apps-changed');
|
||||
},
|
||||
|
||||
saveVirtualReference_meta: true,
|
||||
async saveVirtualReference({ appFolder, schemaName, pureName, refSchemaName, refTableName, columns }) {
|
||||
await this.saveConfigFile(
|
||||
appFolder,
|
||||
'virtual-references.config.json',
|
||||
columns.length == 1
|
||||
? x =>
|
||||
!(
|
||||
x.schemaName == schemaName &&
|
||||
x.pureName == pureName &&
|
||||
x.columns.length == 1 &&
|
||||
x.columns[0].columnName == columns[0].columnName
|
||||
)
|
||||
: null,
|
||||
{
|
||||
schemaName,
|
||||
pureName,
|
||||
refSchemaName,
|
||||
refTableName,
|
||||
columns,
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
return true;
|
||||
},
|
||||
|
||||
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
|
||||
|
||||
socket.emitChanged(`app-files-changed-${appFolder}`);
|
||||
socket.emitChanged('used-apps-changed');
|
||||
saveDictionaryDescription_meta: true,
|
||||
async saveDictionaryDescription({ appFolder, pureName, schemaName, expresssion, columns, delimiter }) {
|
||||
await this.saveConfigFile(
|
||||
appFolder,
|
||||
'dictionary-descriptions.config.json',
|
||||
x => !(x.schemaName == schemaName && x.pureName == pureName),
|
||||
{
|
||||
schemaName,
|
||||
pureName,
|
||||
expresssion,
|
||||
columns,
|
||||
delimiter,
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
@ -101,10 +101,10 @@ export function extendDatabaseInfoFromApps(db: DatabaseInfo, apps: ApplicationDe
|
||||
...(table.foreignKeys || []),
|
||||
..._flatten(apps.map(app => app.virtualReferences || []))
|
||||
.filter(fk => fk.pureName == table.pureName && fk.schemaName == table.schemaName)
|
||||
.map(fk => ({ ...fk, isVirtual: true })),
|
||||
.map(fk => ({ ...fk, constraintType: 'foreignKey', isVirtual: true })),
|
||||
],
|
||||
})),
|
||||
};
|
||||
} as DatabaseInfo;
|
||||
return addTableDependencies(dbExt);
|
||||
}
|
||||
|
||||
|
6
packages/types/appdefs.d.ts
vendored
6
packages/types/appdefs.d.ts
vendored
@ -19,10 +19,10 @@ interface VirtualReferenceDefinition {
|
||||
}[];
|
||||
}
|
||||
|
||||
interface ColumnDescriptionDefinition {
|
||||
interface DictionaryDescriptionDefinition {
|
||||
pureName: string;
|
||||
schemaName?: string;
|
||||
expresssion?: string;
|
||||
expresssion: string;
|
||||
columns: string[];
|
||||
delimiter: string;
|
||||
}
|
||||
@ -33,5 +33,5 @@ export interface ApplicationDefinition {
|
||||
queries: ApplicationQuery[];
|
||||
commands: ApplicationCommand[];
|
||||
virtualReferences: VirtualReferenceDefinition[];
|
||||
columnDescriptions: ColumnDescriptionDefinition[];
|
||||
dictionaryDescriptions: DictionaryDescriptionDefinition[];
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
import { subscribeConnectionPingers } from './utility/connectionsPinger';
|
||||
import { subscribePermissionCompiler } from './utility/hasPermission';
|
||||
import { apiCall } from './utility/api';
|
||||
import { getUsedApps } from './utility/metadataLoaders';
|
||||
|
||||
let loadedApi = false;
|
||||
|
||||
@ -30,7 +31,8 @@
|
||||
const settings = await apiCall('config/get-settings');
|
||||
const connections = await apiCall('connections/list');
|
||||
const config = await apiCall('config/get');
|
||||
loadedApi = settings && connections && config;
|
||||
const apps = await getUsedApps();
|
||||
loadedApi = settings && connections && config && apps;
|
||||
|
||||
if (loadedApi) {
|
||||
subscribeApiDependendStores();
|
||||
|
@ -1,11 +1,6 @@
|
||||
<script lang="ts" context="module">
|
||||
export const extractKey = props => props.name;
|
||||
|
||||
export function filterAppsForDatabase(connection, database, $apps) {
|
||||
const db = (connection?.databases || []).find(x => x.name == database);
|
||||
return $apps.filter(app => db && db[`useApp:${app.name}`]);
|
||||
}
|
||||
|
||||
export function getDatabaseMenuItems(connection, name, $extensions, $currentDatabase, $apps) {
|
||||
const apps = filterAppsForDatabase(connection, name, $apps);
|
||||
const handleNewQuery = () => {
|
||||
@ -244,6 +239,7 @@
|
||||
import { apiCall } from '../utility/api';
|
||||
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
|
||||
import { filterAppsForDatabase } from '../utility/appTools';
|
||||
|
||||
export let data;
|
||||
export let passProps;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import {
|
||||
useConnectionInfo,
|
||||
useConnectionList,
|
||||
useDatabaseInfo,
|
||||
useDatabaseServerVersion,
|
||||
useServerVersion,
|
||||
@ -49,6 +50,7 @@
|
||||
$: serverVersion = useDatabaseServerVersion({ conid, database });
|
||||
$: apps = useUsedApps();
|
||||
$: extendedDbInfo = extendDatabaseInfoFromApps($dbinfo, $apps);
|
||||
$: connections = useConnectionList();
|
||||
|
||||
// $: console.log('serverVersion', $serverVersion);
|
||||
|
||||
@ -70,7 +72,7 @@
|
||||
extendedDbInfo,
|
||||
{ showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) },
|
||||
$serverVersion,
|
||||
table => getDictionaryDescription(table, conid, database)
|
||||
table => getDictionaryDescription(table, conid, database, $apps, $connections)
|
||||
)
|
||||
: null;
|
||||
|
||||
@ -86,7 +88,7 @@
|
||||
extendedDbInfo,
|
||||
{ showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) },
|
||||
$serverVersion,
|
||||
table => getDictionaryDescription(table, conid, database)
|
||||
table => getDictionaryDescription(table, conid, database, $apps, $connections)
|
||||
)
|
||||
: null;
|
||||
|
||||
|
@ -9,11 +9,13 @@
|
||||
export let name;
|
||||
export let options;
|
||||
export let isClearable = false;
|
||||
export let selectFieldComponent = SelectField;
|
||||
|
||||
const { values, setFieldValue } = getFormContext();
|
||||
</script>
|
||||
|
||||
<SelectField
|
||||
<svelte:component
|
||||
this={selectFieldComponent}
|
||||
{...$$restProps}
|
||||
value={$values && $values[name]}
|
||||
options={_.compact(options)}
|
||||
|
@ -1,47 +1,22 @@
|
||||
<script lang="ts" context="module">
|
||||
export async function saveDbToApp(conid, database, app) {
|
||||
if (app == '#new') {
|
||||
const folder = await apiCall('apps/create-folder', { folder: database });
|
||||
|
||||
await apiCall('connections/update-database', {
|
||||
conid,
|
||||
database,
|
||||
values: {
|
||||
[`useApp:${folder}`]: true,
|
||||
},
|
||||
});
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
await apiCall('connections/update-database', {
|
||||
conid,
|
||||
database,
|
||||
values: {
|
||||
[`useApp:${app}`]: true,
|
||||
},
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import { filterAppsForDatabase } from '../appobj/DatabaseAppObject.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
import SelectField from '../forms/SelectField.svelte';
|
||||
import { currentDatabase } from '../stores';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { filterAppsForDatabase } from '../utility/appTools';
|
||||
import { useAppFolders, useUsedApps } from '../utility/metadataLoaders';
|
||||
|
||||
export let value = '#new';
|
||||
export let disableInitialize = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: appFolders = useAppFolders();
|
||||
$: usedApps = useUsedApps();
|
||||
|
||||
$: {
|
||||
if (value == '#new' && $currentDatabase) {
|
||||
if (!disableInitialize && value == '#new' && $currentDatabase) {
|
||||
const filtered = filterAppsForDatabase($currentDatabase.connection, $currentDatabase.name, $usedApps || []);
|
||||
const common = _.intersection(
|
||||
($appFolders || []).map(x => x.name),
|
||||
@ -49,6 +24,7 @@
|
||||
);
|
||||
if (common.length > 0) {
|
||||
value = common[0] as string;
|
||||
dispatch('change', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,6 +36,7 @@
|
||||
{value}
|
||||
on:change={e => {
|
||||
value = e.detail;
|
||||
dispatch('change', value);
|
||||
}}
|
||||
options={[
|
||||
{ label: '(New application linked to current DB)', value: '#new' },
|
@ -1,10 +1,11 @@
|
||||
<script lang="ts">
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import _ from 'lodash';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormStyledButton from '../elements/FormStyledButton.svelte';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { useTableInfo } from '../utility/metadataLoaders';
|
||||
import { useAppFolders, useConnectionList, useTableInfo, useUsedApps } from '../utility/metadataLoaders';
|
||||
import TableControl from '../elements/TableControl.svelte';
|
||||
import TextField from '../forms/TextField.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
@ -19,6 +20,10 @@
|
||||
} from '../utility/dictionaryDescriptionTools';
|
||||
import { includes } from 'lodash';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import TargetApplicationSelect from '../forms/TargetApplicationSelect.svelte';
|
||||
import { currentDatabase } from '../stores';
|
||||
import { filterAppsForDatabase } from '../utility/appTools';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@ -28,18 +33,41 @@
|
||||
|
||||
$: tableInfo = useTableInfo({ conid, database, schemaName, pureName });
|
||||
|
||||
$: descriptionInfo = getDictionaryDescription($tableInfo, conid, database, true);
|
||||
$: apps = useUsedApps();
|
||||
$: appFolders = useAppFolders();
|
||||
$: connections = useConnectionList();
|
||||
|
||||
const values = writable({});
|
||||
$: descriptionInfo = getDictionaryDescription($tableInfo, conid, database, $apps, $connections, true);
|
||||
|
||||
const values = writable({ targetApplication: '#new' } as any);
|
||||
|
||||
function initValues(descriptionInfo) {
|
||||
$values = {
|
||||
targetApplication: $values.targetApplication,
|
||||
columns: descriptionInfo.expression,
|
||||
delimiter: descriptionInfo.delimiter,
|
||||
};
|
||||
}
|
||||
|
||||
$: if (descriptionInfo) initValues(descriptionInfo);
|
||||
$: {
|
||||
if (descriptionInfo) initValues(descriptionInfo);
|
||||
}
|
||||
|
||||
$: {
|
||||
if ($values.targetApplication == '#new' && $currentDatabase) {
|
||||
const filtered = filterAppsForDatabase($currentDatabase.connection, $currentDatabase.name, $apps || []);
|
||||
const common = _.intersection(
|
||||
($appFolders || []).map(x => x.name),
|
||||
filtered.map(x => x.name)
|
||||
);
|
||||
if (common.length > 0) {
|
||||
$values = {
|
||||
...$values,
|
||||
targetApplication: common[0],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<FormProviderCore {values}>
|
||||
@ -75,7 +103,14 @@
|
||||
|
||||
<FormTextField name="delimiter" label="Delimiter" />
|
||||
|
||||
<FormCheckboxField name="useForAllDatabases" label="Use for all databases" />
|
||||
<FormSelectField
|
||||
label="Target application"
|
||||
name="targetApplication"
|
||||
disableInitialize
|
||||
selectFieldComponent={TargetApplicationSelect}
|
||||
/>
|
||||
|
||||
<!-- <FormCheckboxField name="useForAllDatabases" label="Use for all databases" /> -->
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormSubmit
|
||||
@ -89,7 +124,7 @@
|
||||
database,
|
||||
$values.columns,
|
||||
$values.delimiter,
|
||||
$values.useForAllDatabases
|
||||
$values.targetApplication
|
||||
);
|
||||
onConfirm();
|
||||
}}
|
||||
|
@ -6,7 +6,7 @@
|
||||
import { closeCurrentModal, showModal } from './modalTools';
|
||||
import DefineDictionaryDescriptionModal from './DefineDictionaryDescriptionModal.svelte';
|
||||
import ScrollableTableControl from '../elements/ScrollableTableControl.svelte';
|
||||
import { getTableInfo } from '../utility/metadataLoaders';
|
||||
import { getTableInfo, useConnectionList, useUsedApps } from '../utility/metadataLoaders';
|
||||
import { getDictionaryDescription } from '../utility/dictionaryDescriptionTools';
|
||||
import { onMount } from 'svelte';
|
||||
import { dumpSqlSelect } from 'dbgate-sqltree';
|
||||
@ -33,6 +33,9 @@
|
||||
|
||||
let checkedKeys = [];
|
||||
|
||||
$: apps = useUsedApps();
|
||||
$: connections = useConnectionList();
|
||||
|
||||
function defineDescription() {
|
||||
showModal(DefineDictionaryDescriptionModal, {
|
||||
conid,
|
||||
@ -45,7 +48,7 @@
|
||||
|
||||
async function reload() {
|
||||
tableInfo = await getTableInfo({ conid, database, schemaName, pureName });
|
||||
description = getDictionaryDescription(tableInfo, conid, database);
|
||||
description = getDictionaryDescription(tableInfo, conid, database, $apps, $connections);
|
||||
|
||||
if (!tableInfo || !description) return;
|
||||
if (tableInfo?.primaryKey?.columns?.length != 1) return;
|
||||
@ -112,6 +115,8 @@
|
||||
|
||||
$: {
|
||||
search;
|
||||
$apps;
|
||||
$connections;
|
||||
reload();
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,9 @@
|
||||
import _ from 'lodash';
|
||||
import { useDatabaseInfo, useTableInfo } from '../utility/metadataLoaders';
|
||||
import { onMount } from 'svelte';
|
||||
import TargetApplicationSelect, { saveDbToApp } from '../elements/TargetApplicationSelect.svelte';
|
||||
import TargetApplicationSelect from '../forms/TargetApplicationSelect.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { saveDbToApp } from '../utility/appTools';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@ -155,7 +156,7 @@
|
||||
value={'Save'}
|
||||
on:click={async () => {
|
||||
const appFolder = await saveDbToApp(conid, database, dstApp);
|
||||
await apiCall('apps/save-vfk', {
|
||||
await apiCall('apps/save-virtual-reference', {
|
||||
appFolder,
|
||||
schemaName,
|
||||
pureName,
|
||||
|
33
packages/web/src/utility/appTools.ts
Normal file
33
packages/web/src/utility/appTools.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { ApplicationDefinition, StoredConnection } from 'dbgate-types';
|
||||
import { apiCall } from '../utility/api';
|
||||
|
||||
export async function saveDbToApp(conid: string, database: string, app: string) {
|
||||
if (app == '#new') {
|
||||
const folder = await apiCall('apps/create-folder', { folder: database });
|
||||
|
||||
await apiCall('connections/update-database', {
|
||||
conid,
|
||||
database,
|
||||
values: {
|
||||
[`useApp:${folder}`]: true,
|
||||
},
|
||||
});
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
await apiCall('connections/update-database', {
|
||||
conid,
|
||||
database,
|
||||
values: {
|
||||
[`useApp:${app}`]: true,
|
||||
},
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
export function filterAppsForDatabase(connection, database: string, $apps): ApplicationDefinition[] {
|
||||
const db = (connection?.databases || []).find(x => x.name == database);
|
||||
return $apps.filter(app => db && db[`useApp:${app.name}`]);
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import { DictionaryDescription } from 'dbgate-datalib';
|
||||
import { TableInfo } from 'dbgate-types';
|
||||
import { ApplicationDefinition, TableInfo } from 'dbgate-types';
|
||||
import _ from 'lodash';
|
||||
import { getLocalStorage, setLocalStorage, removeLocalStorage } from './storageCache';
|
||||
import { apiCall } from './api';
|
||||
import { filterAppsForDatabase, saveDbToApp } from './appTools';
|
||||
|
||||
function checkDescriptionColumns(columns: string[], table: TableInfo) {
|
||||
if (!columns?.length) return false;
|
||||
@ -14,17 +15,20 @@ export function getDictionaryDescription(
|
||||
table: TableInfo,
|
||||
conid: string,
|
||||
database: string,
|
||||
apps: ApplicationDefinition[],
|
||||
connections,
|
||||
skipCheckSaved: boolean = false
|
||||
): DictionaryDescription {
|
||||
const keySpecific = `dictionary_spec_${table.schemaName}||${table.pureName}||${conid}||${database}`;
|
||||
const keyCommon = `dictionary_spec_${table.schemaName}||${table.pureName}`;
|
||||
const conn = connections.find(x => x._id == conid);
|
||||
const dbApps = filterAppsForDatabase(conn, database, apps);
|
||||
|
||||
const cachedSpecific = getLocalStorage(keySpecific);
|
||||
const cachedCommon = getLocalStorage(keyCommon);
|
||||
const cached = _.flatten(dbApps.map(x => x.dictionaryDescriptions || [])).find(
|
||||
x => x.pureName == table.pureName && x.schemaName == table.schemaName
|
||||
);
|
||||
|
||||
if (cachedSpecific && (skipCheckSaved || checkDescriptionColumns(cachedSpecific.columns, table)))
|
||||
return cachedSpecific;
|
||||
if (cachedCommon && (skipCheckSaved || checkDescriptionColumns(cachedCommon.columns, table))) return cachedCommon;
|
||||
if (cached && (skipCheckSaved || checkDescriptionColumns(cached.columns, table))) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const descColumn = table.columns.find(x => x?.dataType?.toLowerCase()?.includes('char'));
|
||||
if (descColumn) {
|
||||
@ -57,29 +61,22 @@ export function changeDelimitedColumnList(columns, columnName, isChecked) {
|
||||
return parsed.join(',');
|
||||
}
|
||||
|
||||
export function saveDictionaryDescription(
|
||||
export async function saveDictionaryDescription(
|
||||
table: TableInfo,
|
||||
conid: string,
|
||||
database: string,
|
||||
expression: string,
|
||||
delimiter: string,
|
||||
useForAllDatabases: boolean
|
||||
targetApplication: string
|
||||
) {
|
||||
const keySpecific = `dictionary_spec_${table.schemaName}||${table.pureName}||${conid}||${database}`;
|
||||
const keyCommon = `dictionary_spec_${table.schemaName}||${table.pureName}`;
|
||||
const appFolder = await saveDbToApp(conid, database, targetApplication);
|
||||
|
||||
removeLocalStorage(keySpecific);
|
||||
if (useForAllDatabases) removeLocalStorage(keyCommon);
|
||||
|
||||
const description = {
|
||||
await apiCall('apps/save-dictionary-description', {
|
||||
appFolder,
|
||||
schemaName: table.schemaName,
|
||||
pureName: table.pureName,
|
||||
columns: parseDelimitedColumnList(expression),
|
||||
expression,
|
||||
delimiter,
|
||||
};
|
||||
|
||||
if (useForAllDatabases) {
|
||||
setLocalStorage(keyCommon, description);
|
||||
} else {
|
||||
setLocalStorage(keySpecific, description);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user