From c3c8e155c763f20b32e5ca88fdf714d3298f91a6 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Wed, 6 Apr 2022 20:18:52 +0200 Subject: [PATCH 1/5] feat(plugin-data-spreadsheet-new): CB-1887 save LOB content --- .../src/queries/grid/readLobValue.gql | 9 +++ webapp/packages/core-sdk/src/sdk.ts | 36 +++++++++ webapp/packages/core-utils/src/download.ts | 4 +- .../DataGridContextMenuSaveContentService.ts | 74 +++++++++++++++++++ .../src/SpreadsheetBootstrap.ts | 15 ++-- .../src/manifest.ts | 2 + 6 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 webapp/packages/core-sdk/src/queries/grid/readLobValue.gql create mode 100644 webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts diff --git a/webapp/packages/core-sdk/src/queries/grid/readLobValue.gql b/webapp/packages/core-sdk/src/queries/grid/readLobValue.gql new file mode 100644 index 0000000000..0864e3dc42 --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/grid/readLobValue.gql @@ -0,0 +1,9 @@ +mutation readLobValue($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: ID!, $row: [ SQLResultRow! ]) { + lobValue: readLobValue( + connectionId: $connectionId, + contextId: $contextId, + resultsId: $resultsId, + lobColumnIndex: $lobColumnIndex, + row: $row + ) +} \ No newline at end of file diff --git a/webapp/packages/core-sdk/src/sdk.ts b/webapp/packages/core-sdk/src/sdk.ts index b868bd1005..b6c48447c1 100644 --- a/webapp/packages/core-sdk/src/sdk.ts +++ b/webapp/packages/core-sdk/src/sdk.ts @@ -225,6 +225,7 @@ export interface DatabaseAuthModel { icon?: Maybe; id: Scalars['ID']; properties: Array; + requiresLocalConfiguration?: Maybe; } export interface DatabaseCatalog { @@ -328,6 +329,7 @@ export interface Mutation { /** @deprecated Field no longer supported */ openConnection: ConnectionInfo; openSession: SessionInfo; + readLobValue: Scalars['String']; refreshSessionConnections?: Maybe; 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>; +} + + export interface MutationSetConnectionNavigatorSettingsArgs { id: Scalars['ID']; settings: NavigatorSettingsInput; @@ -1994,6 +2005,17 @@ export type GetSqlExecutionPlanResultMutationVariables = Exact<{ export type GetSqlExecutionPlanResultMutation = { result: { query: string; nodes: Array<{ id: string; parentId?: string; kind: string; name?: string; type: string; condition?: string; description?: string; properties: Array<{ id?: string; category?: string; dataType?: string; description?: string; displayName?: string; length: ObjectPropertyLength; features: Array; value?: any; order: number }> }> } }; +export type ReadLobValueMutationVariables = Exact<{ + connectionId: Scalars['ID']; + contextId: Scalars['ID']; + resultsId: Scalars['ID']; + lobColumnIndex: Scalars['ID']; + row?: InputMaybe | SqlResultRow>; +}>; + + +export type ReadLobValueMutation = { lobValue: string }; + export type UpdateResultsDataBatchMutationVariables = Exact<{ connectionId: Scalars['ID']; contextId: Scalars['ID']; @@ -3232,6 +3254,17 @@ export const GetSqlExecutionPlanResultDocument = ` } } `; +export const ReadLobValueDocument = ` + mutation readLobValue($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: ID!, $row: [SQLResultRow!]) { + lobValue: readLobValue( + connectionId: $connectionId + contextId: $contextId + resultsId: $resultsId + lobColumnIndex: $lobColumnIndex + row: $row + ) +} + `; export const UpdateResultsDataBatchDocument = ` mutation updateResultsDataBatch($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $updatedRows: [SQLResultRow!], $deletedRows: [SQLResultRow!], $addedRows: [SQLResultRow!]) { result: updateResultsDataBatch( @@ -3748,6 +3781,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = getSqlExecutionPlanResult(variables: GetSqlExecutionPlanResultMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(GetSqlExecutionPlanResultDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getSqlExecutionPlanResult', 'mutation'); }, + readLobValue(variables: ReadLobValueMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { + return withWrapper(wrappedRequestHeaders => client.request(ReadLobValueDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'readLobValue', 'mutation'); + }, updateResultsDataBatch(variables: UpdateResultsDataBatchMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(UpdateResultsDataBatchDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'updateResultsDataBatch', 'mutation'); }, diff --git a/webapp/packages/core-utils/src/download.ts b/webapp/packages/core-utils/src/download.ts index efa34a6b4f..00ee26f9fc 100644 --- a/webapp/packages/core-utils/src/download.ts +++ b/webapp/packages/core-utils/src/download.ts @@ -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: string): 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) { diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts new file mode 100644 index 0000000000..704649da8d --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts @@ -0,0 +1,74 @@ +/* + * 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 { download } from '@cloudbeaver/core-utils'; +import { isResultSetContentValue, ResultSetDataAction, ResultSetViewAction } from '@cloudbeaver/plugin-data-viewer'; + +import { DataGridContextMenuService } from './DataGridContextMenuService'; + +const RESULT_VALUE_URL = '/api/sql-result-value'; + +@injectable() +export class DataGridContextMenuSaveContentService { + private static readonly menuContentSaveToken = 'menuContentSave'; + + constructor( + private readonly dataGridContextMenuService: DataGridContextMenuService, + private readonly graphQLService: GraphQLService + ) { } + + 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 result = context.data.model.getResult(context.data.resultIndex); + const data = context.data.model.source.getAction(context.data.resultIndex, ResultSetDataAction); + const column = data.getColumn(context.data.key.column); + const row = data.getRowValue(context.data.key.row); + + if (!result?.id || !row || !column) { + return; + } + + const response = await this.graphQLService.sdk.readLobValue({ + resultsId: result.id, + connectionId: result.connectionId, + contextId: result.contextId, + lobColumnIndex: String(column.position), + row: { + data: row, + }, + }); + + const url = `${RESULT_VALUE_URL}/${response.lobValue}`; + download(url, ''); + }, + isHidden(context) { + const view = context.data.model.source.getAction(context.data.resultIndex, ResultSetViewAction); + const cellValue = view.getCellValue(context.data.key); + + return !isResultSetContentValue(cellValue); + }, + } + ); + } +} diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts b/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts index 3fbaadcd0f..8a43cf048b 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/SpreadsheetBootstrap.ts @@ -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(), diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/manifest.ts b/webapp/packages/plugin-data-spreadsheet-new/src/manifest.ts index e244cd6692..0431a12da3 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/manifest.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/manifest.ts @@ -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, ], }; From 2724fc5905890c22f2809911d528bbab9ac6ed13 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Wed, 6 Apr 2022 22:36:31 +0200 Subject: [PATCH 2/5] fix(plugin-data-spreadsheet-new): CB-1887 sdk naming --- .../src/queries/grid/getResultsetDataURL.gql | 9 ++++ .../src/queries/grid/readLobValue.gql | 9 ---- webapp/packages/core-sdk/src/sdk.ts | 50 +++++++++---------- .../DataGridContextMenuSaveContentService.ts | 4 +- 4 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql delete mode 100644 webapp/packages/core-sdk/src/queries/grid/readLobValue.gql diff --git a/webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql b/webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql new file mode 100644 index 0000000000..ba9a4ad4c7 --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql @@ -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 + ) +} \ No newline at end of file diff --git a/webapp/packages/core-sdk/src/queries/grid/readLobValue.gql b/webapp/packages/core-sdk/src/queries/grid/readLobValue.gql deleted file mode 100644 index 0864e3dc42..0000000000 --- a/webapp/packages/core-sdk/src/queries/grid/readLobValue.gql +++ /dev/null @@ -1,9 +0,0 @@ -mutation readLobValue($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: ID!, $row: [ SQLResultRow! ]) { - lobValue: readLobValue( - connectionId: $connectionId, - contextId: $contextId, - resultsId: $resultsId, - lobColumnIndex: $lobColumnIndex, - row: $row - ) -} \ No newline at end of file diff --git a/webapp/packages/core-sdk/src/sdk.ts b/webapp/packages/core-sdk/src/sdk.ts index b6c48447c1..51fb5e282f 100644 --- a/webapp/packages/core-sdk/src/sdk.ts +++ b/webapp/packages/core-sdk/src/sdk.ts @@ -1991,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 | SqlResultRow>; +}>; + + +export type GetResultsetDataUrlMutation = { url: string }; + export type GetSqlExecuteTaskResultsMutationVariables = Exact<{ taskId: Scalars['ID']; }>; @@ -2005,17 +2016,6 @@ export type GetSqlExecutionPlanResultMutationVariables = Exact<{ export type GetSqlExecutionPlanResultMutation = { result: { query: string; nodes: Array<{ id: string; parentId?: string; kind: string; name?: string; type: string; condition?: string; description?: string; properties: Array<{ id?: string; category?: string; dataType?: string; description?: string; displayName?: string; length: ObjectPropertyLength; features: Array; value?: any; order: number }> }> } }; -export type ReadLobValueMutationVariables = Exact<{ - connectionId: Scalars['ID']; - contextId: Scalars['ID']; - resultsId: Scalars['ID']; - lobColumnIndex: Scalars['ID']; - row?: InputMaybe | SqlResultRow>; -}>; - - -export type ReadLobValueMutation = { lobValue: string }; - export type UpdateResultsDataBatchMutationVariables = Exact<{ connectionId: Scalars['ID']; contextId: Scalars['ID']; @@ -3185,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) { @@ -3254,17 +3265,6 @@ export const GetSqlExecutionPlanResultDocument = ` } } `; -export const ReadLobValueDocument = ` - mutation readLobValue($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: ID!, $row: [SQLResultRow!]) { - lobValue: readLobValue( - connectionId: $connectionId - contextId: $contextId - resultsId: $resultsId - lobColumnIndex: $lobColumnIndex - row: $row - ) -} - `; export const UpdateResultsDataBatchDocument = ` mutation updateResultsDataBatch($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $updatedRows: [SQLResultRow!], $deletedRows: [SQLResultRow!], $addedRows: [SQLResultRow!]) { result: updateResultsDataBatch( @@ -3775,15 +3775,15 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = closeResult(variables: CloseResultMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(CloseResultDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'closeResult', 'mutation'); }, + getResultsetDataURL(variables: GetResultsetDataUrlMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { + return withWrapper(wrappedRequestHeaders => client.request(GetResultsetDataUrlDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getResultsetDataURL', 'mutation'); + }, getSqlExecuteTaskResults(variables: GetSqlExecuteTaskResultsMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(GetSqlExecuteTaskResultsDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getSqlExecuteTaskResults', 'mutation'); }, getSqlExecutionPlanResult(variables: GetSqlExecutionPlanResultMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(GetSqlExecutionPlanResultDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getSqlExecutionPlanResult', 'mutation'); }, - readLobValue(variables: ReadLobValueMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { - return withWrapper(wrappedRequestHeaders => client.request(ReadLobValueDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'readLobValue', 'mutation'); - }, updateResultsDataBatch(variables: UpdateResultsDataBatchMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(UpdateResultsDataBatchDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'updateResultsDataBatch', 'mutation'); }, diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts index 704649da8d..e0a2d8485a 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts @@ -49,7 +49,7 @@ export class DataGridContextMenuSaveContentService { return; } - const response = await this.graphQLService.sdk.readLobValue({ + const response = await this.graphQLService.sdk.getResultsetDataURL({ resultsId: result.id, connectionId: result.connectionId, contextId: result.contextId, @@ -59,7 +59,7 @@ export class DataGridContextMenuSaveContentService { }, }); - const url = `${RESULT_VALUE_URL}/${response.lobValue}`; + const url = `${RESULT_VALUE_URL}/${response.url}`; download(url, ''); }, isHidden(context) { From 9cb432d138da8ef094a9081bd1b222166ca75b80 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Wed, 6 Apr 2022 23:26:48 +0200 Subject: [PATCH 3/5] fix(plugin-data-spreadsheet-new): CB-1887 add common element content value saver service --- .../DataGridContextMenuSaveContentService.ts | 45 ++++--------- .../src/DataViewerContentSaverService.ts | 66 +++++++++++++++++++ .../packages/plugin-data-viewer/src/index.ts | 1 + .../plugin-data-viewer/src/manifest.ts | 2 + 4 files changed, 81 insertions(+), 33 deletions(-) create mode 100644 webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts index e0a2d8485a..23fea22ef2 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts @@ -7,21 +7,17 @@ */ import { injectable } from '@cloudbeaver/core-di'; -import { GraphQLService } from '@cloudbeaver/core-sdk'; -import { download } from '@cloudbeaver/core-utils'; -import { isResultSetContentValue, ResultSetDataAction, ResultSetViewAction } from '@cloudbeaver/plugin-data-viewer'; +import { DataViewerContentSaverService } from '@cloudbeaver/plugin-data-viewer'; import { DataGridContextMenuService } from './DataGridContextMenuService'; -const RESULT_VALUE_URL = '/api/sql-result-value'; - @injectable() export class DataGridContextMenuSaveContentService { private static readonly menuContentSaveToken = 'menuContentSave'; constructor( private readonly dataGridContextMenuService: DataGridContextMenuService, - private readonly graphQLService: GraphQLService + private readonly dataViewerContentSaverService: DataViewerContentSaverService ) { } getMenuContentSaveToken(): string { @@ -40,34 +36,17 @@ export class DataGridContextMenuSaveContentService { return context.contextType === DataGridContextMenuService.cellContext; }, onClick: async context => { - const result = context.data.model.getResult(context.data.resultIndex); - const data = context.data.model.source.getAction(context.data.resultIndex, ResultSetDataAction); - const column = data.getColumn(context.data.key.column); - const row = data.getRowValue(context.data.key.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, - }, - }); - - const url = `${RESULT_VALUE_URL}/${response.url}`; - download(url, ''); - }, - isHidden(context) { - const view = context.data.model.source.getAction(context.data.resultIndex, ResultSetViewAction); - const cellValue = view.getCellValue(context.data.key); - - return !isResultSetContentValue(cellValue); + await this.dataViewerContentSaverService.saveElementValue( + context.data.model, + context.data.resultIndex, + context.data.key + ); }, + isHidden: context => !this.dataViewerContentSaverService.canSaveElementValue( + context.data.model, + context.data.resultIndex, + context.data.key + ), } ); } diff --git a/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts b/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts new file mode 100644 index 0000000000..9c3072febf --- /dev/null +++ b/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts @@ -0,0 +1,66 @@ +/* + * 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 { download } from '@cloudbeaver/core-utils'; + +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 saveElementValue( + 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, + }, + }); + + const url = `${RESULT_VALUE_URL}/${response.url}`; + download(url, ''); + } +} \ No newline at end of file diff --git a/webapp/packages/plugin-data-viewer/src/index.ts b/webapp/packages/plugin-data-viewer/src/index.ts index c916c7a0b4..e3982fc049 100644 --- a/webapp/packages/plugin-data-viewer/src/index.ts +++ b/webapp/packages/plugin-data-viewer/src/index.ts @@ -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'; diff --git a/webapp/packages/plugin-data-viewer/src/manifest.ts b/webapp/packages/plugin-data-viewer/src/manifest.ts index 641fb6fdeb..c06b7471e8 100644 --- a/webapp/packages/plugin-data-viewer/src/manifest.ts +++ b/webapp/packages/plugin-data-viewer/src/manifest.ts @@ -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, ], }; From 14ebe7633899883c33d2101c37b2e293002ee987 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Thu, 7 Apr 2022 11:52:30 +0200 Subject: [PATCH 4/5] fix(plugin-data-spreadsheet-new): CB-1887 return url instead of download it --- webapp/packages/core-utils/src/download.ts | 2 +- .../DataGridContextMenuSaveContentService.ts | 7 ++++++- .../src/DataViewerContentSaverService.ts | 5 ++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/webapp/packages/core-utils/src/download.ts b/webapp/packages/core-utils/src/download.ts index 00ee26f9fc..0cd0dee02c 100644 --- a/webapp/packages/core-utils/src/download.ts +++ b/webapp/packages/core-utils/src/download.ts @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -export function download(content: Blob | string, fileName: string): void { +export function download(content: Blob | string, fileName = ''): void { const saveLink = document.createElement('a'); saveLink.tabIndex = -1; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts index 23fea22ef2..cbf7bccdee 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuSaveContentService.ts @@ -7,6 +7,7 @@ */ import { injectable } from '@cloudbeaver/core-di'; +import { download } from '@cloudbeaver/core-utils'; import { DataViewerContentSaverService } from '@cloudbeaver/plugin-data-viewer'; import { DataGridContextMenuService } from './DataGridContextMenuService'; @@ -36,11 +37,15 @@ export class DataGridContextMenuSaveContentService { return context.contextType === DataGridContextMenuService.cellContext; }, onClick: async context => { - await this.dataViewerContentSaverService.saveElementValue( + 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, diff --git a/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts b/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts index 9c3072febf..83ff89bb9f 100644 --- a/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts +++ b/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts @@ -36,7 +36,7 @@ export class DataViewerContentSaverService { return isResultSetContentValue(cellValue); } - async saveElementValue( + async getElementValueURL( model: IDatabaseDataModel, resultIndex: number, element: IResultSetElementKey @@ -60,7 +60,6 @@ export class DataViewerContentSaverService { }, }); - const url = `${RESULT_VALUE_URL}/${response.url}`; - download(url, ''); + return `${RESULT_VALUE_URL}/${response.url}`; } } \ No newline at end of file From ce782bc7c3a057afab5002827b7c37986dc96b75 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Thu, 7 Apr 2022 11:56:08 +0200 Subject: [PATCH 5/5] fix(plugin-data-spreadsheet-new): CB-1887 remove unused imports --- .../plugin-data-viewer/src/DataViewerContentSaverService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts b/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts index 83ff89bb9f..44ecec8dac 100644 --- a/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts +++ b/webapp/packages/plugin-data-viewer/src/DataViewerContentSaverService.ts @@ -8,7 +8,6 @@ import { injectable } from '@cloudbeaver/core-di'; import { GraphQLService } from '@cloudbeaver/core-sdk'; -import { download } from '@cloudbeaver/core-utils'; import type { IResultSetElementKey } from './DatabaseDataModel/Actions/ResultSet/IResultSetDataKey'; import { isResultSetContentValue } from './DatabaseDataModel/Actions/ResultSet/isResultSetContentValue';