define virtual fk

This commit is contained in:
Jan Prochazka
2022-01-28 17:36:40 +01:00
parent 20938fb6ce
commit a47973d58d
8 changed files with 220 additions and 37 deletions

View File

@@ -18,9 +18,10 @@ module.exports = {
createFolder_meta: true, createFolder_meta: true,
async createFolder({ folder }) { async createFolder({ folder }) {
await fs.mkdir(path.join(appdir(), folder)); const name = await this.getNewAppFolder({ name: folder });
await fs.mkdir(path.join(appdir(), name));
socket.emitChanged('app-folders-changed'); socket.emitChanged('app-folders-changed');
return true; return name;
}, },
files_meta: true, files_meta: true,
@@ -45,7 +46,7 @@ module.exports = {
.map(name => ({ .map(name => ({
name: 'virtual-references.json', name: 'virtual-references.json',
label: 'virtual-references.json', label: 'virtual-references.json',
type: 'vfk', type: 'vfk.json',
})); }));
} }
@@ -186,4 +187,46 @@ module.exports = {
return res; return res;
}, },
saveVfk_meta: true,
async saveVfk({ appFolder, schemaName, pureName, refSchemaName, refTableName, columns }) {
const file = path.join(appdir(), appFolder, 'virtual-references.json');
let json;
try {
json = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
} catch (err) {
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
)
);
}
json = [
...json,
{
schemaName,
pureName,
refSchemaName,
refTableName,
columns,
},
];
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
socket.emitChanged(`app-files-changed-${appFolder}`);
socket.emitChanged('used-apps-changed');
return true;
},
}; };

View File

@@ -3,9 +3,11 @@
const connProps: any = {}; const connProps: any = {};
let tooltip = undefined; let tooltip = undefined;
const savedFile = fileType == 'vfk.json' ? fileName : fileName + '.' + fileType;
const resp = await apiCall('files/load', { const resp = await apiCall('files/load', {
folder: 'app:' + folderName, folder: 'app:' + folderName,
file: fileName + '.' + fileType, file: savedFile,
format: 'text', format: 'text',
}); });
@@ -16,7 +18,7 @@
tabComponent, tabComponent,
tooltip, tooltip,
props: { props: {
savedFile: fileName + '.' + fileType, savedFile,
savedFolder: 'app:' + folderName, savedFolder: 'app:' + folderName,
savedFormat: 'text', savedFormat: 'text',
appFolder: folderName, appFolder: folderName,
@@ -30,6 +32,7 @@
export const extractKey = data => data.fileName; export const extractKey = data => data.fileName;
export const createMatcher = ({ fileName }) => filter => filterName(filter, fileName); export const createMatcher = ({ fileName }) => filter => filterName(filter, fileName);
const APP_ICONS = { const APP_ICONS = {
'vfk.json': 'img json',
'command.sql': 'img app-command', 'command.sql': 'img app-command',
'query.sql': 'img app-query', 'query.sql': 'img app-query',
}; };
@@ -85,12 +88,15 @@
if (data.fileType.endsWith('.sql')) { if (data.fileType.endsWith('.sql')) {
handleOpenSqlFile(); handleOpenSqlFile();
} }
if (data.fileType.endsWith('.json')) {
handleOpenJsonFile();
}
}; };
const handleOpenSqlFile = () => { const handleOpenSqlFile = () => {
openTextFile(data.fileName, data.fileType, data.folderName, 'QueryTab', 'img sql-file'); openTextFile(data.fileName, data.fileType, data.folderName, 'QueryTab', 'img sql-file');
}; };
const handleOpenYamlFile = () => { const handleOpenJsonFile = () => {
openTextFile(data.fileName, data.fileType, data.folderName, 'YamlEditorTab', 'img yaml'); openTextFile(data.fileName, data.fileType, data.folderName, 'JsonEditorTab', 'img json');
}; };
function createMenu() { function createMenu() {
@@ -98,6 +104,7 @@
{ text: 'Delete', onClick: handleDelete }, { text: 'Delete', onClick: handleDelete },
{ text: 'Rename', onClick: handleRename }, { text: 'Rename', onClick: handleRename },
data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile }, data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile },
data.fileType.endsWith('.json') && { text: 'Open JSON', onClick: handleOpenJsonFile },
// data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile }, // data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile },
]; ];

View File

@@ -1,18 +1,69 @@
<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"> <script lang="ts">
import { map } from 'lodash'; import _ from 'lodash';
import { filterAppsForDatabase } from '../appobj/DatabaseAppObject.svelte';
import SelectField from '../forms/SelectField.svelte'; import SelectField from '../forms/SelectField.svelte';
import { useAppFolders } from '../utility/metadataLoaders'; import { currentDatabase } from '../stores';
import { apiCall } from '../utility/api';
import { useAppFolders, useUsedApps } from '../utility/metadataLoaders';
$: apps = useAppFolders(); export let value = '#new';
$: appFolders = useAppFolders();
$: usedApps = useUsedApps();
$: {
if (value == '#new' && $currentDatabase) {
const filtered = filterAppsForDatabase($currentDatabase.connection, $currentDatabase.name, $usedApps || []);
const common = _.intersection(
($appFolders || []).map(x => x.name),
filtered.map(x => x.name)
);
if (common.length > 0) {
value = common[0] as string;
}
}
}
</script> </script>
<SelectField <SelectField
isNative isNative
{...$$restProps} {...$$restProps}
{value}
on:change={e => {
value = e.detail;
}}
options={[ options={[
{ label: '(New application linked to current DB)', value: 'new' }, { label: '(New application linked to current DB)', value: '#new' },
...($apps || []).map(app => ({ ...($appFolders || []).map(app => ({
label: app.name, label: app.name,
value: app.name, value: app.name,
})), })),

View File

@@ -81,7 +81,7 @@
value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })} value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })}
isNative isNative
notSelected notSelected
options={(dbInfo?.tables || []).map(tbl => ({ options={_.sortBy(dbInfo?.tables || [], ['schemaName', 'pureName']).map(tbl => ({
label: fullNameToLabel(tbl), label: fullNameToLabel(tbl),
value: fullNameToString(tbl), value: fullNameToString(tbl),
}))} }))}

View File

@@ -1,32 +1,17 @@
<script lang="ts"> <script lang="ts">
import FormStyledButton from '../elements/FormStyledButton.svelte'; import FormStyledButton from '../elements/FormStyledButton.svelte';
import uuidv1 from 'uuid/v1';
import FormSelectField from '../forms/FormSelectField.svelte';
import FormTextField from '../forms/FormTextField.svelte';
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
import FormProvider from '../forms/FormProvider.svelte'; import FormProvider from '../forms/FormProvider.svelte';
import FormSubmit from '../forms/FormSubmit.svelte'; import FormSubmit from '../forms/FormSubmit.svelte';
import ModalBase from '../modals/ModalBase.svelte'; import ModalBase from '../modals/ModalBase.svelte';
import { closeCurrentModal } from '../modals/modalTools'; import { closeCurrentModal } from '../modals/modalTools';
import ElectronFilesInput from '../impexp/ElectronFilesInput.svelte'; import { fullNameFromString, fullNameToLabel, fullNameToString } from 'dbgate-tools';
import DropDownButton from '../elements/DropDownButton.svelte';
import DataTypeEditor from './DataTypeEditor.svelte';
import {
editorAddConstraint,
editorDeleteConstraint,
editorModifyConstraint,
fullNameFromString,
fullNameToLabel,
fullNameToString,
} from 'dbgate-tools';
import TextField from '../forms/TextField.svelte';
import SelectField from '../forms/SelectField.svelte'; import SelectField from '../forms/SelectField.svelte';
import _ from 'lodash'; import _ from 'lodash';
import { useDatabaseInfo, useTableInfo } from '../utility/metadataLoaders'; import { useDatabaseInfo, useTableInfo } from '../utility/metadataLoaders';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import TargetApplicationSelect from '../elements/TargetApplicationSelect.svelte'; import TargetApplicationSelect, { saveDbToApp } from '../elements/TargetApplicationSelect.svelte';
import { apiCall } from '../utility/api';
export let conid; export let conid;
export let database; export let database;
@@ -34,6 +19,8 @@
export let pureName; export let pureName;
export let columnName; export let columnName;
let dstApp;
const dbInfo = useDatabaseInfo({ conid, database }); const dbInfo = useDatabaseInfo({ conid, database });
const tableInfo = useTableInfo({ conid, database, schemaName, pureName }); const tableInfo = useTableInfo({ conid, database, schemaName, pureName });
@@ -69,7 +56,10 @@
value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })} value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })}
isNative isNative
notSelected notSelected
options={[...($dbInfo?.tables || []), ...($dbInfo?.views || [])].map(tbl => ({ options={[
..._.sortBy($dbInfo?.tables || [], ['schemaName', 'pureName']),
..._.sortBy($dbInfo?.views || [], ['schemaName', 'pureName']),
].map(tbl => ({
label: fullNameToLabel(tbl), label: fullNameToLabel(tbl),
value: fullNameToString(tbl), value: fullNameToString(tbl),
}))} }))}
@@ -155,7 +145,7 @@
<div class="row"> <div class="row">
<div class="label col-3">Target application</div> <div class="label col-3">Target application</div>
<div class="col-9"> <div class="col-9">
<TargetApplicationSelect /> <TargetApplicationSelect bind:value={dstApp} />
</div> </div>
</div> </div>
</div> </div>
@@ -163,7 +153,16 @@
<svelte:fragment slot="footer"> <svelte:fragment slot="footer">
<FormSubmit <FormSubmit
value={'Save'} value={'Save'}
on:click={() => { on:click={async () => {
const appFolder = await saveDbToApp(conid, database, dstApp);
await apiCall('apps/save-vfk', {
appFolder,
schemaName,
pureName,
refSchemaName,
refTableName,
columns,
});
closeCurrentModal(); closeCurrentModal();
}} }}
/> />

View File

@@ -0,0 +1,83 @@
<script lang="ts" context="module">
const getCurrentEditor = () => getActiveComponent('JsonEditorTab');
registerFileCommands({
idPrefix: 'json',
category: 'Json',
getCurrentEditor,
folder: 'yaml',
format: 'text',
fileExtension: 'json',
toggleComment: true,
findReplace: true,
});
</script>
<script lang="ts">
import { getContext } from 'svelte';
import { registerFileCommands } from '../commands/stdCommands';
import AceEditor from '../query/AceEditor.svelte';
import useEditorData from '../query/useEditorData';
import invalidateCommands from '../commands/invalidateCommands';
import createActivator, { getActiveComponent } from '../utility/createActivator';
export let tabid;
const tabVisible: any = getContext('tabVisible');
export const activator = createActivator('JsonEditorTab', false);
let domEditor;
$: if ($tabVisible && domEditor) {
domEditor?.getEditor()?.focus();
}
export function getData() {
return $editorState.value || '';
}
export function toggleComment() {
domEditor.getEditor().execCommand('togglecomment');
}
export function find() {
domEditor.getEditor().execCommand('find');
}
export function replace() {
domEditor.getEditor().execCommand('replace');
}
export function getTabId() {
return tabid;
}
const { editorState, editorValue, setEditorData, saveToStorage } = useEditorData({ tabid });
function createMenu() {
return [
{ command: 'json.toggleComment' },
{ divider: true },
{ command: 'json.save' },
{ command: 'json.saveAs' },
{ divider: true },
{ command: 'json.find' },
{ command: 'json.replace' },
];
}
</script>
<AceEditor
value={$editorState.value || ''}
menu={createMenu()}
on:input={e => setEditorData(e.detail)}
on:focus={() => {
activator.activate();
invalidateCommands();
}}
bind:this={domEditor}
mode="json"
/>

View File

@@ -31,7 +31,7 @@
const tabVisible: any = getContext('tabVisible'); const tabVisible: any = getContext('tabVisible');
export const activator = createActivator('MarkdownEditorTab', false); export const activator = createActivator('YamlEditorTab', false);
let domEditor; let domEditor;
@@ -63,8 +63,6 @@
function createMenu() { function createMenu() {
return [ return [
{ command: 'yaml.preview' },
{ divider: true },
{ command: 'yaml.toggleComment' }, { command: 'yaml.toggleComment' },
{ divider: true }, { divider: true },
{ command: 'yaml.save' }, { command: 'yaml.save' },

View File

@@ -15,6 +15,7 @@ import * as FavoriteEditorTab from './FavoriteEditorTab.svelte';
import * as QueryDesignTab from './QueryDesignTab.svelte'; import * as QueryDesignTab from './QueryDesignTab.svelte';
import * as CommandListTab from './CommandListTab.svelte'; import * as CommandListTab from './CommandListTab.svelte';
import * as YamlEditorTab from './YamlEditorTab.svelte'; import * as YamlEditorTab from './YamlEditorTab.svelte';
import * as JsonEditorTab from './JsonEditorTab.svelte';
import * as CompareModelTab from './CompareModelTab.svelte'; import * as CompareModelTab from './CompareModelTab.svelte';
import * as JsonTab from './JsonTab.svelte'; import * as JsonTab from './JsonTab.svelte';
import * as ChangelogTab from './ChangelogTab.svelte'; import * as ChangelogTab from './ChangelogTab.svelte';
@@ -38,6 +39,7 @@ export default {
QueryDesignTab, QueryDesignTab,
CommandListTab, CommandListTab,
YamlEditorTab, YamlEditorTab,
JsonEditorTab,
CompareModelTab, CompareModelTab,
JsonTab, JsonTab,
ChangelogTab, ChangelogTab,