Skip to content

Commit

Permalink
CB-4289 pass file name based on context
Browse files Browse the repository at this point in the history
  • Loading branch information
devnaumov committed Jan 19, 2024
1 parent abc44a8 commit 0ebcdf0
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 49 deletions.
1 change: 1 addition & 0 deletions webapp/packages/core-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@ export * from './renamePathName';
export * from './removeLineBreak';
export * from './replaceSubstring';
export * from './formatNumber';
export * from './withTimestamp';
22 changes: 22 additions & 0 deletions webapp/packages/core-utils/src/withTimestamp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { withTimestamp } from './withTimestamp';

describe('withTimestamp', () => {
it('should generate a value with timestamp at the end', () => {
const mockDate = new Date('2020-09-09T14:13:20');
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);

const value = 'value';
const expectedValue = `${value} 2020-09-09 14-13-20`;

expect(withTimestamp(value)).toEqual(expectedValue);

spy.mockRestore();
});
});
14 changes: 14 additions & 0 deletions webapp/packages/core-utils/src/withTimestamp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/

export function withTimestamp(value: string) {
const now = new Date();
return `${value} ${now.toISOString().slice(0, 10)} ${('0' + now.getHours()).slice(-2)}-${('0' + now.getMinutes()).slice(-2)}-${(
'0' + now.getSeconds()
).slice(-2)}`;
}
14 changes: 14 additions & 0 deletions webapp/packages/plugin-data-export/src/ACTION_EXPORT_DATA.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createAction } from '@cloudbeaver/core-view';

export const ACTION_EXPORT_DATA = createAction('export-data', {
label: 'data_transfer_dialog_export',
tooltip: 'data_transfer_dialog_export',
icon: 'table-export',
});
124 changes: 76 additions & 48 deletions webapp/packages/plugin-data-export/src/DataExportMenuService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,99 @@
import { EAdminPermission } from '@cloudbeaver/core-authentication';
import { createConnectionParam, DATA_CONTEXT_CONNECTION } from '@cloudbeaver/core-connections';
import { injectable } from '@cloudbeaver/core-di';
import { CommonDialogService, IMenuContext } from '@cloudbeaver/core-dialogs';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
import { LocalizationService } from '@cloudbeaver/core-localization';
import { DATA_CONTEXT_NAV_NODE, EObjectFeature } from '@cloudbeaver/core-navigation-tree';
import { SessionPermissionsResource } from '@cloudbeaver/core-root';
import { ACTION_EXPORT, ActionService, DATA_CONTEXT_MENU_NESTED, MenuService } from '@cloudbeaver/core-view';
import { IDatabaseDataSource, IDataContainerOptions, ITableFooterMenuContext, TableFooterMenuService } from '@cloudbeaver/plugin-data-viewer';
import { withTimestamp } from '@cloudbeaver/core-utils';
import { ACTION_EXPORT, ActionService, DATA_CONTEXT_MENU, DATA_CONTEXT_MENU_NESTED, menuExtractItems, MenuService } from '@cloudbeaver/core-view';
import {
DATA_CONTEXT_DV_DDM,
DATA_CONTEXT_DV_DDM_RESULT_INDEX,
DATA_VIEWER_DATA_MODEL_ACTIONS_MENU,
IDatabaseDataSource,
IDataContainerOptions,
} from '@cloudbeaver/plugin-data-viewer';
import type { IDataQueryOptions } from '@cloudbeaver/plugin-sql-editor';

import { ACTION_EXPORT_DATA } from './ACTION_EXPORT_DATA';
import { DataExportSettingsService } from './DataExportSettingsService';
import { DataExportDialog } from './Dialog/DataExportDialog';

@injectable()
export class DataExportMenuService {
constructor(
private readonly commonDialogService: CommonDialogService,
private readonly tableFooterMenuService: TableFooterMenuService,
private readonly dataExportSettingsService: DataExportSettingsService,
private readonly actionService: ActionService,
private readonly menuService: MenuService,
private readonly sessionPermissionsResource: SessionPermissionsResource,
private readonly localizationService: LocalizationService,
) {}

register(): void {
this.tableFooterMenuService.registerMenuItem({
id: 'export ',
order: 5,
title: 'data_transfer_dialog_export',
tooltip: 'data_transfer_dialog_export_tooltip',
icon: 'table-export',
isPresent(context) {
return context.contextType === TableFooterMenuService.nodeContextType;
this.actionService.addHandler({
id: 'data-export-base-handler',
isActionApplicable(context, action) {
const menu = context.hasValue(DATA_CONTEXT_MENU, DATA_VIEWER_DATA_MODEL_ACTIONS_MENU);
const model = context.tryGet(DATA_CONTEXT_DV_DDM);
const resultIndex = context.tryGet(DATA_CONTEXT_DV_DDM_RESULT_INDEX);

if (!menu || !model || resultIndex === undefined) {
return false;
}

return [ACTION_EXPORT_DATA].includes(action);
},
isHidden: () => this.isDisabled(),
isDisabled(context) {
return (
context.data.model.isLoading() ||
context.data.model.isDisabled(context.data.resultIndex) ||
!context.data.model.getResult(context.data.resultIndex)
);
const model = context.get(DATA_CONTEXT_DV_DDM);
const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX);

return model.isLoading() || model.isDisabled(resultIndex) || !model.getResult(resultIndex);
},
handler: (context, action) => {
const model = context.get(DATA_CONTEXT_DV_DDM);
const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX);

if (action === ACTION_EXPORT_DATA) {
const result = model.getResult(resultIndex);

if (!result) {
throw new Error('Result must be provided');
}

const source = model.source as IDatabaseDataSource<IDataContainerOptions & IDataQueryOptions>;

if (!source.options) {
throw new Error('Source options must be provided');
}

this.commonDialogService.open(DataExportDialog, {
connectionKey: source.options.connectionKey,
contextId: source.executionContext?.context?.id,
containerNodePath: source.options.containerNodePath,
resultId: result.id,
name: model.name ?? undefined,
fileName: withTimestamp(model.name ?? this.localizationService.translate('data_transfer_dialog_title')),
query: source.options.query,
filter: {
constraints: source.options.constraints,
where: source.options.whereFilter,
},
});
}
},
});
this.menuService.addCreator({
menus: [DATA_VIEWER_DATA_MODEL_ACTIONS_MENU],
isApplicable: () => !this.isExportDisabled(),
getItems(context, items) {
return [...items, ACTION_EXPORT_DATA];
},
orderItems(context, items) {
const extracted = menuExtractItems(items, [ACTION_EXPORT_DATA]);
return [...items, ...extracted];
},
onClick: this.exportData.bind(this),
});

this.menuService.addCreator({
Expand All @@ -58,7 +111,7 @@ export class DataExportMenuService {
return false;
}

return !this.isDisabled() && context.has(DATA_CONTEXT_CONNECTION) && !context.has(DATA_CONTEXT_MENU_NESTED);
return !this.isExportDisabled() && context.has(DATA_CONTEXT_CONNECTION) && !context.has(DATA_CONTEXT_MENU_NESTED);
},
getItems: (context, items) => [...items, ACTION_EXPORT],
});
Expand All @@ -69,44 +122,19 @@ export class DataExportMenuService {
handler: async (context, action) => {
const node = context.get(DATA_CONTEXT_NAV_NODE);
const connection = context.get(DATA_CONTEXT_CONNECTION);
const fileName = withTimestamp(`${connection.name}${node?.name ? ` - ${node.name}` : ''}`);

this.commonDialogService.open(DataExportDialog, {
connectionKey: createConnectionParam(connection),
name: node?.name,
fileName,
containerNodePath: node?.id,
});
},
});
}

private exportData(context: IMenuContext<ITableFooterMenuContext>) {
const result = context.data.model.getResult(context.data.resultIndex);

if (!result) {
throw new Error('Result must be provided');
}

const source = context.data.model.source as IDatabaseDataSource<IDataContainerOptions & IDataQueryOptions>;

if (!source.options) {
throw new Error('Source options must be provided');
}

this.commonDialogService.open(DataExportDialog, {
connectionKey: source.options.connectionKey,
contextId: context.data.model.source.executionContext?.context?.id,
containerNodePath: source.options.containerNodePath,
resultId: result.id,
name: context.data.model.name ?? undefined,
query: source.options.query,
filter: {
constraints: source.options.constraints,
where: source.options.whereFilter,
},
});
}

private isDisabled() {
private isExportDisabled() {
if (this.sessionPermissionsResource.has(EAdminPermission.admin)) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ export function useDataExportDialog(context: IExportContext, onExport?: () => vo
processorId: this.processor.id,
processorProperties: this.processorProperties,
filter: this.context.filter,
outputSettings: this.outputSettings,
outputSettings: {
...this.outputSettings,
fileName: this.context.fileName,
},
});

this.onExport?.();
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-data-export/src/IExportContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface IExportContext {
resultId?: string | null;
containerNodePath?: string;
name?: string;
fileName?: string;
query?: string;
filter?: SqlDataFilter;
}

0 comments on commit 0ebcdf0

Please sign in to comment.