mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge pull request #751 from dbeaver/feat/save-lob
feat(plugin-data-spreadsheet-new): CB-1887 save LOB content
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
mutation getResultsetDataURL($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: ID!, $row: [ SQLResultRow! ]) {
|
||||
url: readLobValue(
|
||||
connectionId: $connectionId,
|
||||
contextId: $contextId,
|
||||
resultsId: $resultsId,
|
||||
lobColumnIndex: $lobColumnIndex,
|
||||
row: $row
|
||||
)
|
||||
}
|
||||
@@ -225,6 +225,7 @@ export interface DatabaseAuthModel {
|
||||
icon?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
properties: Array<ObjectPropertyInfo>;
|
||||
requiresLocalConfiguration?: Maybe<Scalars['Boolean']>;
|
||||
}
|
||||
|
||||
export interface DatabaseCatalog {
|
||||
@@ -328,6 +329,7 @@ export interface Mutation {
|
||||
/** @deprecated Field no longer supported */
|
||||
openConnection: ConnectionInfo;
|
||||
openSession: SessionInfo;
|
||||
readLobValue: Scalars['String'];
|
||||
refreshSessionConnections?: Maybe<Scalars['Boolean']>;
|
||||
setConnectionNavigatorSettings: ConnectionInfo;
|
||||
setUserConfigurationParameter: Scalars['Boolean'];
|
||||
@@ -459,6 +461,15 @@ export interface MutationOpenSessionArgs {
|
||||
}
|
||||
|
||||
|
||||
export interface MutationReadLobValueArgs {
|
||||
connectionId: Scalars['ID'];
|
||||
contextId: Scalars['ID'];
|
||||
lobColumnIndex: Scalars['ID'];
|
||||
resultsId: Scalars['ID'];
|
||||
row?: InputMaybe<Array<SqlResultRow>>;
|
||||
}
|
||||
|
||||
|
||||
export interface MutationSetConnectionNavigatorSettingsArgs {
|
||||
id: Scalars['ID'];
|
||||
settings: NavigatorSettingsInput;
|
||||
@@ -1980,6 +1991,17 @@ export type CloseResultMutationVariables = Exact<{
|
||||
|
||||
export type CloseResultMutation = { result: boolean };
|
||||
|
||||
export type GetResultsetDataUrlMutationVariables = Exact<{
|
||||
connectionId: Scalars['ID'];
|
||||
contextId: Scalars['ID'];
|
||||
resultsId: Scalars['ID'];
|
||||
lobColumnIndex: Scalars['ID'];
|
||||
row?: InputMaybe<Array<SqlResultRow> | SqlResultRow>;
|
||||
}>;
|
||||
|
||||
|
||||
export type GetResultsetDataUrlMutation = { url: string };
|
||||
|
||||
export type GetSqlExecuteTaskResultsMutationVariables = Exact<{
|
||||
taskId: Scalars['ID'];
|
||||
}>;
|
||||
@@ -3163,6 +3185,17 @@ export const CloseResultDocument = `
|
||||
)
|
||||
}
|
||||
`;
|
||||
export const GetResultsetDataUrlDocument = `
|
||||
mutation getResultsetDataURL($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: ID!, $row: [SQLResultRow!]) {
|
||||
url: readLobValue(
|
||||
connectionId: $connectionId
|
||||
contextId: $contextId
|
||||
resultsId: $resultsId
|
||||
lobColumnIndex: $lobColumnIndex
|
||||
row: $row
|
||||
)
|
||||
}
|
||||
`;
|
||||
export const GetSqlExecuteTaskResultsDocument = `
|
||||
mutation getSqlExecuteTaskResults($taskId: ID!) {
|
||||
result: asyncSqlExecuteResults(taskId: $taskId) {
|
||||
@@ -3742,6 +3775,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
|
||||
closeResult(variables: CloseResultMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise<CloseResultMutation> {
|
||||
return withWrapper(wrappedRequestHeaders => client.request<CloseResultMutation>(CloseResultDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'closeResult', 'mutation');
|
||||
},
|
||||
getResultsetDataURL(variables: GetResultsetDataUrlMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise<GetResultsetDataUrlMutation> {
|
||||
return withWrapper(wrappedRequestHeaders => client.request<GetResultsetDataUrlMutation>(GetResultsetDataUrlDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getResultsetDataURL', 'mutation');
|
||||
},
|
||||
getSqlExecuteTaskResults(variables: GetSqlExecuteTaskResultsMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise<GetSqlExecuteTaskResultsMutation> {
|
||||
return withWrapper(wrappedRequestHeaders => client.request<GetSqlExecuteTaskResultsMutation>(GetSqlExecuteTaskResultsDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getSqlExecuteTaskResults', 'mutation');
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export function download(blob: Blob, fileName: string): void {
|
||||
export function download(content: Blob | string, fileName = ''): void {
|
||||
const saveLink = document.createElement('a');
|
||||
|
||||
saveLink.tabIndex = -1;
|
||||
@@ -15,7 +15,7 @@ export function download(blob: Blob, fileName: string): void {
|
||||
document.body.appendChild(saveLink);
|
||||
|
||||
try {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const url = typeof content === 'string' ? content : URL.createObjectURL(content);
|
||||
saveLink.href = url;
|
||||
saveLink.onclick = () => requestAnimationFrame(() => URL.revokeObjectURL(url));
|
||||
} catch (e: any) {
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 { injectable } from '@cloudbeaver/core-di';
|
||||
import { download } from '@cloudbeaver/core-utils';
|
||||
import { DataViewerContentSaverService } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { DataGridContextMenuService } from './DataGridContextMenuService';
|
||||
|
||||
@injectable()
|
||||
export class DataGridContextMenuSaveContentService {
|
||||
private static readonly menuContentSaveToken = 'menuContentSave';
|
||||
|
||||
constructor(
|
||||
private readonly dataGridContextMenuService: DataGridContextMenuService,
|
||||
private readonly dataViewerContentSaverService: DataViewerContentSaverService
|
||||
) { }
|
||||
|
||||
getMenuContentSaveToken(): string {
|
||||
return DataGridContextMenuSaveContentService.menuContentSaveToken;
|
||||
}
|
||||
|
||||
register(): void {
|
||||
this.dataGridContextMenuService.add(
|
||||
this.dataGridContextMenuService.getMenuToken(),
|
||||
{
|
||||
id: this.getMenuContentSaveToken(),
|
||||
order: 4,
|
||||
title: 'ui_processing_save',
|
||||
icon: '/icons/save.svg',
|
||||
isPresent(context) {
|
||||
return context.contextType === DataGridContextMenuService.cellContext;
|
||||
},
|
||||
onClick: async context => {
|
||||
const url = await this.dataViewerContentSaverService.getElementValueURL(
|
||||
context.data.model,
|
||||
context.data.resultIndex,
|
||||
context.data.key
|
||||
);
|
||||
|
||||
if (url) {
|
||||
download(url);
|
||||
}
|
||||
},
|
||||
isHidden: context => !this.dataViewerContentSaverService.canSaveElementValue(
|
||||
context.data.model,
|
||||
context.data.resultIndex,
|
||||
context.data.key
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { DataPresentationService } from '@cloudbeaver/plugin-data-viewer';
|
||||
import { DataGridContextMenuCellEditingService } from './DataGrid/DataGridContextMenu/DataGridContextMenuCellEditingService';
|
||||
import { DataGridContextMenuFilterService } from './DataGrid/DataGridContextMenu/DataGridContextMenuFilter/DataGridContextMenuFilterService';
|
||||
import { DataGridContextMenuOrderService } from './DataGrid/DataGridContextMenu/DataGridContextMenuOrderService';
|
||||
import { DataGridContextMenuSaveContentService } from './DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService';
|
||||
import { DataGridContextMenuService } from './DataGrid/DataGridContextMenu/DataGridContextMenuService';
|
||||
import { DataGridSettingsService } from './DataGridSettingsService';
|
||||
import { SpreadsheetGrid } from './SpreadsheetGrid';
|
||||
@@ -21,12 +22,13 @@ import { SpreadsheetGrid } from './SpreadsheetGrid';
|
||||
@injectable()
|
||||
export class SpreadsheetBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private dataPresentationService: DataPresentationService,
|
||||
private dataGridSettingsService: DataGridSettingsService,
|
||||
private dataGridContextMenuSortingService: DataGridContextMenuOrderService,
|
||||
private dataGridContextMenuFilterService: DataGridContextMenuFilterService,
|
||||
private dataGridContextMenuCellEditingService: DataGridContextMenuCellEditingService,
|
||||
private dataGridContextMenuService: DataGridContextMenuService,
|
||||
private readonly dataPresentationService: DataPresentationService,
|
||||
private readonly dataGridSettingsService: DataGridSettingsService,
|
||||
private readonly dataGridContextMenuSortingService: DataGridContextMenuOrderService,
|
||||
private readonly dataGridContextMenuFilterService: DataGridContextMenuFilterService,
|
||||
private readonly dataGridContextMenuCellEditingService: DataGridContextMenuCellEditingService,
|
||||
private readonly dataGridContextMenuService: DataGridContextMenuService,
|
||||
private readonly dataGridContextMenuSaveContentService: DataGridContextMenuSaveContentService,
|
||||
exceptionsCatcherService: ExceptionsCatcherService
|
||||
) {
|
||||
super();
|
||||
@@ -45,6 +47,7 @@ export class SpreadsheetBootstrap extends Bootstrap {
|
||||
this.dataGridContextMenuSortingService.register();
|
||||
this.dataGridContextMenuFilterService.register();
|
||||
this.dataGridContextMenuCellEditingService.register();
|
||||
this.dataGridContextMenuSaveContentService.register();
|
||||
|
||||
this.dataGridContextMenuService.add(
|
||||
this.dataGridContextMenuService.getMenuToken(),
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { PluginManifest } from '@cloudbeaver/core-di';
|
||||
import { DataGridContextMenuCellEditingService } from './DataGrid/DataGridContextMenu/DataGridContextMenuCellEditingService';
|
||||
import { DataGridContextMenuFilterService } from './DataGrid/DataGridContextMenu/DataGridContextMenuFilter/DataGridContextMenuFilterService';
|
||||
import { DataGridContextMenuOrderService } from './DataGrid/DataGridContextMenu/DataGridContextMenuOrderService';
|
||||
import { DataGridContextMenuSaveContentService } from './DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService';
|
||||
import { DataGridContextMenuService } from './DataGrid/DataGridContextMenu/DataGridContextMenuService';
|
||||
import { DataGridSettingsService } from './DataGridSettingsService';
|
||||
import { LocaleService } from './LocaleService';
|
||||
@@ -26,5 +27,6 @@ export const manifest: PluginManifest = {
|
||||
DataGridContextMenuOrderService,
|
||||
DataGridContextMenuFilterService,
|
||||
DataGridContextMenuCellEditingService,
|
||||
DataGridContextMenuSaveContentService,
|
||||
],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 { injectable } from '@cloudbeaver/core-di';
|
||||
import { GraphQLService } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import type { IResultSetElementKey } from './DatabaseDataModel/Actions/ResultSet/IResultSetDataKey';
|
||||
import { isResultSetContentValue } from './DatabaseDataModel/Actions/ResultSet/isResultSetContentValue';
|
||||
import { ResultSetDataAction } from './DatabaseDataModel/Actions/ResultSet/ResultSetDataAction';
|
||||
import { ResultSetViewAction } from './DatabaseDataModel/Actions/ResultSet/ResultSetViewAction';
|
||||
import type { IDatabaseDataModel } from './DatabaseDataModel/IDatabaseDataModel';
|
||||
|
||||
const RESULT_VALUE_URL = '/api/sql-result-value';
|
||||
|
||||
@injectable()
|
||||
export class DataViewerContentSaverService {
|
||||
|
||||
constructor(
|
||||
private readonly graphQLService: GraphQLService
|
||||
) { }
|
||||
|
||||
canSaveElementValue(
|
||||
model: IDatabaseDataModel,
|
||||
resultIndex: number,
|
||||
element: IResultSetElementKey
|
||||
) {
|
||||
const view = model.source.getAction(resultIndex, ResultSetViewAction);
|
||||
const cellValue = view.getCellValue(element);
|
||||
|
||||
return isResultSetContentValue(cellValue);
|
||||
}
|
||||
|
||||
async getElementValueURL(
|
||||
model: IDatabaseDataModel,
|
||||
resultIndex: number,
|
||||
element: IResultSetElementKey
|
||||
) {
|
||||
const result = model.getResult(resultIndex);
|
||||
const data = model.source.getAction(resultIndex, ResultSetDataAction);
|
||||
const column = data.getColumn(element.column);
|
||||
const row = data.getRowValue(element.row);
|
||||
|
||||
if (!result?.id || !row || !column) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await this.graphQLService.sdk.getResultsetDataURL({
|
||||
resultsId: result.id,
|
||||
connectionId: result.connectionId,
|
||||
contextId: result.contextId,
|
||||
lobColumnIndex: String(column.position),
|
||||
row: {
|
||||
data: row,
|
||||
},
|
||||
});
|
||||
|
||||
return `${RESULT_VALUE_URL}/${response.url}`;
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ export * from './DatabaseDataModel/IDatabaseDataSource';
|
||||
export * from './DatabaseDataModel/IDatabaseResultSet';
|
||||
export * from './DatabaseDataModel/Order';
|
||||
export * from './DataViewerService';
|
||||
export * from './DataViewerContentSaverService';
|
||||
|
||||
// All Services and Components that is provided by this plugin should be exported here
|
||||
export * from './TableViewer/TableViewerStorageService';
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { PluginManifest } from '@cloudbeaver/core-di';
|
||||
|
||||
import { DataPresentationService } from './DataPresentationService';
|
||||
import { DataViewerBootstrap } from './DataViewerBootstrap';
|
||||
import { DataViewerContentSaverService } from './DataViewerContentSaverService';
|
||||
import { DataViewerDataChangeConfirmationService } from './DataViewerDataChangeConfirmationService';
|
||||
import { DataViewerService } from './DataViewerService';
|
||||
import { DataViewerSettingsService } from './DataViewerSettingsService';
|
||||
@@ -51,5 +52,6 @@ export const manifest: PluginManifest = {
|
||||
DataValuePanelBootstrap,
|
||||
DataViewerSettingsService,
|
||||
DataViewerService,
|
||||
DataViewerContentSaverService,
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user