Cb 4112 open scripts datasets from virtual file systems (#2089)

* CB-4125 use base path

* CB-4112 feat: read objectId

* CB-4145 add required auth to fs system api

* CB-4145 move feature

* CB-4112 feat: open scripts from file systems

* CB-4125 improve file reading

* CB-4112 refactor: naming

* CB-4112 update schema

* CB-4112 fs by id gql

* CB-4112 unique fs id

* CB-4112 fix gql schema

* CB-4112 fix gql schema

* CB-4112 update manifest

* CB-4112 feat: require authentication for nav-tree

---------

Co-authored-by: Aleksey Potsetsuev <wrouds@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
This commit is contained in:
Alexander Skoblikov
2023-10-31 17:18:32 +03:00
committed by GitHub
co-authored by Aleksey Potsetsuev Daria Marutkina
parent 0f9e7b650c
commit cb60358ae6
22 changed files with 311 additions and 138 deletions
@@ -14,7 +14,7 @@ import { isResourceOfType, ProjectInfoResource, ProjectsService } from '@cloudbe
import { CachedMapAllKey, CachedTreeChildrenKey } from '@cloudbeaver/core-resource';
import { getRmResourcePath, NAV_NODE_TYPE_RM_RESOURCE, ResourceManagerResource, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager';
import { createPath, getPathName } from '@cloudbeaver/core-utils';
import { ACTION_SAVE, ActionService, DATA_CONTEXT_MENU, KEY_BINDING_SAVE, KeyBindingService, MenuService } from '@cloudbeaver/core-view';
import { ActionService, DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view';
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
import { getResourceKeyFromNodeId } from '@cloudbeaver/plugin-navigation-tree-rm';
import { RESOURCE_NAME_REGEX, ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
@@ -55,7 +55,6 @@ export class PluginBootstrap extends Bootstrap {
private readonly sqlEditorSettingsService: SqlEditorSettingsService,
private readonly resourceManagerResource: ResourceManagerResource,
private readonly resourceManagerScriptsService: ResourceManagerScriptsService,
private readonly keyBindingService: KeyBindingService,
) {
super();
}
@@ -83,10 +82,6 @@ export class PluginBootstrap extends Bootstrap {
return dataSource instanceof MemorySqlDataSource || dataSource instanceof LocalStorageSqlDataSource;
}
if (action === ACTION_SAVE) {
return dataSource?.isAutoSaveEnabled === false;
}
return false;
},
handler: async (context, action) => {
@@ -187,34 +182,9 @@ export class PluginBootstrap extends Bootstrap {
}
}
}
if (action === ACTION_SAVE) {
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE);
const source = this.sqlDataSourceService.get(state.editorId) as ResourceSqlDataSource | undefined;
if (!source) {
return;
}
await source.save();
}
},
isDisabled: (context, action) => {
if (action === ACTION_SAVE) {
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE);
const source = this.sqlDataSourceService.get(state.editorId) as ResourceSqlDataSource | undefined;
if (!source) {
return true;
}
return source.isLoading() || source.isSaved;
}
return false;
},
getActionInfo: (context, action) => {
if (action === ACTION_SAVE_AS_SCRIPT || action === ACTION_SAVE) {
if (action === ACTION_SAVE_AS_SCRIPT) {
return {
...action.info,
label: '',
@@ -241,23 +211,7 @@ export class PluginBootstrap extends Bootstrap {
!!dataSource?.hasFeature(ESqlDataSourceFeatures.script)
);
},
getItems: (context, items) => [...items, ACTION_SAVE_AS_SCRIPT, ACTION_SAVE],
});
this.keyBindingService.addKeyBindingHandler({
id: 'script-save',
binding: KEY_BINDING_SAVE,
isBindingApplicable: (context, action) => action === ACTION_SAVE,
handler: async context => {
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE);
const source = this.sqlDataSourceService.get(state.editorId) as ResourceSqlDataSource | undefined;
if (!source) {
return;
}
await source.save();
},
getItems: (context, items) => [...items, ACTION_SAVE_AS_SCRIPT],
});
}
@@ -17,16 +17,13 @@ import { TaskScheduler } from '@cloudbeaver/core-executor';
import type { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { isResourceAlias, ResourceKey, ResourceKeyUtils } from '@cloudbeaver/core-resource';
import { getRmResourceKey, ResourceManagerResource } from '@cloudbeaver/core-resource-manager';
import type { NetworkStateService } from '@cloudbeaver/core-root';
import { debounce, getPathName, isArraysEqual, isNotNullDefined, isObjectsEqual, isValuesEqual } from '@cloudbeaver/core-utils';
import { SCRIPTS_TYPE_ID } from '@cloudbeaver/plugin-resource-manager-scripts';
import { BaseSqlDataSource, ESqlDataSourceFeatures, SqlEditorService } from '@cloudbeaver/plugin-sql-editor';
import type { IResourceSqlDataSourceState } from './IResourceSqlDataSourceState';
interface IResourceInfo {
isReadonly?: (dataSource: ResourceSqlDataSource) => boolean;
}
interface IResourceActions {
rename(dataSource: ResourceSqlDataSource, key: string, name: string): Promise<string>;
read(dataSource: ResourceSqlDataSource, key: string): Promise<string>;
@@ -103,7 +100,6 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
}
private actions?: IResourceActions;
private info?: IResourceInfo;
private lastAction: (() => Promise<void>) | undefined;
private state!: IResourceSqlDataSourceState;
@@ -112,6 +108,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
private resourceUseKeyId: string | null;
constructor(
private readonly networkStateService: NetworkStateService,
private readonly projectInfoResource: ProjectInfoResource,
private readonly connectionInfoResource: ConnectionInfoResource,
private readonly resourceManagerResource: ResourceManagerResource,
@@ -144,10 +141,22 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
}
isReadonly(): boolean {
return !this.isLoaded() || this.info?.isReadonly?.(this) === true;
if (!this.projectId || !this.networkStateService.state) {
return true;
}
const project = this.projectInfoResource.get(this.projectId);
return !this.isLoaded() || !project?.canEditResources;
}
isOutdated(): boolean {
if (this.projectId) {
if (this.projectInfoResource.isOutdated(this.projectId)) {
return true;
}
}
return this.resourceKey !== undefined && super.isOutdated();
}
@@ -189,10 +198,6 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
this.actions = actions;
}
setInfo(info?: IResourceInfo): void {
this.info = info;
}
setName(name: string | null): void {
name = name?.trim() ?? null;
if (!name || name === this.name) {
@@ -267,6 +272,11 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
if (this.state.resourceKey && !this.resourceUseKeyId) {
this.resourceUseKeyId = this.resourceManagerResource.useTracker.use(this.state.resourceKey);
}
if (this.projectId) {
await this.projectInfoResource.load(this.projectId);
}
await this.read();
}
@@ -5,7 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action, makeObservable, observable, untracked } from 'mobx';
import { action, makeObservable, observable } from 'mobx';
import { ConfirmationDialog } from '@cloudbeaver/core-blocks';
import { ConnectionInfoResource, IConnectionExecutionContextInfo } from '@cloudbeaver/core-connections';
@@ -13,11 +13,11 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs';
import { NotificationService } from '@cloudbeaver/core-events';
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { CachedMapAllKey, resourceKeyList, ResourceKeySimple, ResourceKeyUtils } from '@cloudbeaver/core-resource';
import { getRmResourceKey, IResourceManagerMoveData, ResourceManagerResource } from '@cloudbeaver/core-resource-manager';
import { NetworkStateService, WindowEventsService } from '@cloudbeaver/core-root';
import { resourceKeyList, ResourceKeySimple, ResourceKeyUtils } from '@cloudbeaver/core-resource';
import { IResourceManagerMoveData, ResourceManagerResource } from '@cloudbeaver/core-resource-manager';
import { NetworkStateService } from '@cloudbeaver/core-root';
import { LocalStorageSaveService } from '@cloudbeaver/core-settings';
import { createPath, getPathParent, throttle } from '@cloudbeaver/core-utils';
import { createPath, getPathParent } from '@cloudbeaver/core-utils';
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
import { NavResourceNodeService } from '@cloudbeaver/plugin-navigation-tree-rm';
import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
@@ -30,7 +30,6 @@ import { ResourceSqlDataSource } from './ResourceSqlDataSource';
import { SqlEditorTabResourceService } from './SqlEditorTabResourceService';
const RESOURCE_TAB_STATE = 'sql_editor_resource_tab_state';
const SYNC_DELAY = 5 * 60 * 1000;
@injectable()
export class ResourceSqlDataSourceBootstrap extends Bootstrap {
@@ -49,14 +48,12 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
private readonly resourceManagerResource: ResourceManagerResource,
private readonly resourceManagerScriptsService: ResourceManagerScriptsService,
private readonly projectInfoResource: ProjectInfoResource,
private readonly windowEventsService: WindowEventsService,
private readonly sqlEditorTabResourceService: SqlEditorTabResourceService,
private readonly sqlEditorService: SqlEditorService,
localStorageSaveService: LocalStorageSaveService,
) {
super();
this.dataSourceStateState = new Map();
this.focusChangeHandler = throttle(this.focusChangeHandler.bind(this), SYNC_DELAY, false);
makeObservable<this, 'dataSourceStateState' | 'createState'>(this, {
createState: action,
@@ -87,7 +84,6 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
}
register(): void | Promise<void> {
this.windowEventsService.onFocusChange.addHandler(this.focusChangeHandler.bind(this));
this.resourceManagerResource.onItemDelete.addHandler(this.resourceDeleteHandler.bind(this));
this.resourceManagerResource.onMove.addHandler(this.resourceMoveHandler.bind(this));
@@ -95,6 +91,7 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
key: ResourceSqlDataSource.key,
getDataSource: (editorId, options) => {
const dataSource = new ResourceSqlDataSource(
this.networkStateService,
this.projectInfoResource,
this.connectionInfoResource,
this.resourceManagerResource,
@@ -118,20 +115,6 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
setProperties: this.setProperties.bind(this),
});
dataSource.setInfo({
isReadonly: (dataSource: ResourceSqlDataSource) => {
if (!dataSource.resourceKey) {
return true;
}
const resourceKey = getRmResourceKey(dataSource.resourceKey);
untracked(() => this.projectInfoResource.load(CachedMapAllKey));
const project = this.projectInfoResource.get(resourceKey.projectId);
return !this.networkStateService.state || !project?.canEditResources;
},
});
return dataSource;
},
onDestroy: (_, editorId) => this.deleteState(editorId),
@@ -188,22 +171,6 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
this.dataSourceStateState.delete(editorId);
}
private async focusChangeHandler(focused: boolean) {
if (!this.resourceManagerService.enabled) {
return;
}
if (focused) {
const dataSources = this.sqlDataSourceService.dataSources
.filter(([, dataSource]) => dataSource instanceof ResourceSqlDataSource)
.map(([, dataSource]) => dataSource as ResourceSqlDataSource);
for (const dataSource of dataSources) {
dataSource.markOutdated();
}
}
}
private resourceMoveHandler(data: IResourceManagerMoveData) {
if (!this.resourceManagerService.enabled) {
return;