mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
refactor(plugin-data-viewer): DatabaseDataModel
CB-287
This commit is contained in:
@@ -13,7 +13,7 @@ import { injectable } from '@cloudbeaver/core-di';
|
||||
import {
|
||||
IContextMenuItem, IMenuContext, CommonDialogService, ContextMenuService
|
||||
} from '@cloudbeaver/core-dialogs';
|
||||
import { TableFooterMenuService, TableViewerModel } from '@cloudbeaver/plugin-data-viewer';
|
||||
import { TableFooterMenuService, DataModelWrapper } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { DataExportDialog } from './Dialog/DataExportDialog';
|
||||
|
||||
@@ -26,7 +26,7 @@ export class DataExportMenuService {
|
||||
) { }
|
||||
|
||||
register() {
|
||||
const exportData: IContextMenuItem<TableViewerModel> = {
|
||||
const exportData: IContextMenuItem<DataModelWrapper> = {
|
||||
id: 'export ',
|
||||
isPresent(context) {
|
||||
return context.contextType === TableFooterMenuService.nodeContextType;
|
||||
@@ -60,13 +60,13 @@ export class DataExportMenuService {
|
||||
);
|
||||
}
|
||||
|
||||
private exportData(context: IMenuContext<TableViewerModel>) {
|
||||
private exportData(context: IMenuContext<DataModelWrapper>) {
|
||||
this.commonDialogService.open(DataExportDialog, {
|
||||
connectionId: context.data.connectionId,
|
||||
contextId: context.data.executionContext?.contextId,
|
||||
containerNodePath: context.data.containerNodePath,
|
||||
resultId: context.data.resultId,
|
||||
sourceName: context.data.sourceName,
|
||||
connectionId: context.data.deprecatedModel.connectionId,
|
||||
contextId: context.data.deprecatedModel.executionContext?.contextId,
|
||||
containerNodePath: context.data.deprecatedModel.containerNodePath,
|
||||
resultId: context.data.deprecatedModel.resultId,
|
||||
sourceName: context.data.deprecatedModel.sourceName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from '@ag-grid-community/core';
|
||||
import { injectable, IInitializableController, IDestructibleController } from '@cloudbeaver/core-di';
|
||||
import {
|
||||
TableViewerModel, SortModel, AgGridRow, IAgGridCol, AccessMode
|
||||
TableViewerModel, SortModel, AgGridRow, IAgGridCol, DatabaseDataAccessMode
|
||||
} from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { AgGridContext } from './AgGridContext';
|
||||
@@ -45,7 +45,7 @@ export class AgGridTableController implements IInitializableController, IDestruc
|
||||
*/
|
||||
private readonly context: AgGridContext = {
|
||||
selection: this.selection,
|
||||
isReadonly: () => this.gridModel.access === AccessMode.Readonly,
|
||||
isReadonly: () => this.gridModel.access === DatabaseDataAccessMode.Readonly,
|
||||
isCellEdited: this.isCellEdited.bind(this),
|
||||
editCellValue: this.editCellValue.bind(this),
|
||||
revertCellValue: this.revertCellValue.bind(this),
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { TableViewerModel } from '@cloudbeaver/plugin-data-viewer';
|
||||
import { DataModelWrapper } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { AgGridTable } from './AgGridTable/AgGridTable';
|
||||
|
||||
type Props = {
|
||||
tableModel: TableViewerModel;
|
||||
tableModel: DataModelWrapper;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@ export function Spreadsheet({
|
||||
className,
|
||||
}: Props) {
|
||||
|
||||
return <AgGridTable tableModel={tableModel} className={className} />;
|
||||
return <AgGridTable tableModel={tableModel.deprecatedModel} className={className} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { GraphQLService, SqlDataFilterConstraint } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { DatabaseDataSource } from './DatabaseDataModel/DatabaseDataSource';
|
||||
import { IDatabaseDataResult } from './DatabaseDataModel/IDatabaseDataResult';
|
||||
import { DataUpdate } from './DatabaseDataModel/IDatabaseDataSource';
|
||||
import { IExecutionContext } from './IExecutionContext';
|
||||
|
||||
export interface IDataContainerOptions {
|
||||
containerNodePath: string;
|
||||
connectionId: string;
|
||||
whereFilter: string;
|
||||
constraints: SqlDataFilterConstraint[];
|
||||
}
|
||||
|
||||
export interface IDataContainerResult extends IDatabaseDataResult {
|
||||
|
||||
}
|
||||
|
||||
export class ContainerDataSource extends DatabaseDataSource<IDataContainerOptions, IDataContainerResult> {
|
||||
private executionContext: IExecutionContext | null;
|
||||
|
||||
constructor(private graphQLService: GraphQLService) {
|
||||
super();
|
||||
this.executionContext = null;
|
||||
}
|
||||
|
||||
async request(
|
||||
prevResults: IDataContainerResult[]
|
||||
): Promise<IDataContainerResult[]> {
|
||||
if (!this.options?.containerNodePath) {
|
||||
throw new Error('containerNodePath must be provided for table');
|
||||
}
|
||||
const executionContext = await this.ensureContextCreated();
|
||||
|
||||
const { readDataFromContainer } = await this.graphQLService.sdk.readDataFromContainer({
|
||||
connectionId: executionContext.connectionId,
|
||||
contextId: executionContext.contextId,
|
||||
containerNodePath: this.options.containerNodePath,
|
||||
filter: {
|
||||
offset: this.offset,
|
||||
limit: this.count,
|
||||
constraints: this.options.constraints,
|
||||
where: this.options.whereFilter || undefined,
|
||||
},
|
||||
});
|
||||
|
||||
this.requestInfo = {
|
||||
requestDuration: readDataFromContainer?.duration || 0,
|
||||
requestMessage: readDataFromContainer?.statusMessage || '',
|
||||
};
|
||||
|
||||
return readDataFromContainer?.results.map<IDataContainerResult>(result => ({
|
||||
id: result.resultSet!.id,
|
||||
dataFormat: result.dataFormat!,
|
||||
loadedFully: (result.resultSet?.rows?.length || 0) < this.count || !result.resultSet?.hasMoreData,
|
||||
data: result.resultSet,
|
||||
})) || prevResults;
|
||||
}
|
||||
|
||||
async save(
|
||||
prevResults: IDataContainerResult[],
|
||||
data: DataUpdate
|
||||
): Promise<IDataContainerResult[]> {
|
||||
const executionContext = await this.ensureContextCreated();
|
||||
|
||||
const response = await this.graphQLService.sdk.updateResultsDataBatch({
|
||||
connectionId: executionContext.connectionId,
|
||||
contextId: executionContext.contextId,
|
||||
resultsId: data.data.id,
|
||||
// updatedRows: this.getRowsDiff(data),
|
||||
});
|
||||
|
||||
this.requestInfo = {
|
||||
requestDuration: response.result?.duration || 0,
|
||||
requestMessage: 'Saved successfully',
|
||||
};
|
||||
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
private async ensureContextCreated(): Promise<IExecutionContext> {
|
||||
if (!this.executionContext) {
|
||||
if (!this.options) {
|
||||
throw new Error('Options must be provided');
|
||||
}
|
||||
this.executionContext = await this.createExecutionContext(this.options.connectionId);
|
||||
}
|
||||
return this.executionContext;
|
||||
}
|
||||
|
||||
private async createExecutionContext(
|
||||
connectionId: string,
|
||||
defaultCatalog?: string,
|
||||
defaultSchema?: string
|
||||
): Promise<IExecutionContext> {
|
||||
|
||||
const response = await this.graphQLService.sdk.sqlContextCreate({
|
||||
connectionId,
|
||||
defaultCatalog,
|
||||
defaultSchema,
|
||||
});
|
||||
return {
|
||||
contextId: response.context.id,
|
||||
connectionId,
|
||||
objectCatalogId: response.context.defaultCatalog,
|
||||
objectSchemaId: response.context.defaultSchema,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { TableViewerModel } from './TableViewer/TableViewerModel';
|
||||
import { DataModelWrapper } from './TableViewer/DataModelWrapper';
|
||||
|
||||
export interface IDataPresentationProps {
|
||||
tableModel: TableViewerModel;
|
||||
tableModel: DataModelWrapper;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,11 @@ import { ConnectionInfoResource } from '@cloudbeaver/core-connections';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { GraphQLService } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { ContainerDataSource } from './ContainerDataSource';
|
||||
import { DatabaseDataAccessMode } from './DatabaseDataModel/IDatabaseDataModel';
|
||||
import { IExecutionContext } from './IExecutionContext';
|
||||
import { RowDiff } from './TableViewer/TableDataModel/EditedRow';
|
||||
import { IRequestDataResult, TableViewerModel, AccessMode } from './TableViewer/TableViewerModel';
|
||||
import { IRequestDataResult, TableViewerModel } from './TableViewer/TableViewerModel';
|
||||
import { TableViewerStorageService } from './TableViewer/TableViewerStorageService';
|
||||
|
||||
@injectable()
|
||||
@@ -22,7 +24,7 @@ export class DataViewerTableService {
|
||||
private graphQLService: GraphQLService) {
|
||||
}
|
||||
|
||||
has(tableId: string) {
|
||||
has(tableId: string): boolean {
|
||||
return this.tableViewerStorageService.has(tableId);
|
||||
}
|
||||
|
||||
@@ -33,7 +35,7 @@ export class DataViewerTableService {
|
||||
async create(
|
||||
tabId: string,
|
||||
connectionId: string,
|
||||
containerNodePath?: string,
|
||||
containerNodePath = '',
|
||||
): Promise<TableViewerModel> {
|
||||
const connectionInfo = await this.connectionInfoResource.load(connectionId);
|
||||
|
||||
@@ -42,11 +44,19 @@ export class DataViewerTableService {
|
||||
tableId: tabId,
|
||||
connectionId,
|
||||
containerNodePath,
|
||||
access: connectionInfo.readOnly ? AccessMode.Readonly : AccessMode.Default,
|
||||
access: connectionInfo.readOnly ? DatabaseDataAccessMode.Readonly : DatabaseDataAccessMode.Default,
|
||||
requestDataAsync: this.requestDataAsync.bind(this),
|
||||
saveChanges: this.saveChanges.bind(this),
|
||||
}
|
||||
);
|
||||
},
|
||||
new ContainerDataSource(this.graphQLService)
|
||||
)
|
||||
.setAccess(connectionInfo.readOnly ? DatabaseDataAccessMode.Readonly : DatabaseDataAccessMode.Default)
|
||||
.setOptions({
|
||||
connectionId,
|
||||
containerNodePath,
|
||||
constraints: [],
|
||||
whereFilter: '',
|
||||
}).deprecatedModel;
|
||||
}
|
||||
|
||||
private async createExecutionContext(
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { DatabaseDataModel } from './DatabaseDataModel';
|
||||
|
||||
export interface DatabaseDataFormat<T> {
|
||||
processResult(model: DatabaseDataModel<any, any>, data: T): void;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { uuid } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { DatabaseDataAccessMode, IDatabaseDataModel } from './IDatabaseDataModel';
|
||||
import { IDatabaseDataResult } from './IDatabaseDataResult';
|
||||
import { IDatabaseDataSource, IRequestInfo } from './IDatabaseDataSource';
|
||||
|
||||
export class DatabaseDataModel<TOptions, TResult extends IDatabaseDataResult>
|
||||
implements IDatabaseDataModel<TOptions, TResult> {
|
||||
id: string;
|
||||
results: TResult[];
|
||||
source: IDatabaseDataSource<TOptions, TResult>;
|
||||
access: DatabaseDataAccessMode;
|
||||
|
||||
get requestInfo(): IRequestInfo {
|
||||
return this.source.requestInfo;
|
||||
}
|
||||
|
||||
constructor(source: IDatabaseDataSource<TOptions, TResult>) {
|
||||
this.id = uuid();
|
||||
this.source = source;
|
||||
this.results = [];
|
||||
this.access = DatabaseDataAccessMode.Default;
|
||||
}
|
||||
|
||||
isLoading(): boolean {
|
||||
return this.source.isLoading();
|
||||
}
|
||||
|
||||
setAccess(access: DatabaseDataAccessMode): this {
|
||||
this.access = access;
|
||||
return this;
|
||||
}
|
||||
|
||||
setSlice(offset: number, count: number): this {
|
||||
this.source.setSlice(offset, count);
|
||||
return this;
|
||||
}
|
||||
|
||||
setOptions(options: TOptions): this {
|
||||
this.source.setOptions(options);
|
||||
return this;
|
||||
}
|
||||
|
||||
async requestData(): Promise<void> {
|
||||
this.results = await this.source.requestData(this.results);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { IDatabaseDataResult } from './IDatabaseDataResult';
|
||||
import { DataUpdate, IDatabaseDataSource, IRequestInfo } from './IDatabaseDataSource';
|
||||
|
||||
export abstract class DatabaseDataSource<TOptions, TResult extends IDatabaseDataResult>
|
||||
implements IDatabaseDataSource<TOptions, TResult> {
|
||||
offset: number;
|
||||
count: number;
|
||||
options: TOptions | null;
|
||||
requestInfo: IRequestInfo;
|
||||
|
||||
private activeRequest: Promise<TResult[]> | null;
|
||||
private activeSave: Promise<TResult[]> | null;
|
||||
|
||||
constructor() {
|
||||
this.offset = 0;
|
||||
this.count = 0;
|
||||
this.options = null;
|
||||
this.activeRequest = null;
|
||||
this.activeSave = null;
|
||||
this.requestInfo = {
|
||||
requestDuration: 0,
|
||||
requestMessage: '',
|
||||
};
|
||||
}
|
||||
|
||||
isLoading(): boolean {
|
||||
return !!this.activeRequest;
|
||||
}
|
||||
|
||||
setSlice(offset: number, count: number): void {
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
setOptions(options: TOptions): void {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
async requestData(prevResults: TResult[]): Promise<TResult[]> {
|
||||
if (this.activeSave) {
|
||||
try {
|
||||
await this.activeSave;
|
||||
} finally {}
|
||||
}
|
||||
|
||||
if (this.activeRequest) {
|
||||
return this.activeRequest;
|
||||
}
|
||||
|
||||
try {
|
||||
const promise = this.request(prevResults);
|
||||
|
||||
if (promise instanceof Promise) {
|
||||
this.activeRequest = promise;
|
||||
}
|
||||
return promise;
|
||||
} finally {
|
||||
this.activeRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
async saveData(
|
||||
prevResults: TResult[],
|
||||
data: DataUpdate
|
||||
): Promise<TResult[]> {
|
||||
if (this.activeRequest) {
|
||||
try {
|
||||
await this.activeRequest;
|
||||
} finally {}
|
||||
}
|
||||
|
||||
if (this.activeSave) {
|
||||
return this.activeSave;
|
||||
}
|
||||
|
||||
try {
|
||||
const promise = this.save(prevResults, data);
|
||||
|
||||
if (promise instanceof Promise) {
|
||||
this.activeSave = promise;
|
||||
}
|
||||
return promise;
|
||||
} finally {
|
||||
this.activeSave = null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract request(prevResults: TResult[]): TResult[] | Promise<TResult[]>;
|
||||
abstract save(prevResults: TResult[], data: DataUpdate): Promise<TResult[]> | TResult[];
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 interface IDatabaseDataEditor {
|
||||
isEdited(): boolean;
|
||||
getChanges(): void;
|
||||
applyChanges(): void;
|
||||
cancelChanges(skipUpdate?: boolean): void;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { IDatabaseDataResult } from './IDatabaseDataResult';
|
||||
import { IDatabaseDataSource, IRequestInfo } from './IDatabaseDataSource';
|
||||
|
||||
export enum DatabaseDataAccessMode {
|
||||
Default,
|
||||
Readonly
|
||||
}
|
||||
|
||||
export interface IDatabaseDataModel<TOptions, TResult extends IDatabaseDataResult> {
|
||||
readonly id: string;
|
||||
readonly results: TResult[];
|
||||
readonly source: IDatabaseDataSource<TOptions, TResult>;
|
||||
readonly access: DatabaseDataAccessMode;
|
||||
readonly requestInfo: IRequestInfo;
|
||||
|
||||
isLoading(): boolean;
|
||||
setAccess(access: DatabaseDataAccessMode): this;
|
||||
setSlice(offset: number, count: number): this;
|
||||
setOptions(options: TOptions): this;
|
||||
requestData(): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { ResultDataFormat } from '@cloudbeaver/core-sdk';
|
||||
|
||||
export interface IDatabaseDataResult {
|
||||
id: string;
|
||||
dataFormat: ResultDataFormat;
|
||||
loadedFully: boolean;
|
||||
data: any;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { SqlResultSet } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { IDatabaseDataResult } from './IDatabaseDataResult';
|
||||
|
||||
export enum DataUpdateType {
|
||||
delete,
|
||||
update,
|
||||
add
|
||||
}
|
||||
|
||||
export interface IRequestInfo {
|
||||
readonly requestDuration: number;
|
||||
readonly requestMessage: string;
|
||||
}
|
||||
|
||||
export interface DataUpdate<T = any> {
|
||||
data: SqlResultSet;
|
||||
dataUpdate: SqlResultSet;
|
||||
type: DataUpdateType;
|
||||
}
|
||||
|
||||
export interface IDatabaseDataSource<TOptions, TResult extends IDatabaseDataResult> {
|
||||
readonly offset: number;
|
||||
readonly count: number;
|
||||
readonly options: TOptions | null;
|
||||
readonly requestInfo: IRequestInfo;
|
||||
|
||||
isLoading(): boolean;
|
||||
setSlice(offset: number, count: number): void;
|
||||
setOptions(options: TOptions): void;
|
||||
requestData(
|
||||
prevResults: TResult[]
|
||||
): Promise<TResult[]> | TResult[];
|
||||
saveData(
|
||||
prevResults: TResult[],
|
||||
data: DataUpdate
|
||||
): Promise<TResult[]> | TResult[];
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { DatabaseDataModel } from '../DatabaseDataModel/DatabaseDataModel';
|
||||
import { DatabaseDataAccessMode } from '../DatabaseDataModel/IDatabaseDataModel';
|
||||
import { IDatabaseDataSource } from '../DatabaseDataModel/IDatabaseDataSource';
|
||||
import { ITableViewerModelOptions, TableViewerModel } from './TableViewerModel';
|
||||
|
||||
export class DataModelWrapper extends DatabaseDataModel<any, any> {
|
||||
/**
|
||||
* @deprecated Use getModel instead
|
||||
*/
|
||||
readonly deprecatedModel: TableViewerModel;
|
||||
|
||||
constructor(
|
||||
commonDialogService: CommonDialogService,
|
||||
options: ITableViewerModelOptions,
|
||||
source: IDatabaseDataSource<any, any>
|
||||
) {
|
||||
super(source);
|
||||
if (options.tableId) {
|
||||
this.id = options.tableId;
|
||||
}
|
||||
this.deprecatedModel = new TableViewerModel(options, commonDialogService);
|
||||
}
|
||||
|
||||
isLoading(): boolean {
|
||||
return this.deprecatedModel.isLoaderVisible || this.source.isLoading();
|
||||
}
|
||||
|
||||
setAccess(access: DatabaseDataAccessMode): this {
|
||||
this.access = access;
|
||||
this.deprecatedModel.access = access;
|
||||
return this;
|
||||
}
|
||||
|
||||
setSlice(offset: number, count: number): this {
|
||||
this.source.setSlice(offset, count);
|
||||
this.deprecatedModel.setChunkSize(count);
|
||||
return this;
|
||||
}
|
||||
|
||||
setOptions(options: any): this {
|
||||
this.source.setOptions(options);
|
||||
return this;
|
||||
}
|
||||
|
||||
async requestData(): Promise<void> {
|
||||
this.results = await this.source.requestData(this.results);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import styled, { css, use } from 'reshadow';
|
||||
import { IconButton, SubmittingForm } from '@cloudbeaver/core-blocks';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { TableViewerModel } from '../TableViewerModel';
|
||||
import { DataModelWrapper } from '../DataModelWrapper';
|
||||
import { TableFooterMenu } from './TableFooterMenu/TableFooterMenu';
|
||||
|
||||
const tableFooterStyles = composes(
|
||||
@@ -67,7 +67,7 @@ const tableFooterStyles = composes(
|
||||
);
|
||||
|
||||
type TableFooterProps = {
|
||||
model: TableViewerModel;
|
||||
model: DataModelWrapper;
|
||||
}
|
||||
|
||||
export const TableFooter = observer(function TableFooter({
|
||||
@@ -81,8 +81,8 @@ export const TableFooter = observer(function TableFooter({
|
||||
}
|
||||
const value = parseInt(ref.current.value, 10);
|
||||
|
||||
if (model.getChunkSize() !== value) {
|
||||
model.setChunkSize(value);
|
||||
if (model.deprecatedModel.getChunkSize() !== value) {
|
||||
model.deprecatedModel.setChunkSize(value);
|
||||
}
|
||||
},
|
||||
[model]
|
||||
@@ -91,17 +91,17 @@ export const TableFooter = observer(function TableFooter({
|
||||
return styled(useStyles(tableFooterStyles))(
|
||||
<table-footer as="div">
|
||||
<reload as="div">
|
||||
<IconButton type="button" name='reload' onClick={model.refresh} viewBox="0 0 16 16"/>
|
||||
<IconButton type="button" name='reload' onClick={model.deprecatedModel.refresh} viewBox="0 0 16 16"/>
|
||||
</reload>
|
||||
<count as="div">
|
||||
<SubmittingForm onSubmit={handleChange}>
|
||||
<input type="number" value={model.getChunkSize()} ref={ref} onBlur={handleChange} {...use({ mod: 'surface' })} />
|
||||
<input type="number" value={model.deprecatedModel.getChunkSize()} ref={ref} onBlur={handleChange} {...use({ mod: 'surface' })} />
|
||||
</SubmittingForm>
|
||||
</count>
|
||||
<TableFooterMenu model={model}/>
|
||||
{model.requestStatusMessage.length > 0 && (
|
||||
{model.deprecatedModel.requestStatusMessage.length > 0 && (
|
||||
<time>
|
||||
{model.requestStatusMessage} - {model.queryDuration}ms
|
||||
{model.deprecatedModel.requestStatusMessage} - {model.deprecatedModel.queryDuration}ms
|
||||
</time>
|
||||
)}
|
||||
</table-footer>
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import styled, { css } from 'reshadow';
|
||||
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
|
||||
import { TableViewerModel } from '../../TableViewerModel';
|
||||
import { DataModelWrapper } from '../../DataModelWrapper';
|
||||
import { TableFooterMenuItem } from './TableFooterMenuItem';
|
||||
import { TableFooterMenuService } from './TableFooterMenuService';
|
||||
|
||||
@@ -27,7 +27,7 @@ const styles = css`
|
||||
`;
|
||||
|
||||
type TableFooterMenuProps = {
|
||||
model: TableViewerModel;
|
||||
model: DataModelWrapper;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -11,7 +11,7 @@ import {
|
||||
ContextMenuService, IMenuContext, IContextMenuItem, IMenuItem
|
||||
} from '@cloudbeaver/core-dialogs';
|
||||
|
||||
import { TableViewerModel } from '../../TableViewerModel';
|
||||
import { DataModelWrapper } from '../../DataModelWrapper';
|
||||
|
||||
@injectable()
|
||||
export class TableFooterMenuService {
|
||||
@@ -28,12 +28,12 @@ export class TableFooterMenuService {
|
||||
return context.contextType === TableFooterMenuService.nodeContextType;
|
||||
},
|
||||
isDisabled(context) {
|
||||
return !context.data.isEdited();
|
||||
return !context.data.deprecatedModel.isEdited();
|
||||
},
|
||||
order: 1,
|
||||
title: 'Save',
|
||||
icon: 'table-save',
|
||||
onClick: context => context.data.saveChanges(),
|
||||
onClick: context => context.data.deprecatedModel.saveChanges(),
|
||||
});
|
||||
this.registerMenuItem({
|
||||
id: 'cancel ',
|
||||
@@ -41,26 +41,26 @@ export class TableFooterMenuService {
|
||||
return context.contextType === TableFooterMenuService.nodeContextType;
|
||||
},
|
||||
isDisabled(context) {
|
||||
return !context.data.isEdited();
|
||||
return !context.data.deprecatedModel.isEdited();
|
||||
},
|
||||
order: 2,
|
||||
title: 'Cancel',
|
||||
icon: 'table-cancel',
|
||||
onClick: context => context.data.cancelChanges(),
|
||||
onClick: context => context.data.deprecatedModel.cancelChanges(),
|
||||
});
|
||||
}
|
||||
|
||||
constructMenuWithContext(model: TableViewerModel): IMenuItem[] {
|
||||
const context: IMenuContext<TableViewerModel> = {
|
||||
constructMenuWithContext(model: DataModelWrapper): IMenuItem[] {
|
||||
const context: IMenuContext<DataModelWrapper> = {
|
||||
menuId: this.tableFooterMenuToken,
|
||||
contextId: model.tableId,
|
||||
contextId: model.id,
|
||||
contextType: TableFooterMenuService.nodeContextType,
|
||||
data: model,
|
||||
};
|
||||
return this.contextMenuService.createContextMenu(context, this.tableFooterMenuToken).menuItems;
|
||||
}
|
||||
|
||||
registerMenuItem(options: IContextMenuItem<TableViewerModel>): void {
|
||||
this.contextMenuService.addMenuItem<TableViewerModel>(this.tableFooterMenuToken, options);
|
||||
registerMenuItem(options: IContextMenuItem<DataModelWrapper>): void {
|
||||
this.contextMenuService.addMenuItem<DataModelWrapper>(this.tableFooterMenuToken, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ import { useService } from '@cloudbeaver/core-di';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
|
||||
import { DataPresentationService } from '../DataPresentationService';
|
||||
import { TableViewerModel } from './TableViewerModel';
|
||||
import { DataModelWrapper } from './DataModelWrapper';
|
||||
|
||||
type TableGridProps = PropsWithChildren<{
|
||||
model: TableViewerModel;
|
||||
model: DataModelWrapper;
|
||||
}>
|
||||
|
||||
const styles = css`
|
||||
@@ -37,13 +37,14 @@ export const TableGrid = observer(function TableGrid({
|
||||
const translate = useTranslate();
|
||||
const dataPresentationService = useService(DataPresentationService);
|
||||
|
||||
if (model.errorMessage.length > 0) {
|
||||
// TODO: probably must be implemented in presentation component
|
||||
if (model.deprecatedModel.errorMessage.length > 0) {
|
||||
return styled(styles)(
|
||||
<error as="div">
|
||||
{model.errorMessage}
|
||||
{model.deprecatedModel.errorMessage}
|
||||
<br/><br/>
|
||||
{model.hasDetails && (
|
||||
<Button type='button' mod={['outlined']} onClick={model.onShowDetails}>
|
||||
{model.deprecatedModel.hasDetails && (
|
||||
<Button type='button' mod={['outlined']} onClick={model.deprecatedModel.onShowDetails}>
|
||||
{translate('ui_errors_details')}
|
||||
</Button>
|
||||
)}
|
||||
@@ -53,7 +54,7 @@ export const TableGrid = observer(function TableGrid({
|
||||
|
||||
const Spreadsheet = dataPresentationService.default?.component;
|
||||
|
||||
if ((model.isFullyLoaded && model.isEmpty) || !Spreadsheet) {
|
||||
if ((model.deprecatedModel.isFullyLoaded && model.deprecatedModel.isEmpty) || !Spreadsheet) {
|
||||
return styled(styles)(<no-data as="div">{translate('data_viewer_nodata_message')}</no-data>);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Placeholder } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { TableViewerModel } from '../TableViewerModel';
|
||||
import { DataModelWrapper } from '../DataModelWrapper';
|
||||
import { TableHeaderService } from './TableHeaderService';
|
||||
|
||||
const styles = composes(
|
||||
@@ -33,7 +33,7 @@ const styles = composes(
|
||||
);
|
||||
|
||||
type Props = {
|
||||
model: TableViewerModel;
|
||||
model: DataModelWrapper;
|
||||
}
|
||||
|
||||
export const TableHeader = observer(function TableHeader({
|
||||
|
||||
+2
-2
@@ -9,12 +9,12 @@
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
|
||||
import { TableViewerModel } from '../TableViewerModel';
|
||||
import { DataModelWrapper } from '../DataModelWrapper';
|
||||
import { TableWhereFilter } from './TableWhereFilter';
|
||||
|
||||
@injectable()
|
||||
export class TableHeaderService extends Bootstrap {
|
||||
readonly tableHeaderPlaceholder = new PlaceholderContainer<TableViewerModel>()
|
||||
readonly tableHeaderPlaceholder = new PlaceholderContainer<DataModelWrapper>()
|
||||
|
||||
register() {
|
||||
this.tableHeaderPlaceholder.add(TableWhereFilter, 1);
|
||||
|
||||
@@ -15,7 +15,7 @@ import { SubmittingForm } from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { TableViewerModel } from '../TableViewerModel';
|
||||
import { DataModelWrapper } from '../DataModelWrapper';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
@@ -39,29 +39,29 @@ const styles = composes(
|
||||
);
|
||||
|
||||
type Props = {
|
||||
context: TableViewerModel;
|
||||
context: DataModelWrapper;
|
||||
}
|
||||
|
||||
export const TableWhereFilter = observer(function TableWhereFilter({
|
||||
context,
|
||||
}: Props) {
|
||||
const translate = useTranslate();
|
||||
const [filterValue, setValue] = useState(() => context.getQueryWhereFilter() || '');
|
||||
const [filterValue, setValue] = useState(() => context.deprecatedModel.getQueryWhereFilter() || '');
|
||||
|
||||
const handleApply = useCallback(() => {
|
||||
context.setQueryWhereFilter(filterValue);
|
||||
context.refresh();
|
||||
context.deprecatedModel.setQueryWhereFilter(filterValue);
|
||||
context.deprecatedModel.refresh();
|
||||
}, [context, filterValue]);
|
||||
|
||||
const resetFilter = useCallback(
|
||||
() => {
|
||||
const applyNeeded = context.getQueryWhereFilter() === filterValue;
|
||||
const applyNeeded = context.deprecatedModel.getQueryWhereFilter() === filterValue;
|
||||
|
||||
setValue('');
|
||||
|
||||
if (applyNeeded) {
|
||||
context.setQueryWhereFilter('');
|
||||
context.refresh();
|
||||
context.deprecatedModel.setQueryWhereFilter('');
|
||||
context.deprecatedModel.refresh();
|
||||
}
|
||||
},
|
||||
[context, filterValue]
|
||||
|
||||
@@ -49,7 +49,7 @@ export const TableViewer = observer(function TableViewer({
|
||||
<TableHeader model={tableViewerModel} />
|
||||
<TableGrid model={tableViewerModel} />
|
||||
<TableFooter model={tableViewerModel} />
|
||||
<Loader loading={tableViewerModel.isLoaderVisible} overlay/>
|
||||
<Loader loading={tableViewerModel.isLoading()} overlay/>
|
||||
</table-viewer>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ErrorDetailsDialog } from '@cloudbeaver/core-notifications';
|
||||
import { GQLError, SqlDataFilterConstraint } from '@cloudbeaver/core-sdk';
|
||||
import { uuid, MetadataMap } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { DatabaseDataAccessMode } from '../DatabaseDataModel/IDatabaseDataModel';
|
||||
import { IExecutionContext } from '../IExecutionContext';
|
||||
import { ErrorDialog } from './ErrorDialog';
|
||||
import { RowDiff } from './TableDataModel/EditedRow';
|
||||
@@ -28,11 +29,6 @@ export const fetchingSettings = {
|
||||
fetchDefault: 200,
|
||||
};
|
||||
|
||||
export enum AccessMode {
|
||||
Default,
|
||||
Readonly
|
||||
}
|
||||
|
||||
export type AgGridRow = any[];
|
||||
|
||||
export type SortMode = 'asc' | 'desc' | null;
|
||||
@@ -73,7 +69,7 @@ export interface ITableViewerModelOptions {
|
||||
executionContext?: IExecutionContext | null; // will be filled before fist data fetch
|
||||
sourceName?: string; // TODO: refactor it, used for showing sql query for export
|
||||
noLoaderWhileRequestingDataAsync?: boolean;
|
||||
access?: AccessMode;
|
||||
access?: DatabaseDataAccessMode;
|
||||
requestDataAsync(
|
||||
model: TableViewerModel,
|
||||
rowOffset: number,
|
||||
@@ -99,7 +95,7 @@ export class TableViewerModel {
|
||||
sourceName?: string;
|
||||
noLoaderWhileRequestingDataAsync?: boolean;
|
||||
|
||||
@observable access: AccessMode;
|
||||
@observable access: DatabaseDataAccessMode;
|
||||
|
||||
requestDataAsync: (
|
||||
model: TableViewerModel,
|
||||
@@ -156,7 +152,7 @@ export class TableViewerModel {
|
||||
this.executionContext = options.executionContext || null;
|
||||
this.sourceName = options.sourceName;
|
||||
this.noLoaderWhileRequestingDataAsync = options.noLoaderWhileRequestingDataAsync;
|
||||
this.access = options.access || AccessMode.Default;
|
||||
this.access = options.access || DatabaseDataAccessMode.Default;
|
||||
this.requestDataAsync = options.requestDataAsync;
|
||||
this._saveChanges = options.saveChanges;
|
||||
this.resetSubject = new Subject();
|
||||
@@ -223,7 +219,7 @@ export class TableViewerModel {
|
||||
}
|
||||
|
||||
isEdited(): boolean {
|
||||
if (this.access === AccessMode.Readonly) {
|
||||
if (this.access === DatabaseDataAccessMode.Readonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -243,7 +239,7 @@ export class TableViewerModel {
|
||||
}
|
||||
|
||||
async saveChanges(): Promise<void> {
|
||||
if (this.access === AccessMode.Readonly) {
|
||||
if (this.access === DatabaseDataAccessMode.Readonly) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -321,7 +317,7 @@ export class TableViewerModel {
|
||||
}
|
||||
|
||||
onCellEditingStopped(rowNumber: number, column: string, value: any, editing: boolean): void {
|
||||
if (this.access === AccessMode.Readonly) {
|
||||
if (this.access === DatabaseDataAccessMode.Readonly) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,31 +11,49 @@ import { observable } from 'mobx';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
|
||||
|
||||
import { TableViewerModel, ITableViewerModelOptions } from './TableViewerModel';
|
||||
import { IDatabaseDataModel } from '../DatabaseDataModel/IDatabaseDataModel';
|
||||
import { IDatabaseDataResult } from '../DatabaseDataModel/IDatabaseDataResult';
|
||||
import { IDatabaseDataSource } from '../DatabaseDataModel/IDatabaseDataSource';
|
||||
import { DataModelWrapper } from './DataModelWrapper';
|
||||
import { ITableViewerModelOptions } from './TableViewerModel';
|
||||
|
||||
@injectable()
|
||||
export class TableViewerStorageService {
|
||||
@observable private tableModelMap: Map<string, TableViewerModel> = new Map();
|
||||
@observable private tableModelMap: Map<string, IDatabaseDataModel<any, any>> = new Map();
|
||||
|
||||
constructor(private commonDialogService: CommonDialogService) {}
|
||||
|
||||
has(tableId: string): boolean {
|
||||
return this.tableModelMap.has(tableId);
|
||||
}
|
||||
get(tableId: string) {
|
||||
return this.tableModelMap.get(tableId);
|
||||
|
||||
get(tableId: string): DataModelWrapper | null | undefined {
|
||||
return this.tableModelMap.get(tableId) as DataModelWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use add method instead
|
||||
*/
|
||||
create(
|
||||
options: ITableViewerModelOptions,
|
||||
): TableViewerModel {
|
||||
source: IDatabaseDataSource<any, any>
|
||||
): DataModelWrapper {
|
||||
|
||||
const tableModel = new TableViewerModel(options, this.commonDialogService);
|
||||
this.tableModelMap.set(tableModel.tableId, tableModel);
|
||||
return tableModel;
|
||||
return this.add(new DataModelWrapper(
|
||||
this.commonDialogService,
|
||||
options,
|
||||
source
|
||||
)) as DataModelWrapper;
|
||||
}
|
||||
|
||||
remove(tableId: string) {
|
||||
this.tableModelMap.delete(tableId);
|
||||
add<TOptions, TResult extends IDatabaseDataResult>(
|
||||
model: IDatabaseDataModel<TOptions, TResult>,
|
||||
): IDatabaseDataModel<TOptions, TResult> {
|
||||
this.tableModelMap.set(model.id, model);
|
||||
return this.tableModelMap.get(model.id)!;
|
||||
}
|
||||
|
||||
remove(id: string): void {
|
||||
this.tableModelMap.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,21 @@ import { manifest } from './manifest';
|
||||
|
||||
export default manifest;
|
||||
|
||||
export * from './DatabaseDataModel/DatabaseDataFormat';
|
||||
export * from './DatabaseDataModel/DatabaseDataModel';
|
||||
export * from './DatabaseDataModel/DatabaseDataSource';
|
||||
export * from './DatabaseDataModel/IDatabaseDataEditor';
|
||||
export * from './DatabaseDataModel/IDatabaseDataModel';
|
||||
export * from './DatabaseDataModel/IDatabaseDataResult';
|
||||
export * from './DatabaseDataModel/IDatabaseDataSource';
|
||||
|
||||
// All Services and Components that is provided by this plugin should be exported here
|
||||
export * from './TableViewer/TableViewerStorageService';
|
||||
|
||||
export * from './TableViewer/TableViewer';
|
||||
export * from './TableViewer/TableFooter/TableFooterMenu/TableFooterMenuService';
|
||||
export * from './TableViewer/TableViewerModel';
|
||||
export * from './TableViewer/DataModelWrapper';
|
||||
|
||||
export * from './TableViewer/TableDataModel/TableColumn';
|
||||
export * from './TableViewer/TableDataModel/TableRow';
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { GraphQLService, SqlDataFilterConstraint } from '@cloudbeaver/core-sdk';
|
||||
import {
|
||||
DatabaseDataSource, DataUpdate, IDatabaseDataResult, IExecutionContext
|
||||
} from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
export interface IDataContainerOptions {
|
||||
sourceName: string;
|
||||
connectionId: string;
|
||||
whereFilter: string;
|
||||
constraints: SqlDataFilterConstraint[];
|
||||
}
|
||||
|
||||
export interface IDataContainerResult extends IDatabaseDataResult {
|
||||
|
||||
}
|
||||
|
||||
export class QueryDataSource extends DatabaseDataSource<IDataContainerOptions, IDataContainerResult> {
|
||||
private executionContext: IExecutionContext | null;
|
||||
|
||||
constructor(private graphQLService: GraphQLService) {
|
||||
super();
|
||||
this.executionContext = null;
|
||||
}
|
||||
|
||||
async request(
|
||||
prevResults: IDataContainerResult[]
|
||||
): Promise<IDataContainerResult[]> {
|
||||
// if (!this.options?.containerNodePath) {
|
||||
// throw new Error('containerNodePath must be provided for table');
|
||||
// }
|
||||
// const executionContext = await this.ensureContextCreated();
|
||||
|
||||
// const { readDataFromContainer } = await this.graphQLService.sdk.readDataFromContainer({
|
||||
// connectionId: executionContext.connectionId,
|
||||
// contextId: executionContext.contextId,
|
||||
// containerNodePath: this.options.containerNodePath,
|
||||
// filter: {
|
||||
// offset: this.offset,
|
||||
// limit: this.count,
|
||||
// constraints: this.options.constraints,
|
||||
// where: this.options.whereFilter || undefined,
|
||||
// },
|
||||
// });
|
||||
|
||||
// this.requestInfo = {
|
||||
// requestDuration: readDataFromContainer?.duration || 0,
|
||||
// requestMessage: readDataFromContainer?.statusMessage || '',
|
||||
// };
|
||||
|
||||
// return readDataFromContainer?.results.map<IDataContainerResult>(result => ({
|
||||
// id: result.resultSet!.id,
|
||||
// dataFormat: result.dataFormat!,
|
||||
// loadedFully: (result.resultSet?.rows?.length || 0) < this.count || !result.resultSet?.hasMoreData,
|
||||
// data: result.resultSet,
|
||||
// })) || prevResults;
|
||||
|
||||
return prevResults;
|
||||
}
|
||||
|
||||
async save(
|
||||
prevResults: IDataContainerResult[],
|
||||
data: DataUpdate
|
||||
): Promise<IDataContainerResult[]> {
|
||||
const executionContext = await this.ensureContextCreated();
|
||||
|
||||
const response = await this.graphQLService.sdk.updateResultsDataBatch({
|
||||
connectionId: executionContext.connectionId,
|
||||
contextId: executionContext.contextId,
|
||||
resultsId: data.data.id,
|
||||
// updatedRows: this.getRowsDiff(data),
|
||||
});
|
||||
|
||||
this.requestInfo = {
|
||||
requestDuration: response.result?.duration || 0,
|
||||
requestMessage: 'Saved successfully',
|
||||
};
|
||||
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
private async ensureContextCreated(): Promise<IExecutionContext> {
|
||||
if (!this.executionContext) {
|
||||
if (!this.options) {
|
||||
throw new Error('Options must be provided');
|
||||
}
|
||||
this.executionContext = await this.createExecutionContext(this.options.connectionId);
|
||||
}
|
||||
return this.executionContext;
|
||||
}
|
||||
|
||||
private async createExecutionContext(
|
||||
connectionId: string,
|
||||
defaultCatalog?: string,
|
||||
defaultSchema?: string
|
||||
): Promise<IExecutionContext> {
|
||||
|
||||
const response = await this.graphQLService.sdk.sqlContextCreate({
|
||||
connectionId,
|
||||
defaultCatalog,
|
||||
defaultSchema,
|
||||
});
|
||||
return {
|
||||
contextId: response.context.id,
|
||||
connectionId,
|
||||
objectCatalogId: response.context.defaultCatalog,
|
||||
objectSchemaId: response.context.defaultSchema,
|
||||
};
|
||||
}
|
||||
}
|
||||
+26
-14
@@ -13,7 +13,7 @@ import { IDestructibleController, IInitializableController, injectable } from '@
|
||||
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { ErrorDetailsDialog } from '@cloudbeaver/core-notifications';
|
||||
import { GQLError, ServerInternalError } from '@cloudbeaver/core-sdk';
|
||||
import { GQLError, GraphQLService, ServerInternalError } from '@cloudbeaver/core-sdk';
|
||||
import { PromiseCancelledError } from '@cloudbeaver/core-utils';
|
||||
import {
|
||||
fetchingSettings,
|
||||
@@ -21,10 +21,11 @@ import {
|
||||
RowDiff,
|
||||
TableViewerStorageService,
|
||||
TableViewerModel,
|
||||
AccessMode,
|
||||
DatabaseDataAccessMode,
|
||||
} from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import { IResultDataTab, IQueryTabGroup } from '../../ISqlEditorTabState';
|
||||
import { QueryDataSource } from '../../QueryDataSource';
|
||||
import { SqlEditorGroupMetadataService } from '../../SqlEditorGroupMetadataService';
|
||||
import { SqlExecutionState } from '../../SqlExecutionState';
|
||||
import { SQLQueryExecutionProcess } from '../SQLQueryExecutionProcess';
|
||||
@@ -54,6 +55,7 @@ implements IInitializableController, IDestructibleController {
|
||||
private sqlProcess: SQLQueryExecutionProcess | null = null;
|
||||
|
||||
constructor(
|
||||
private sdk: GraphQLService,
|
||||
private sqlResultService: SqlResultService,
|
||||
private tableViewerStorageService: TableViewerStorageService,
|
||||
private connectionInfoResource: ConnectionInfoResource,
|
||||
@@ -101,17 +103,27 @@ implements IInitializableController, IDestructibleController {
|
||||
this.tableViewerStorageService.remove(this.getTableId());
|
||||
}
|
||||
|
||||
const tableModel = this.tableViewerStorageService.create({
|
||||
tableId: this.getTableId(),
|
||||
connectionId: this.group.sqlQueryParams.connectionId,
|
||||
executionContext: this.group.sqlQueryParams,
|
||||
resultId: dataSet.resultSet.id,
|
||||
sourceName: this.group.sqlQueryParams.query,
|
||||
access: connectionInfo.readOnly ? AccessMode.Readonly : AccessMode.Default,
|
||||
requestDataAsync: this.requestDataAsync.bind(this, sqlExecutionContext),
|
||||
noLoaderWhileRequestingDataAsync: true,
|
||||
saveChanges: this.saveChanges.bind(this),
|
||||
});
|
||||
const tableModel = this.tableViewerStorageService.create(
|
||||
{
|
||||
tableId: this.getTableId(),
|
||||
connectionId: this.group.sqlQueryParams.connectionId,
|
||||
executionContext: this.group.sqlQueryParams,
|
||||
resultId: dataSet.resultSet.id,
|
||||
sourceName: this.group.sqlQueryParams.query,
|
||||
access: connectionInfo.readOnly ? DatabaseDataAccessMode.Readonly : DatabaseDataAccessMode.Default,
|
||||
requestDataAsync: this.requestDataAsync.bind(this, sqlExecutionContext),
|
||||
noLoaderWhileRequestingDataAsync: true,
|
||||
saveChanges: this.saveChanges.bind(this),
|
||||
},
|
||||
new QueryDataSource(this.sdk)
|
||||
)
|
||||
.setAccess(connectionInfo.readOnly ? DatabaseDataAccessMode.Readonly : DatabaseDataAccessMode.Default)
|
||||
.setOptions({
|
||||
connectionId: this.group.sqlQueryParams.connectionId,
|
||||
sourceName: this.group.sqlQueryParams.query,
|
||||
constraints: [],
|
||||
whereFilter: '',
|
||||
}).deprecatedModel;
|
||||
|
||||
tableModel.insertRows(0, initialState.rows, !initialState.isFullyLoaded);
|
||||
tableModel.setColumns(initialState.columns);
|
||||
@@ -204,7 +216,7 @@ implements IInitializableController, IDestructibleController {
|
||||
const connectionInfo = await this.connectionInfoResource.load(this.group.sqlQueryParams.connectionId);
|
||||
|
||||
model.resultId = resultId;
|
||||
model.access = connectionInfo.readOnly ? AccessMode.Readonly : AccessMode.Default;
|
||||
model.access = connectionInfo.readOnly ? DatabaseDataAccessMode.Readonly : DatabaseDataAccessMode.Default;
|
||||
model.sourceName = this.group.sqlQueryParams.query;
|
||||
model.executionContext = this.group.sqlQueryParams;
|
||||
model.connectionId = this.group.sqlQueryParams.connectionId;
|
||||
|
||||
Reference in New Issue
Block a user