app commands works

This commit is contained in:
Jan Prochazka
2022-01-27 17:01:58 +01:00
parent 595c9424df
commit a76ba60272
10 changed files with 174 additions and 25 deletions

View File

@@ -1,7 +1,9 @@
const fs = require('fs-extra');
const _ = require('lodash');
const path = require('path');
const { appdir } = require('../utility/directories');
const socket = require('../utility/socket');
const connections = require('./connections');
module.exports = {
folders_meta: true,
@@ -50,6 +52,16 @@ module.exports = {
return [...refsType(), ...fileType('.command.sql', 'command.sql'), ...fileType('.query.sql', 'query.sql')];
},
async emitChangedDbApp(folder) {
for (const conn of await connections.list()) {
for (const db of conn.databases || []) {
if (db[`useApp:${folder}`]) {
socket.emitChanged(`db-apps-changed-${conn._id}-${db.name}`);
}
}
}
},
refreshFiles_meta: true,
async refreshFiles({ folder }) {
socket.emitChanged(`app-files-changed-${folder}`);
@@ -64,6 +76,7 @@ module.exports = {
async deleteFile({ folder, file, fileType }) {
await fs.unlink(path.join(appdir(), folder, `${file}.${fileType}`));
socket.emitChanged(`app-files-changed-${folder}`);
this.emitChangedDbApp(folder);
},
renameFile_meta: true,
@@ -73,6 +86,7 @@ module.exports = {
path.join(path.join(appdir(), folder), `${newFile}.${fileType}`)
);
socket.emitChanged(`app-files-changed-${folder}`);
this.emitChangedDbApp(folder);
},
renameFolder_meta: true,
@@ -97,4 +111,54 @@ module.exports = {
}
return `${name}${index}`;
},
getAppsForDb_meta: true,
async getAppsForDb({ conid, database }) {
const connection = await connections.get({ conid });
if (!connection) return [];
const db = (connection.databases || []).find(x => x.name == database);
const apps = [];
const res = [];
if (db) {
for (const key of _.keys(db || {})) {
if (key.startsWith('useApp:') && db[key]) {
apps.push(key.substring('useApp:'.length));
}
}
}
console.log('APPS', apps);
for (const folder of apps) {
res.push(await this.loadApp({ folder }));
}
return res;
},
loadApp_meta: true,
async loadApp({ folder }) {
const res = {
queries: [],
commands: [],
name: folder,
};
const dir = path.join(appdir(), folder);
if (await fs.exists(dir)) {
const files = await fs.readdir(dir);
async function processType(ext, field) {
for (const file of files) {
if (file.endsWith(ext)) {
res[field].push({
name: file.slice(0, -ext.length),
sql: await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }),
});
}
}
}
await processType('.command.sql', 'commands');
await processType('.query.sql', 'queries');
}
return res;
},
};

View File

@@ -178,6 +178,9 @@ module.exports = {
res = await this.datastore.insert(encrypted);
}
socket.emitChanged('connection-list-changed');
for (const db of connection.databases || []) {
socket.emitChanged(`db-apps-changed-${connection._id}-${db.name}`);
}
return res;
},
@@ -201,6 +204,7 @@ module.exports = {
}
const res = await this.datastore.update({ _id: conid }, { $set: { databases } });
socket.emitChanged('connection-list-changed');
socket.emitChanged(`db-apps-changed-${conid}-${database}`);
return res;
},

View File

@@ -7,6 +7,7 @@ const hasPermission = require('../utility/hasPermission');
const socket = require('../utility/socket');
const scheduler = require('./scheduler');
const getDiagramExport = require('../utility/getDiagramExport');
const apps = require('./apps');
function serialize(format, data) {
if (format == 'text') return data;
@@ -94,8 +95,10 @@ module.exports = {
socket.emitChanged(`archive-files-changed-${folder.substring('archive:'.length)}`);
return true;
} else if (folder.startsWith('app:')) {
await fs.writeFile(path.join(appdir(), folder.substring('app:'.length), file), serialize(format, data));
socket.emitChanged(`app-files-changed-${folder.substring('app:'.length)}`);
const app = folder.substring('app:'.length);
await fs.writeFile(path.join(appdir(), app, file), serialize(format, data));
socket.emitChanged(`app-files-changed-${app}`);
apps.emitChangedDbApp(folder);
return true;
} else {
if (!hasPermission(`files/${folder}/write`)) return false;

16
packages/types/appdefs.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
interface ApplicationCommand {
name: string;
sql: string;
}
interface ApplicationQuery {
name: string;
sql: string;
}
interface ApplicationDefinition {
name: string;
queries: ApplicationQuery[];
commands: ApplicationCommand[];
}

View File

@@ -34,7 +34,7 @@
'query.sql': 'img app-query',
};
function getAppIcon( data) {
function getAppIcon(data) {
return APP_ICONS[data.fileType];
}
</script>
@@ -42,24 +42,14 @@
<script lang="ts">
import _ from 'lodash';
import { filterName } from 'dbgate-tools';
import ImportExportModal from '../modals/ImportExportModal.svelte';
import { showModal } from '../modals/modalTools';
import { archiveFilesAsDataSheets, currentArchive, extensions, getCurrentDatabase } from '../stores';
import createQuickExportMenu from '../utility/createQuickExportMenu';
import { exportElectronFile } from '../utility/exportElectronFile';
import openNewTab from '../utility/openNewTab';
import AppObjectCore from './AppObjectCore.svelte';
import getConnectionLabel from '../utility/getConnectionLabel';
import InputTextModal from '../modals/InputTextModal.svelte';
import ConfirmModal from '../modals/ConfirmModal.svelte';
import {
isArchiveFileMarkedAsDataSheet,
markArchiveFileAsDataSheet,
markArchiveFileAsReadonly,
} from '../utility/archiveTools';
import { apiCall } from '../utility/api';
import { currentDatabase, currentDatabase } from '../stores';
export let data;
@@ -108,6 +98,7 @@
{ text: 'Delete', onClick: handleDelete },
{ text: 'Rename', onClick: handleRename },
data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile },
// data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile },
];
}
@@ -117,7 +108,7 @@
{...$$restProps}
{data}
title={data.fileLabel}
icon={getAppIcon( data)}
icon={getAppIcon(data)}
menu={createMenu}
on:click={handleClick}
/>

View File

@@ -7,7 +7,7 @@
import _ from 'lodash';
import { filterName } from 'dbgate-tools';
import { currentApplication } from '../stores';
import { currentApplication, currentDatabase } from '../stores';
import AppObjectCore from './AppObjectCore.svelte';
import { showModal } from '../modals/modalTools';
@@ -45,10 +45,25 @@
});
};
function setOnCurrentDb(value) {
apiCall('connections/update-database', {
conid: $currentDatabase?.connection?._id,
database: $currentDatabase?.name,
values: {
[`useApp:${data.name}`]: value,
},
});
}
function createMenu() {
return [
{ text: 'Delete', onClick: handleDelete },
{ text: 'Rename', onClick: handleRename },
$currentDatabase && [
{ text: 'Enable on current database', onClick: () => setOnCurrentDb(true) },
{ text: 'Disable on current database', onClick: () => setOnCurrentDb(false) },
],
];
}
</script>

View File

@@ -26,7 +26,7 @@
import { getDatabaseMenuItems } from './DatabaseAppObject.svelte';
import getElectron from '../utility/getElectron';
import getConnectionLabel from '../utility/getConnectionLabel';
import { getDatabaseList } from '../utility/metadataLoaders';
import { getDatabaseList, useDbApps } from '../utility/metadataLoaders';
import { getLocalStorage } from '../utility/storageCache';
import { apiCall } from '../utility/api';
@@ -97,7 +97,7 @@
value: 'newdb',
label: 'Database name',
onConfirm: name =>
apiCall('server-connections/create-database', {
apiCall('server-connections/create-database', {
conid: data._id,
name,
}),
@@ -153,7 +153,7 @@
],
data.singleDatabase && [
{ divider: true },
getDatabaseMenuItems(data, data.defaultDatabase, $extensions, $currentDatabase),
getDatabaseMenuItems(data, data.defaultDatabase, $extensions, $currentDatabase, $apps),
],
];
};
@@ -186,6 +186,8 @@
statusTitle = null;
}
}
$: apps = useDbApps({ conid: data?._id, database: data.defaultDatabase });
</script>
<AppObjectCore

View File

@@ -1,7 +1,7 @@
<script lang="ts" context="module">
export const extractKey = props => props.name;
export function getDatabaseMenuItems(connection, name, $extensions, $currentDatabase) {
export function getDatabaseMenuItems(connection, name, $extensions, $currentDatabase, $apps) {
const handleNewQuery = () => {
const tooltip = `${getConnectionLabel(connection)}\n${name}`;
openNewTab({
@@ -157,8 +157,20 @@
openJsonDocument(db, name);
};
async function handleConfirmSql(sql) {
const resp = await apiCall('database-connections/run-script', { conid: connection._id, database: name, sql });
const { errorMessage } = resp || {};
if (errorMessage) {
showModal(ErrorMessageModal, { title: 'Error when executing script', message: errorMessage });
} else {
showSnackbarSuccess('Saved to database');
}
}
const driver = findEngineDriver(connection, getExtensions());
const commands = _.flatten(($apps || []).map(x => x.commands || []));
return [
{ onClick: handleNewQuery, text: 'New query', isNewQuery: true },
!driver?.dialect?.nosql && { onClick: handleNewTable, text: 'New table' },
@@ -180,6 +192,20 @@
_.get($currentDatabase, 'connection._id') == _.get(connection, '_id') &&
_.get($currentDatabase, 'name') == name && { onClick: handleDisconnect, text: 'Disconnect' },
commands.length > 0 && [
{ divider: true },
commands.map((cmd: any) => ({
text: cmd.name,
onClick: () => {
showModal(ConfirmSqlModal, {
sql: cmd.sql,
onConfirm: () => handleConfirmSql(cmd.sql),
engine: driver.engine,
});
},
})),
],
];
}
</script>
@@ -207,18 +233,21 @@
import { showSnackbarSuccess } from '../utility/snackbar';
import { findEngineDriver } from 'dbgate-tools';
import InputTextModal from '../modals/InputTextModal.svelte';
import { getDatabaseInfo } from '../utility/metadataLoaders';
import { getDatabaseInfo, useDbApps } from '../utility/metadataLoaders';
import { openJsonDocument } from '../tabs/JsonTab.svelte';
import { apiCall } from '../utility/api';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
export let data;
export let passProps;
function createMenu() {
return getDatabaseMenuItems(data.connection, data.name, $extensions, $currentDatabase);
return getDatabaseMenuItems(data.connection, data.name, $extensions, $currentDatabase, $apps);
}
$: isPinned = !!$pinnedDatabases.find(x => x.name == data.name && x.connection?._id == data.connection?._id);
$: apps = useDbApps({ conid: data?.connection?._id, database: data?.name });
</script>
<AppObjectCore

View File

@@ -115,6 +115,12 @@ const appFilesLoader = ({ folder }) => ({
reloadTrigger: `app-files-changed-${folder}`,
});
const dbAppsLoader = ({ conid, database }) => ({
url: 'apps/get-apps-for-db',
params: { conid, database },
reloadTrigger: `db-apps-changed-${conid}-${database}`,
});
const serverStatusLoader = () => ({
url: 'server-connections/server-status',
params: {},
@@ -427,6 +433,13 @@ export function useAppFolders(args = {}) {
return useCore(appFoldersLoader, args);
}
export function getDbApps(args = {}) {
return getCore(dbAppsLoader, args);
}
export function useDbApps(args = {}) {
return useCore(dbAppsLoader, args);
}
export function getInstalledPlugins(args = {}) {
return getCore(installedPluginsLoader, args) || [];
}

View File

@@ -3,6 +3,14 @@
'command.sql': 'SQL commands',
'query.sql': 'SQL queries',
};
const COMMAND_TEMPLATE = `-- Write SQL command here
-- After save, you can execute it from database context menu, for all databases, which use this application
`;
const QUERY_TEMPLATE = `-- Write SQL query here
-- After save, you can view it in tables list, for all databases, which use this application
`;
</script>
<script lang="ts">
@@ -39,7 +47,7 @@
apiCall('apps/refresh-files', { folder });
};
function handleNewSqlFile(fileType, header) {
function handleNewSqlFile(fileType, header, initialData) {
showModal(InputTextModal, {
value: '',
label: 'New file name',
@@ -47,6 +55,7 @@
onConfirm: async file => {
newQuery({
title: file,
initialData,
// @ts-ignore
savedFile: file + '.' + fileType,
savedFolder: 'app:' + $currentApplication,
@@ -59,8 +68,11 @@
function createAddMenu() {
return [
{ text: 'New SQL command', onClick: () => handleNewSqlFile('command.sql', 'Create new SQL command') },
{ text: 'New query view', onClick: () => handleNewSqlFile('query.sql', 'Create new SQL query') },
{
text: 'New SQL command',
onClick: () => handleNewSqlFile('command.sql', 'Create new SQL command', COMMAND_TEMPLATE),
},
{ text: 'New query view', onClick: () => handleNewSqlFile('query.sql', 'Create new SQL query', QUERY_TEMPLATE) },
];
}
</script>