Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class ConnectionDialogsService {
id: 'closeConnection',
isPresent(context) {
return context.contextType === NavigationTreeContextMenuService.nodeContextType
&& Boolean((context.data as NodeWithParent)?.object?.features?.includes(EObjectFeature.dataSource));
&& !!context.data.object?.features?.includes(EObjectFeature.dataSource);
},
title: 'Disconnect',
onClick: (context: IMenuContext<NodeWithParent>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,13 @@ export class NodeManagerUtils {
return connectionNodeId.replace('database://', '');
}

static nodeIdToConnectionId(nodeId: string): string {
const matches = nodeId.match(/database:\/\/(.*?)(|\/.*)$/);
if (!matches) {
throw new Error('Not database object');
}

return matches[1];
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { IMenuContext } from './IMenuContext';
export interface IContextMenuItem<T> extends IMenuItemOptions {
onClick?: (context: IMenuContext<T>) => void;
// if isPresent is false menu item will not be included in resulting context menu
isPresent: (context: IMenuContext<any>) => boolean;
isPresent: (context: IMenuContext<T>) => boolean;
isDisabled?: (context: IMenuContext<T>) => boolean;
// When the item is present in menu it can be hidden based on certain conditions
isHidden?: (context: IMenuContext<T>) => boolean;
Expand Down
29 changes: 28 additions & 1 deletion webapp/packages/data-export-plugin/src/DataExportMenuService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
* you may not use this file except in compliance with the License.
*/

import {
NavigationTreeContextMenuService, NodeManagerUtils, NodeWithParent, EObjectFeature
} from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import { IContextMenuItem, IMenuContext, CommonDialogService } from '@dbeaver/core/dialogs';
import {
IContextMenuItem, IMenuContext, CommonDialogService, ContextMenuService
} from '@dbeaver/core/dialogs';
import { TableFooterMenuService, TableViewerModel } from '@dbeaver/data-viewer-plugin';

import { DataExportDialog } from './Dialog/DataExportDialog';
Expand All @@ -18,6 +23,7 @@ export class DataExportMenuService {
constructor(
private commonDialogService: CommonDialogService,
private tableFooterMenuService: TableFooterMenuService,
private contextMenuService: ContextMenuService,
) { }

register() {
Expand All @@ -32,6 +38,27 @@ export class DataExportMenuService {
onClick: this.exportData.bind(this),
};
this.tableFooterMenuService.registerMenuItem(exportData);

this.contextMenuService.addMenuItem<NodeWithParent>(
this.contextMenuService.getRootMenuToken(),
{
id: 'export',
isPresent(context) {
return context.contextType === NavigationTreeContextMenuService.nodeContextType
&& !!context.data.object?.features?.includes(EObjectFeature.dataContainer);
},
order: 2,
title: 'Export',
onClick: (context) => {
const node = context.data;
const connectionId = NodeManagerUtils.nodeIdToConnectionId(node.id);
this.commonDialogService.open(DataExportDialog, {
connectionId,
containerNodePath: node.id,
});
},
}
);
}

private exportData(context: IMenuContext<TableViewerModel>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

import { observable, computed } from 'mobx';

import { ErrorDetailsDialog } from '@dbeaver/core/app';
import { IProperty } from '@dbeaver/core/blocks';
import { injectable, IInitializableController } from '@dbeaver/core/di';
import { injectable, IInitializableController, IDestructibleController } from '@dbeaver/core/di';
import { CommonDialogService } from '@dbeaver/core/dialogs';
import { NotificationService } from '@dbeaver/core/eventsLog';
import { DataTransferProcessorInfo } from '@dbeaver/core/sdk';
import { DataTransferProcessorInfo, GQLErrorCatcher } from '@dbeaver/core/sdk';

import { DataExportService } from '../DataExportService';
import { IExportContext } from '../IExportContext';
Expand All @@ -22,7 +24,7 @@ export enum DataExportStep {
}

@injectable()
export class DataExportController implements IInitializableController {
export class DataExportController implements IInitializableController, IDestructibleController {
@observable step = DataExportStep.DataTransferProcessor
get isLoading() {
return this.dataExportService.processors.isLoading();
Expand All @@ -35,18 +37,22 @@ export class DataExportController implements IInitializableController {
.from(
this.dataExportService.processors.data.values()
)
.sort((a, b) => this.sortProcessors(a, b));
.sort(sortProcessors);
}

@observable processorProperties: any = {}
@observable properties: IProperty[] = []

readonly error = new GQLErrorCatcher();

private context!: IExportContext;
private close!: () => void;
private isDistructed = false;

constructor(
private dataExportService: DataExportService,
private notificationService: NotificationService
private notificationService: NotificationService,
private commonDialogService: CommonDialogService
) { }

init(context: IExportContext, close: () => void) {
Expand All @@ -55,6 +61,10 @@ export class DataExportController implements IInitializableController {
this.loadProcessors();
}

destruct(): void {
this.isDistructed = true;
}

prepareExport = async () => {
if (!this.processor || this.isExporting) {
return;
Expand All @@ -71,7 +81,9 @@ export class DataExportController implements IInitializableController {
);
this.close();
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t export');
if (!this.error.catch(exception) || this.isDistructed) {
this.notificationService.logException(exception, 'Can\'t export');
}
} finally {
this.isExporting = false;
close();
Expand Down Expand Up @@ -100,6 +112,13 @@ export class DataExportController implements IInitializableController {
this.processorProperties = {};

this.step = DataExportStep.Configure;
this.error.clear();
}

showDetails = () => {
if (this.error.exception) {
this.commonDialogService.open(ErrorDetailsDialog, this.error.exception);
}
}

private async loadProcessors() {
Expand All @@ -109,13 +128,13 @@ export class DataExportController implements IInitializableController {
this.notificationService.logException(exception, 'Can\'t load data export processors');
}
}
}

private sortProcessors(processorA: DataTransferProcessorInfo, processorB: DataTransferProcessorInfo): number {
if (processorA.order === processorB.order)
{
return (processorA.name || '').localeCompare((processorB.name || ''));
}

return processorA.order - processorB.order;
function sortProcessors(processorA: DataTransferProcessorInfo, processorB: DataTransferProcessorInfo): number {
if (processorA.order === processorB.order)
{
return (processorA.name || '').localeCompare((processorB.name || ''));
}

return processorA.order - processorB.order;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export const DataExportDialog: DialogComponent<IExportContext, null> = observer(
processor={controller.processor}
properties={controller.properties}
processorProperties={controller.processorProperties}
error={controller.error}
isExporting={controller.isExporting}
onShowDetails={controller.showDetails}
onBack={() => controller.setStep(DataExportStep.DataTransferProcessor)}
onClose={props.rejectDialog}
onExport={controller.prepareExport}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,49 @@
import { observer } from 'mobx-react';
import styled, { css } from 'reshadow';

import { IProperty, PropertiesTable } from '@dbeaver/core/blocks';
import { IProperty, PropertiesTable, ErrorMessage } from '@dbeaver/core/blocks';
import { CommonDialogWrapper } from '@dbeaver/core/dialogs';
import { useTranslate } from '@dbeaver/core/localization';
import { DataTransferProcessorInfo } from '@dbeaver/core/sdk';
import { DataTransferProcessorInfo, GQLErrorCatcher } from '@dbeaver/core/sdk';
import { composes, useStyles } from '@dbeaver/core/theming';

import { ProcessorConfigureDialogFooter } from './ProcessorConfigureDialogFooter';

const styles = css`
CommonDialogWrapper {
max-height: 500px;
min-height: 500px;
}
PropertiesTable {
flex: 1;
}
message {
margin: auto;
}
`;
const styles = composes(
css`
Tab {
composes: theme-ripple theme-background-secondary theme-text-on-secondary from global;
}
ErrorMessage {
composes: theme-background-secondary from global;
}
`,
css`
CommonDialogWrapper {
max-height: 500px;
min-height: 500px;
}
PropertiesTable {
flex: 1;
}
message {
margin: auto;
}
ErrorMessage {
position: sticky;
bottom: 0;
padding: 8px 24px;
}
`
);

type ProcessorSelectDialogProps = {
processor: DataTransferProcessorInfo;
properties: IProperty[];
processorProperties: any;
error: GQLErrorCatcher;
isExporting: boolean;
onShowDetails(): void;
onClose(): void;
onBack(): void;
onExport(): void;
Expand All @@ -44,15 +62,17 @@ export const ProcessorConfigureDialog = observer(
processor,
properties,
processorProperties,
error,
isExporting,
onShowDetails,
onClose,
onBack,
onExport,
}: ProcessorSelectDialogProps) {
const translate = useTranslate();
const title = `${translate('data_transfer_dialog_configuration_title')} (${processor.name})`;

return styled(styles)(
return styled(useStyles(styles))(
<CommonDialogWrapper
title={title}
noBodyPadding
Expand All @@ -66,11 +86,15 @@ export const ProcessorConfigureDialog = observer(
}
onReject={onClose}
>
{isExporting && <message as="div">{translate('data_transfer_dialog_preparation')}</message>}
{!isExporting && (
<PropertiesTable
properties={properties}
propertiesState={processorProperties}
<PropertiesTable
properties={properties}
propertiesState={processorProperties}
/>
{error.responseMessage && (
<ErrorMessage
text={error.responseMessage}
hasDetails={error.hasDetails}
onShowDetails={onShowDetails}
/>
)}
</CommonDialogWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const ProcessorSelectDialog = observer(
>
<export-object as="div">
{context.sourceName ? translate('data_transfer_exporting_sql') : `${translate('data_transfer_exporting_table')} ${node?.name}`}
<pre>{context.sourceName}</pre>
<pre title={context.sourceName}>{context.sourceName}</pre>
</export-object>
{isLoading && <Loader />}
{!isLoading && <ExportProcessorList processors={processors} onSelect={onSelect}/>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const ExportNotification = observer(function ExportNotification({
<notification-body as="div">
<source-name as="div">
{controller.sourceName}
<pre>{controller.task?.context.sourceName}</pre>
<pre title={controller.task?.context.sourceName}>{controller.task?.context.sourceName}</pre>
</source-name>
<actions as="div">
{controller.isSuccess && (
Expand Down