dictionary descriptions saved to app

This commit is contained in:
Jan Prochazka 2022-01-29 14:15:39 +01:00
parent ae861ef1ae
commit 9a486c47b0
13 changed files with 180 additions and 100 deletions

View File

@ -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;
},

View File

@ -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);
}

View File

@ -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[];
}

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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)}

View File

@ -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' },

View File

@ -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();
}}

View File

@ -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();
}

View File

@ -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,

View 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}`]);
}

View File

@ -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);
}
});
}