mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 00:46:01 +00:00
query designer moved to premium
This commit is contained in:
@@ -351,8 +351,8 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
||||
text: `New ${driver?.collectionSingularLabel ?? 'collection/container'}`,
|
||||
},
|
||||
hasPermission(`dbops/query`) &&
|
||||
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleQueryDesigner, text: 'Design query' },
|
||||
driver?.databaseEngineTypes?.includes('sql') && {
|
||||
driver?.databaseEngineTypes?.includes('sql') && isProApp() && { onClick: handleQueryDesigner, text: 'Design query' },
|
||||
driver?.databaseEngineTypes?.includes('sql') && isProApp() && {
|
||||
onClick: handleNewPerspective,
|
||||
text: 'Design perspective query',
|
||||
},
|
||||
|
||||
@@ -76,12 +76,12 @@
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
isProApp() && {
|
||||
label: 'Design query',
|
||||
isQueryDesigner: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
{
|
||||
isProApp() && {
|
||||
label: 'Design perspective query',
|
||||
tab: 'PerspectiveTab',
|
||||
forceNewTab: true,
|
||||
@@ -170,11 +170,11 @@
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
isProApp() && {
|
||||
label: 'Design query',
|
||||
isQueryDesigner: true,
|
||||
},
|
||||
{
|
||||
isProApp() && {
|
||||
label: 'Design perspective query',
|
||||
tab: 'PerspectiveTab',
|
||||
forceNewTab: true,
|
||||
@@ -362,7 +362,7 @@
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
isProApp() && {
|
||||
label: 'Design perspective query',
|
||||
tab: 'PerspectiveTab',
|
||||
forceNewTab: true,
|
||||
@@ -917,6 +917,7 @@
|
||||
import { defaultDatabaseObjectAppObjectActions, matchDatabaseObjectAppObject } from './appObjectTools';
|
||||
import { getSupportedScriptTemplates } from '../utility/applyScriptTemplate';
|
||||
import { getBoolSettingsValue } from '../settings/settingsTools';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
|
||||
export let data;
|
||||
export let passProps;
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
<script lang="ts" context="module">
|
||||
const getCurrentEditor = () => getActiveComponent('PerspectiveTab');
|
||||
|
||||
registerCommand({
|
||||
id: 'perspective.refresh',
|
||||
category: 'Perspective',
|
||||
name: 'Refresh',
|
||||
keyText: 'F5 | CtrlOrCommand+R',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon reload',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().refresh(),
|
||||
});
|
||||
|
||||
registerFileCommands({
|
||||
idPrefix: 'perspective',
|
||||
category: 'Perspective',
|
||||
getCurrentEditor,
|
||||
folder: 'perspectives',
|
||||
format: 'json',
|
||||
fileExtension: 'perspective',
|
||||
|
||||
undoRedo: true,
|
||||
});
|
||||
|
||||
export const allowAddToFavorites = props => true;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createPerspectiveConfig, PerspectiveCache } from 'dbgate-datalib';
|
||||
|
||||
import PerspectiveView from '../perspectives/PerspectiveView.svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import { useConnectionInfo } from '../utility/metadataLoaders';
|
||||
import { extensions } from '../stores';
|
||||
import invalidateCommands from '../commands/invalidateCommands';
|
||||
import useEditorData from '../query/useEditorData';
|
||||
import createUndoReducer from '../utility/createUndoReducer';
|
||||
import { registerFileCommands } from '../commands/stdCommands';
|
||||
import _ from 'lodash';
|
||||
import ToolStripSaveButton from '../buttons/ToolStripSaveButton.svelte';
|
||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||
|
||||
export let tabid;
|
||||
export let conid;
|
||||
export let database;
|
||||
export let schemaName;
|
||||
export let pureName;
|
||||
|
||||
let isFormatError;
|
||||
|
||||
export const activator = createActivator('PerspectiveTab', true);
|
||||
|
||||
$: connection = useConnectionInfo({ conid });
|
||||
$: driver = findEngineDriver($connection, $extensions);
|
||||
|
||||
$: setEditorData($modelState.value);
|
||||
|
||||
export function getTabId() {
|
||||
return tabid;
|
||||
}
|
||||
|
||||
export function getData() {
|
||||
return $editorState.value || '';
|
||||
}
|
||||
|
||||
export function canUndo() {
|
||||
return $modelState.canUndo;
|
||||
}
|
||||
|
||||
export function undo() {
|
||||
dispatchModel({ type: 'undo' });
|
||||
invalidateCommands();
|
||||
}
|
||||
|
||||
export function canRedo() {
|
||||
return $modelState.canRedo;
|
||||
}
|
||||
|
||||
export function redo() {
|
||||
dispatchModel({ type: 'redo' });
|
||||
invalidateCommands();
|
||||
}
|
||||
|
||||
const { editorState, editorValue, setEditorData } = useEditorData({
|
||||
tabid,
|
||||
onInitialData: value => {
|
||||
if (!value.nodes) {
|
||||
isFormatError = true;
|
||||
} else {
|
||||
dispatchModel({ type: 'reset', value });
|
||||
}
|
||||
invalidateCommands();
|
||||
},
|
||||
});
|
||||
|
||||
const [modelState, dispatchModel] = createUndoReducer(
|
||||
createPerspectiveConfig(
|
||||
pureName
|
||||
? {
|
||||
schemaName,
|
||||
pureName,
|
||||
}
|
||||
: null
|
||||
)
|
||||
);
|
||||
|
||||
let cache = new PerspectiveCache();
|
||||
const loadedCounts = writable({});
|
||||
|
||||
export function refresh() {
|
||||
cache = new PerspectiveCache();
|
||||
// cache.clear();
|
||||
loadedCounts.set({});
|
||||
}
|
||||
</script>
|
||||
|
||||
<ToolStripContainer>
|
||||
{#if isFormatError}
|
||||
<ErrorInfo message="Invalid perspective format, please create new perspective" alignTop />
|
||||
{:else}
|
||||
<PerspectiveView
|
||||
{conid}
|
||||
{database}
|
||||
{driver}
|
||||
config={$modelState.value}
|
||||
setConfig={(value, reload) => {
|
||||
if (reload) {
|
||||
cache.clear();
|
||||
}
|
||||
dispatchModel({
|
||||
type: 'compute',
|
||||
// useMerge: skipUndoChain,
|
||||
compute: v => (_.isFunction(value) ? value(v) : value),
|
||||
});
|
||||
invalidateCommands();
|
||||
|
||||
// config.update(value);
|
||||
// loadedCounts.set({});
|
||||
}}
|
||||
{cache}
|
||||
{loadedCounts}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="toolstrip">
|
||||
<ToolStripCommandButton
|
||||
command="designer.arrange"
|
||||
buttonLabel={$modelState.value?.isArranged ? '(Arranged)' : 'Arrange'}
|
||||
/>
|
||||
<ToolStripCommandButton command="perspective.refresh" />
|
||||
<ToolStripCommandButton command="perspective.customJoin" />
|
||||
<ToolStripSaveButton idPrefix="perspective" />
|
||||
<ToolStripCommandButton command="perspective.undo" />
|
||||
<ToolStripCommandButton command="perspective.redo" />
|
||||
</svelte:fragment>
|
||||
</ToolStripContainer>
|
||||
@@ -1,324 +0,0 @@
|
||||
<script lang="ts" context="module">
|
||||
const getCurrentEditor = () => getActiveComponent('QueryDesignTab');
|
||||
|
||||
registerCommand({
|
||||
id: 'designer.openSql',
|
||||
category: 'Designer',
|
||||
icon: 'icon sql-file',
|
||||
name: 'Open SQL',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().openSql(),
|
||||
});
|
||||
|
||||
registerFileCommands({
|
||||
idPrefix: 'designer',
|
||||
category: 'Designer',
|
||||
getCurrentEditor,
|
||||
folder: 'query',
|
||||
format: 'json',
|
||||
fileExtension: 'qdesign',
|
||||
|
||||
execute: true,
|
||||
undoRedo: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import sqlFormatter from 'sql-formatter';
|
||||
|
||||
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
||||
import SqlEditor from '../query/SqlEditor.svelte';
|
||||
import useEditorData from '../query/useEditorData';
|
||||
import { extensions } from '../stores';
|
||||
import { changeTab } from '../utility/common';
|
||||
import { useConnectionInfo } from '../utility/metadataLoaders';
|
||||
import SocketMessageView from '../query/SocketMessageView.svelte';
|
||||
import useEffect from '../utility/useEffect';
|
||||
import ResultTabs from '../query/ResultTabs.svelte';
|
||||
import { registerFileCommands } from '../commands/stdCommands';
|
||||
import invalidateCommands from '../commands/invalidateCommands';
|
||||
import QueryDesigner from '../designer/QueryDesigner.svelte';
|
||||
import createUndoReducer from '../utility/createUndoReducer';
|
||||
import _ from 'lodash';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import { generateDesignedQuery } from '../designer/designerTools';
|
||||
import QueryDesignColumns from '../elements/QueryDesignColumns.svelte';
|
||||
import useTimerLabel from '../utility/useTimerLabel';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
import { apiCall, apiOff, apiOn } from '../utility/api';
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
import newQuery from '../query/newQuery';
|
||||
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||
import ToolStripExportButton, { createQuickExportHandlerRef } from '../buttons/ToolStripExportButton.svelte';
|
||||
import ToolStripSaveButton from '../buttons/ToolStripSaveButton.svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
|
||||
export let tabid;
|
||||
export let conid;
|
||||
export let database;
|
||||
export let initialArgs;
|
||||
|
||||
const timerLabel = useTimerLabel();
|
||||
|
||||
let busy = false;
|
||||
let executeNumber = 0;
|
||||
let visibleResultTabs = false;
|
||||
let sessionId = null;
|
||||
let sqlPreview = '';
|
||||
|
||||
export const activator = createActivator('QueryDesignTab', true);
|
||||
|
||||
$: connection = useConnectionInfo({ conid });
|
||||
$: engine = findEngineDriver($connection, $extensions);
|
||||
|
||||
$: effect = useEffect(() => {
|
||||
return onSession(sessionId);
|
||||
});
|
||||
function onSession(sid) {
|
||||
if (sid) {
|
||||
apiOn(`session-done-${sid}`, handleSessionDone);
|
||||
apiOn(`session-closed-${sid}`, handleSessionClosed);
|
||||
return () => {
|
||||
apiOff(`session-done-${sid}`, handleSessionDone);
|
||||
apiOff(`session-closed-${sid}`, handleSessionClosed);
|
||||
};
|
||||
}
|
||||
return () => {};
|
||||
}
|
||||
$: $effect;
|
||||
|
||||
$: {
|
||||
changeTab(tabid, tab => ({ ...tab, busy }));
|
||||
}
|
||||
|
||||
$: {
|
||||
busy;
|
||||
sessionId;
|
||||
$modelState;
|
||||
invalidateCommands();
|
||||
}
|
||||
|
||||
$: setEditorData($modelState.value);
|
||||
|
||||
$: generatePreview($modelState.value, engine);
|
||||
|
||||
let intervalId;
|
||||
|
||||
onMount(() => {
|
||||
intervalId = setInterval(() => {
|
||||
if (sessionId) {
|
||||
apiCall('sessions/ping', {
|
||||
sesid: sessionId,
|
||||
});
|
||||
}
|
||||
}, 15 * 1000);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
clearInterval(intervalId);
|
||||
});
|
||||
|
||||
export function canKill() {
|
||||
return !!sessionId;
|
||||
}
|
||||
|
||||
export function isBusy() {
|
||||
return busy;
|
||||
}
|
||||
|
||||
export function getTabId() {
|
||||
return tabid;
|
||||
}
|
||||
|
||||
export async function execute() {
|
||||
if (busy) return;
|
||||
executeNumber++;
|
||||
visibleResultTabs = true;
|
||||
|
||||
let sesid = sessionId;
|
||||
if (!sesid) {
|
||||
const resp = await apiCall('sessions/create', {
|
||||
conid,
|
||||
database,
|
||||
});
|
||||
sesid = resp.sesid;
|
||||
sessionId = sesid;
|
||||
}
|
||||
busy = true;
|
||||
timerLabel.start();
|
||||
await apiCall('sessions/execute-query', {
|
||||
sesid,
|
||||
sql: sqlPreview,
|
||||
});
|
||||
}
|
||||
|
||||
export async function kill() {
|
||||
await apiCall('sessions/kill', {
|
||||
sesid: sessionId,
|
||||
});
|
||||
sessionId = null;
|
||||
busy = false;
|
||||
timerLabel.stop();
|
||||
}
|
||||
|
||||
export function getData() {
|
||||
return $editorState.value || '';
|
||||
}
|
||||
|
||||
export function canUndo() {
|
||||
return $modelState.canUndo;
|
||||
}
|
||||
|
||||
export function undo() {
|
||||
dispatchModel({ type: 'undo' });
|
||||
}
|
||||
|
||||
export function canRedo() {
|
||||
return $modelState.canRedo;
|
||||
}
|
||||
|
||||
export function redo() {
|
||||
dispatchModel({ type: 'redo' });
|
||||
}
|
||||
|
||||
export function openSql() {
|
||||
newQuery({ initialData: sqlPreview });
|
||||
}
|
||||
|
||||
const generatePreview = (value, engine) => {
|
||||
if (!engine || !value) return;
|
||||
const sql = generateDesignedQuery(value, engine);
|
||||
sqlPreview = sqlFormatter.format(sql);
|
||||
};
|
||||
|
||||
const handleSessionDone = () => {
|
||||
busy = false;
|
||||
timerLabel.stop();
|
||||
};
|
||||
|
||||
const handleSessionClosed = () => {
|
||||
sessionId = null;
|
||||
handleSessionDone();
|
||||
};
|
||||
|
||||
const handleChange = (value, skipUndoChain) =>
|
||||
// @ts-ignore
|
||||
dispatchModel({
|
||||
type: 'compute',
|
||||
useMerge: skipUndoChain,
|
||||
compute: v => (_.isFunction(value) ? value(v) : value),
|
||||
});
|
||||
|
||||
const { editorState, editorValue, setEditorData } = useEditorData({
|
||||
tabid,
|
||||
onInitialData: value => {
|
||||
dispatchModel({ type: 'reset', value });
|
||||
},
|
||||
});
|
||||
|
||||
const [modelState, dispatchModel] = createUndoReducer({
|
||||
tables: [],
|
||||
references: [],
|
||||
columns: [],
|
||||
});
|
||||
// {visibleResultTabs && (
|
||||
// <TabPage label="Messages" key="messages">
|
||||
// <SocketMessagesView
|
||||
// eventName={sessionId ? `session-info-${sessionId}` : null}
|
||||
// executeNumber={executeNumber}
|
||||
// />
|
||||
// </TabPage>
|
||||
// )}
|
||||
function createMenu() {
|
||||
return [
|
||||
{ command: 'designer.execute' },
|
||||
{ command: 'designer.kill' },
|
||||
{ command: 'designer.openSql' },
|
||||
{ divider: true },
|
||||
{ command: 'designer.save' },
|
||||
{ command: 'designer.saveAs' },
|
||||
{ divider: true },
|
||||
{ command: 'designer.undo' },
|
||||
{ command: 'designer.redo' },
|
||||
{ divider: true },
|
||||
{
|
||||
text: `Remove duplicates: ${$editorState.value?.settings?.isDistinct ? 'YES' : 'NO'}`,
|
||||
onClick: () => {
|
||||
handleChange(
|
||||
{
|
||||
...$editorState.value,
|
||||
settings: {
|
||||
...$editorState.value?.settings,
|
||||
isDistinct: !$editorState.value?.settings?.isDistinct,
|
||||
},
|
||||
},
|
||||
false
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const quickExportHandlerRef = createQuickExportHandlerRef();
|
||||
</script>
|
||||
|
||||
<ToolStripContainer>
|
||||
<VerticalSplitter initialValue="70%">
|
||||
<svelte:fragment slot="1">
|
||||
<QueryDesigner
|
||||
value={$modelState.value || {}}
|
||||
{conid}
|
||||
{database}
|
||||
engine={$connection && $connection.engine}
|
||||
onChange={handleChange}
|
||||
menu={createMenu}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="2">
|
||||
<ResultTabs
|
||||
tabs={[
|
||||
{
|
||||
label: 'Columns',
|
||||
component: QueryDesignColumns,
|
||||
props: {
|
||||
value: $modelState.value || {},
|
||||
onChange: handleChange,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'SQL',
|
||||
component: SqlEditor,
|
||||
props: {
|
||||
engine: $connection && $connection.engine,
|
||||
readOnly: true,
|
||||
value: sqlPreview,
|
||||
},
|
||||
},
|
||||
visibleResultTabs && { label: 'Messages', slot: 0 },
|
||||
]}
|
||||
{sessionId}
|
||||
{executeNumber}
|
||||
>
|
||||
<svelte:fragment slot="0">
|
||||
<SocketMessageView
|
||||
eventName={sessionId ? `session-info-${sessionId}` : null}
|
||||
{executeNumber}
|
||||
showProcedure
|
||||
showLine
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</ResultTabs>
|
||||
</svelte:fragment>
|
||||
</VerticalSplitter>
|
||||
<svelte:fragment slot="toolstrip">
|
||||
<ToolStripCommandButton command="designer.execute" />
|
||||
<ToolStripCommandButton command="designer.kill" />
|
||||
<ToolStripCommandButton command="designer.openSql" />
|
||||
<ToolStripSaveButton idPrefix="designer" />
|
||||
<ToolStripExportButton command="jslTableGrid.export" {quickExportHandlerRef} label="Export result" />
|
||||
</svelte:fragment>
|
||||
</ToolStripContainer>
|
||||
@@ -11,7 +11,6 @@ import * as MarkdownEditorTab from './MarkdownEditorTab.svelte';
|
||||
import * as MarkdownViewTab from './MarkdownViewTab.svelte';
|
||||
import * as MarkdownPreviewTab from './MarkdownPreviewTab.svelte';
|
||||
import * as FavoriteEditorTab from './FavoriteEditorTab.svelte';
|
||||
import * as QueryDesignTab from './QueryDesignTab.svelte';
|
||||
import * as CommandListTab from './CommandListTab.svelte';
|
||||
import * as YamlEditorTab from './YamlEditorTab.svelte';
|
||||
import * as JsonEditorTab from './JsonEditorTab.svelte';
|
||||
@@ -23,7 +22,6 @@ import * as DbKeyDetailTab from './DbKeyDetailTab.svelte';
|
||||
import * as QueryDataTab from './QueryDataTab.svelte';
|
||||
import * as ConnectionTab from './ConnectionTab.svelte';
|
||||
import * as MapTab from './MapTab.svelte';
|
||||
import * as PerspectiveTab from './PerspectiveTab.svelte';
|
||||
import * as ServerSummaryTab from './ServerSummaryTab.svelte';
|
||||
import * as ProfilerTab from './ProfilerTab.svelte';
|
||||
import * as DataDuplicatorTab from './DataDuplicatorTab.svelte';
|
||||
@@ -46,7 +44,6 @@ export default {
|
||||
MarkdownViewTab,
|
||||
MarkdownPreviewTab,
|
||||
FavoriteEditorTab,
|
||||
QueryDesignTab,
|
||||
CommandListTab,
|
||||
YamlEditorTab,
|
||||
JsonEditorTab,
|
||||
@@ -58,7 +55,6 @@ export default {
|
||||
QueryDataTab,
|
||||
ConnectionTab,
|
||||
MapTab,
|
||||
PerspectiveTab,
|
||||
ServerSummaryTab,
|
||||
ProfilerTab,
|
||||
DataDuplicatorTab,
|
||||
|
||||
Reference in New Issue
Block a user