diff --git a/package.json b/package.json index 2ff061c5f..f3f391254 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "4.7.2", + "version": "4.7.3-beta.1", "name": "dbgate-all", "workspaces": [ "packages/*", diff --git a/packages/api/src/controllers/files.js b/packages/api/src/controllers/files.js index 14ea526a5..6d82c2e29 100644 --- a/packages/api/src/controllers/files.js +++ b/packages/api/src/controllers/files.js @@ -141,8 +141,8 @@ module.exports = { }, generateUploadsFile_meta: true, - async generateUploadsFile() { - const fileName = `${uuidv1()}.html`; + async generateUploadsFile({ extension }) { + const fileName = `${uuidv1()}.${extension || 'html'}`; return { fileName, filePath: path.join(uploadsdir(), fileName), diff --git a/packages/web/src/appobj/ArchiveFileAppObject.svelte b/packages/web/src/appobj/ArchiveFileAppObject.svelte index 60eee5e9c..02c92fbdf 100644 --- a/packages/web/src/appobj/ArchiveFileAppObject.svelte +++ b/packages/web/src/appobj/ArchiveFileAppObject.svelte @@ -70,7 +70,7 @@ import { archiveFilesAsDataSheets, currentArchive, extensions, getCurrentDatabase } from '../stores'; import createQuickExportMenu from '../utility/createQuickExportMenu'; - import { exportElectronFile } from '../utility/exportElectronFile'; + import { exportQuickExportFile } from '../utility/exportFileTools'; import openNewTab from '../utility/openNewTab'; import AppObjectCore from './AppObjectCore.svelte'; import getConnectionLabel from '../utility/getConnectionLabel'; @@ -169,31 +169,33 @@ { text: 'Delete', onClick: handleDelete }, { text: 'Rename', onClick: handleRename }, data.fileType == 'jsonl' && - createQuickExportMenu($extensions, fmt => async () => { - exportElectronFile( - data.fileName, - { - functionName: 'archiveReader', - props: { - fileName: data.fileName, - folderName: data.folderName, + createQuickExportMenu( + fmt => async () => { + exportQuickExportFile( + data.fileName, + { + functionName: 'archiveReader', + props: { + fileName: data.fileName, + folderName: data.folderName, + }, }, + fmt + ); + }, + { + text: 'Export', + onClick: () => { + showModal(ImportExportModal, { + initialValues: { + sourceStorageType: 'archive', + sourceArchiveFolder: data.folderName, + sourceList: [data.fileName], + }, + }); }, - fmt - ); - }), - data.fileType == 'jsonl' && { - text: 'Export', - onClick: () => { - showModal(ImportExportModal, { - initialValues: { - sourceStorageType: 'archive', - sourceArchiveFolder: data.folderName, - sourceList: [data.fileName], - }, - }); - }, - }, + } + ), data.fileType.endsWith('.sql') && { text: 'Open SQL', onClick: handleOpenSqlFile }, data.fileType.endsWith('.yaml') && { text: 'Open YAML', onClick: handleOpenYamlFile }, ]; diff --git a/packages/web/src/appobj/DatabaseObjectAppObject.svelte b/packages/web/src/appobj/DatabaseObjectAppObject.svelte index 8e264d051..c7bb2c01f 100644 --- a/packages/web/src/appobj/DatabaseObjectAppObject.svelte +++ b/packages/web/src/appobj/DatabaseObjectAppObject.svelte @@ -1,6 +1,6 @@ -{#if quickExportHandlerRef && electron} - +{#if quickExportHandlerRef} + {:else} {/if} diff --git a/packages/web/src/charts/ChartCore.svelte b/packages/web/src/charts/ChartCore.svelte index 17efe256d..1fc893003 100644 --- a/packages/web/src/charts/ChartCore.svelte +++ b/packages/web/src/charts/ChartCore.svelte @@ -28,7 +28,7 @@ import contextMenu, { getContextMenu, registerMenu } from '../utility/contextMenu'; import createActivator, { getActiveComponent } from '../utility/createActivator'; - import { saveFileToDisk } from '../utility/exportElectronFile'; + import { saveFileToDisk } from '../utility/exportFileTools'; export let data; export let title; diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 143ea85d6..454a59704 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -26,7 +26,7 @@ import { removeLocalStorage } from '../utility/storageCache'; import { showSnackbarSuccess } from '../utility/snackbar'; import { apiCall } from '../utility/api'; import runCommand from './runCommand'; -import { openWebLink } from '../utility/exportElectronFile'; +import { openWebLink } from '../utility/exportFileTools'; import { getSettings } from '../utility/metadataLoaders'; // function themeCommand(theme: ThemeDefinition) { diff --git a/packages/web/src/datagrid/CollectionDataGridCore.svelte b/packages/web/src/datagrid/CollectionDataGridCore.svelte index dde0c38c3..a3f798ba2 100644 --- a/packages/web/src/datagrid/CollectionDataGridCore.svelte +++ b/packages/web/src/datagrid/CollectionDataGridCore.svelte @@ -109,6 +109,7 @@ import { parseFilter } from 'dbgate-filterparser'; import { scriptToSql } from 'dbgate-sqltree'; import _ from 'lodash'; + import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte'; import registerCommand from '../commands/registerCommand'; import ErrorInfo from '../elements/ErrorInfo.svelte'; import ConfirmNoSqlModal from '../modals/ConfirmNoSqlModal.svelte'; @@ -121,7 +122,7 @@ import { registerMenu } from '../utility/contextMenu'; import createActivator, { getActiveComponent } from '../utility/createActivator'; import createQuickExportMenu from '../utility/createQuickExportMenu'; - import { exportElectronFile } from '../utility/exportElectronFile'; + import { exportQuickExportFile } from '../utility/exportFileTools'; import { getConnectionInfo } from '../utility/metadataLoaders'; import openNewTab from '../utility/openNewTab'; import ChangeSetGrider from './ChangeSetGrider'; @@ -193,31 +194,30 @@ ); } - registerMenu( - { command: 'collectionDataGrid.openQuery', tag: 'export' }, - { - ...createQuickExportMenu($extensions, fmt => async () => { - const coninfo = await getConnectionInfo({ conid }); - exportElectronFile( - pureName || 'Data', - { - functionName: 'queryReader', - props: { - connection: { - ..._.omit(coninfo, ['_id', 'displayName']), - database, - }, - sql: getExportQuery(), - }, + const quickExportHandler = fmt => async () => { + const coninfo = await getConnectionInfo({ conid }); + exportQuickExportFile( + pureName || 'Data', + { + functionName: 'queryReader', + props: { + connection: { + ..._.omit(coninfo, ['_id', 'displayName']), + database, }, - fmt - ); - }), - tag: 'export', - }, + sql: getExportQuery(), + }, + }, + fmt + ); + }; - { command: 'collectionDataGrid.export', tag: 'export' } - ); + registerQuickExportHandler(quickExportHandler); + + registerMenu({ command: 'collectionDataGrid.openQuery', tag: 'export' }, () => ({ + ...createQuickExportMenu(quickExportHandler, { command: 'collectionDataGrid.export' }), + tag: 'export', + })); async () => { const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/); if (archiveMatch) { - exportElectronFile( + exportQuickExportFile( archiveMatch[2], { functionName: 'archiveReader', @@ -116,7 +116,7 @@ fmt ); } else { - exportElectronFile( + exportQuickExportFile( 'Query', { functionName: 'jslDataReader', @@ -130,13 +130,10 @@ }; registerQuickExportHandler(quickExportHandler); - registerMenu( - { - ...createQuickExportMenu($extensions, quickExportHandler), - tag: 'export', - }, - { command: 'jslTableGrid.export', tag: 'export' } - ); + registerMenu(() => ({ + ...createQuickExportMenu(quickExportHandler, { command: 'jslTableGrid.export' }), + tag: 'export', + })); import _ from 'lodash'; import { getContext } from 'svelte'; -import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte'; + import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte'; import registerCommand from '../commands/registerCommand'; import ImportExportModal from '../modals/ImportExportModal.svelte'; import { showModal } from '../modals/modalTools'; - import { extensions } from '../stores'; import { apiCall } from '../utility/api'; import { registerMenu } from '../utility/contextMenu'; import createActivator, { getActiveComponent } from '../utility/createActivator'; import createQuickExportMenu from '../utility/createQuickExportMenu'; - import { exportElectronFile } from '../utility/exportElectronFile'; + import { exportQuickExportFile } from '../utility/exportFileTools'; import { getConnectionInfo } from '../utility/metadataLoaders'; import openNewTab from '../utility/openNewTab'; import ChangeSetGrider from './ChangeSetGrider'; @@ -182,7 +181,7 @@ import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.sve const quickExportHandler = fmt => async () => { const coninfo = await getConnectionInfo({ conid }); - exportElectronFile( + exportQuickExportFile( pureName || 'Data', { functionName: 'queryReader', @@ -202,11 +201,10 @@ import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.sve registerMenu( { command: 'sqlDataGrid.openActiveChart', tag: 'chart' }, { command: 'sqlDataGrid.openQuery', tag: 'export' }, - { - ...createQuickExportMenu($extensions, quickExportHandler), + () => ({ + ...createQuickExportMenu(quickExportHandler, { command: 'sqlDataGrid.export' }), tag: 'export', - }, - { command: 'sqlDataGrid.export', tag: 'export' } + }) ); diff --git a/packages/web/src/designer/Designer.svelte b/packages/web/src/designer/Designer.svelte index f7f13ff44..815338cc4 100644 --- a/packages/web/src/designer/Designer.svelte +++ b/packages/web/src/designer/Designer.svelte @@ -39,7 +39,7 @@ import registerCommand from '../commands/registerCommand'; import createActivator, { getActiveComponent } from '../utility/createActivator'; import { GraphDefinition, GraphLayout } from './GraphLayout'; - import { saveFileToDisk } from '../utility/exportElectronFile'; + import { saveFileToDisk } from '../utility/exportFileTools'; import { apiCall } from '../utility/api'; import moveDrag from '../utility/moveDrag'; import { rectanglesHaveIntersection } from './designerMath'; diff --git a/packages/web/src/elements/Link.svelte b/packages/web/src/elements/Link.svelte index b3ce281cf..ed9d16d5d 100644 --- a/packages/web/src/elements/Link.svelte +++ b/packages/web/src/elements/Link.svelte @@ -1,5 +1,5 @@ @@ -64,15 +79,23 @@ value={fullNameToString({ pureName: refTableName, schemaName: refSchemaName })} isNative notSelected - options={tableList.map(tbl => ({ - label: fullNameToLabel(tbl), - value: fullNameToString(tbl), - }))} + options={tableOptions} on:change={e => { if (e.detail) { const name = fullNameFromString(e.detail); refTableName = name.pureName; refSchemaName = name.schemaName; + if (columns?.length == 1) { + const table = $dbInfo?.tables?.find(x => x.pureName == refTableName && x.schemaName == refSchemaName); + if (table?.primaryKey?.columns?.length == 1) { + columns = [ + { + ...columns[0], + refColumnName: table.primaryKey.columns[0].columnName, + }, + ]; + } + } } }} /> diff --git a/packages/web/src/tabs/CompareModelTab.svelte b/packages/web/src/tabs/CompareModelTab.svelte index 6df13b66b..4bec9a75f 100644 --- a/packages/web/src/tabs/CompareModelTab.svelte +++ b/packages/web/src/tabs/CompareModelTab.svelte @@ -158,7 +158,7 @@ import { changeTab } from '../utility/common'; import contextMenu, { getContextMenu, registerMenu } from '../utility/contextMenu'; import createActivator, { getActiveComponent } from '../utility/createActivator'; - import { saveFileToDisk } from '../utility/exportElectronFile'; + import { saveFileToDisk } from '../utility/exportFileTools'; import { useArchiveFolders, useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders'; import resolveApi from '../utility/resolveApi'; import { showSnackbarSuccess } from '../utility/snackbar'; diff --git a/packages/web/src/tabs/TableDataTab.svelte b/packages/web/src/tabs/TableDataTab.svelte index 764c8a336..35ade672e 100644 --- a/packages/web/src/tabs/TableDataTab.svelte +++ b/packages/web/src/tabs/TableDataTab.svelte @@ -50,14 +50,11 @@ import { showSnackbarSuccess } from '../utility/snackbar'; import StatusBarTabItem from '../widgets/StatusBarTabItem.svelte'; import openNewTab from '../utility/openNewTab'; - import { getBoolSettingsValue } from '../settings/settingsTools'; import { setContext } from 'svelte'; import { apiCall } from '../utility/api'; import { getLocalStorage, setLocalStorage } from '../utility/storageCache'; import ToolStripContainer from '../buttons/ToolStripContainer.svelte'; import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte'; - import ToolStripDropDownButton from '../buttons/ToolStripDropDownButton.svelte'; - import { createQuickExportMenuItems } from '../utility/createQuickExportMenu'; import ToolStripExportButton, { createQuickExportHandlerRef } from '../buttons/ToolStripExportButton.svelte'; export let tabid; diff --git a/packages/web/src/utility/createQuickExportMenu.ts b/packages/web/src/utility/createQuickExportMenu.ts index 5872ade54..b304881c9 100644 --- a/packages/web/src/utility/createQuickExportMenu.ts +++ b/packages/web/src/utility/createQuickExportMenu.ts @@ -1,30 +1,27 @@ -import { ExtensionsDirectory, QuickExportDefinition } from 'dbgate-types'; -import getElectron from './getElectron'; +import { QuickExportDefinition } from 'dbgate-types'; +import { getExtensions } from '../stores'; -export function createQuickExportMenuItems( - extensions: ExtensionsDirectory, - handler: (fmt: QuickExportDefinition) => Function -) { - const electron = getElectron(); - if (!electron) { - return null; - } - return extensions.quickExports.map(fmt => ({ - text: fmt.label, - onClick: handler(fmt), - })); +export function createQuickExportMenuItems(handler: (fmt: QuickExportDefinition) => Function, advancedExportMenuItem) { + const extensions = getExtensions(); + return [ + ...extensions.quickExports.map(fmt => ({ + text: fmt.label, + onClick: handler(fmt), + })), + { divider: true }, + { + text: 'More...', + ...advancedExportMenuItem, + }, + ]; } export default function createQuickExportMenu( - extensions: ExtensionsDirectory, - handler: (fmt: QuickExportDefinition) => Function + handler: (fmt: QuickExportDefinition) => Function, + advancedExportMenuItem ) { - const electron = getElectron(); - if (!electron) { - return { _skip: true }; - } return { - text: 'Quick export', - submenu: createQuickExportMenuItems(extensions, handler), + text: 'Export', + submenu: createQuickExportMenuItems(handler, advancedExportMenuItem), }; } diff --git a/packages/web/src/utility/exportElectronFile.ts b/packages/web/src/utility/exportFileTools.ts similarity index 78% rename from packages/web/src/utility/exportElectronFile.ts rename to packages/web/src/utility/exportFileTools.ts index 2bb288d5c..91cf2c798 100644 --- a/packages/web/src/utility/exportElectronFile.ts +++ b/packages/web/src/utility/exportFileTools.ts @@ -4,16 +4,23 @@ import { showSnackbar, showSnackbarInfo, showSnackbarError, closeSnackbar } from import resolveApi from './resolveApi'; import { apiCall, apiOff, apiOn } from './api'; -export async function exportElectronFile(dataName, reader, format) { +export async function exportQuickExportFile(dataName, reader, format) { const electron = getElectron(); - const filters = [{ name: format.label, extensions: [format.extension] }]; - const filePath = await electron.showSaveDialog({ - filters, - defaultPath: `${dataName}.${format.extension}`, - properties: ['showOverwriteConfirmation'], - }); - if (!filePath) return; + let filePath; + let pureFileName; + if (electron) { + const filters = [{ name: format.label, extensions: [format.extension] }]; + filePath = electron.showSaveDialog({ + filters, + defaultPath: `${dataName}.${format.extension}`, + properties: ['showOverwriteConfirmation'], + }); + } else { + const resp = await apiCall('files/generate-uploads-file', { extension: format.extension }); + filePath = resp.filePath; + pureFileName = resp.fileName; + } const script = new ScriptWriter(); @@ -50,6 +57,10 @@ export async function exportElectronFile(dataName, reader, format) { apiOff(`runner-done-${runid}`, handleRunnerDone); if (isCanceled) showSnackbarError(`Export ${dataName} canceled`); else showSnackbarInfo(`Export ${dataName} finished`); + + if (!electron) { + window.open(`${resolveApi()}/uploads/get?file=${pureFileName}`, '_blank'); + } } apiOn(`runner-done-${runid}`, handleRunnerDone); diff --git a/packages/web/src/widgets/TabsPanel.svelte b/packages/web/src/widgets/TabsPanel.svelte index b82fedd82..17220e4bf 100644 --- a/packages/web/src/widgets/TabsPanel.svelte +++ b/packages/web/src/widgets/TabsPanel.svelte @@ -387,7 +387,7 @@ draggingDbGroupTarget = null; }} > - + {tabGroup.tabDbName}