mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
792a430f4f |
@@ -9,12 +9,11 @@ import { runInAction } from 'mobx';
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import {
|
||||
CACHED_RESOURCE_DEFAULT_PAGE_LIMIT,
|
||||
CACHED_RESOURCE_DEFAULT_PAGE_OFFSET,
|
||||
CachedMapAllKey,
|
||||
CachedMapResource,
|
||||
CachedResourceOffsetPageKey,
|
||||
CachedResourceOffsetPageListKey,
|
||||
getOffsetPageKeyInfo,
|
||||
isResourceAlias,
|
||||
type ResourceKey,
|
||||
resourceKeyList,
|
||||
@@ -260,19 +259,11 @@ export class UsersResource extends CachedMapResource<string, AdminUser, UserReso
|
||||
|
||||
usersList.push(user);
|
||||
} else {
|
||||
const pageKey =
|
||||
this.aliases.isAlias(originalKey, CachedResourceOffsetPageKey) || this.aliases.isAlias(originalKey, CachedResourceOffsetPageListKey);
|
||||
const { isPageListKey, offset, limit } = getOffsetPageKeyInfo(this, originalKey);
|
||||
const filterKey = this.aliases.isAlias(originalKey, UsersResourceFilterKey);
|
||||
let offset = CACHED_RESOURCE_DEFAULT_PAGE_OFFSET;
|
||||
let limit = CACHED_RESOURCE_DEFAULT_PAGE_LIMIT;
|
||||
let userIdMask: string | undefined;
|
||||
let enabledState: boolean | undefined;
|
||||
|
||||
if (pageKey) {
|
||||
offset = pageKey.options.offset;
|
||||
limit = pageKey.options.limit;
|
||||
}
|
||||
|
||||
if (filterKey) {
|
||||
userIdMask = filterKey.options.userId;
|
||||
enabledState = filterKey.options.enabledState;
|
||||
@@ -294,7 +285,9 @@ export class UsersResource extends CachedMapResource<string, AdminUser, UserReso
|
||||
usersList.push(...users);
|
||||
|
||||
pages.push([
|
||||
CachedResourceOffsetPageListKey(offset, users.length).setParent(filterKey!),
|
||||
isPageListKey
|
||||
? CachedResourceOffsetPageListKey(offset, users.length).setParent(filterKey)
|
||||
: CachedResourceOffsetPageKey(offset, users.length).setParent(filterKey),
|
||||
users.map(user => user.userId),
|
||||
users.length === limit,
|
||||
]);
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
CachedResourceOffsetPageKey,
|
||||
CachedResourceOffsetPageListKey,
|
||||
CachedResourceOffsetPageTargetKey,
|
||||
getOffsetPageKeyInfo,
|
||||
isResourceAlias,
|
||||
type ResourceKey,
|
||||
resourceKeyList,
|
||||
@@ -96,16 +97,8 @@ export class DBObjectResource extends CachedMapResource<string, DBObject> {
|
||||
}
|
||||
|
||||
protected async loader(originalKey: ResourceKey<string>): Promise<Map<string, DBObject>> {
|
||||
let limit = this.navTreeResource.childrenLimit;
|
||||
let offset = CACHED_RESOURCE_DEFAULT_PAGE_OFFSET;
|
||||
const parentKey = this.aliases.isAlias(originalKey, DBObjectParentKey);
|
||||
const pageKey =
|
||||
this.aliases.isAlias(originalKey, CachedResourceOffsetPageKey) || this.aliases.isAlias(originalKey, CachedResourceOffsetPageListKey);
|
||||
|
||||
if (pageKey) {
|
||||
limit = pageKey.options.limit;
|
||||
offset = pageKey.options.offset;
|
||||
}
|
||||
const { isPageListKey, offset, limit } = getOffsetPageKeyInfo(this, originalKey, undefined, this.navTreeResource.childrenLimit);
|
||||
|
||||
if (parentKey) {
|
||||
const nodeId = parentKey.options.parentId;
|
||||
@@ -116,11 +109,11 @@ export class DBObjectResource extends CachedMapResource<string, DBObject> {
|
||||
this.set(resourceKeyList(keys), dbObjects);
|
||||
|
||||
this.offsetPagination.setPage(
|
||||
CachedResourceOffsetPageKey(offset, limit).setParent(CachedResourceOffsetPageTargetKey(originalKey)),
|
||||
isPageListKey
|
||||
? CachedResourceOffsetPageListKey(offset, limit).setParent(parentKey || CachedResourceOffsetPageTargetKey(nodeId))
|
||||
: CachedResourceOffsetPageKey(offset, limit).setParent(parentKey || CachedResourceOffsetPageTargetKey(nodeId)),
|
||||
keys,
|
||||
this.navTreeResource.offsetPagination.hasNextPage(
|
||||
CachedResourceOffsetPageKey(offset, limit).setParent(CachedResourceOffsetPageTargetKey(nodeId)),
|
||||
),
|
||||
keys.length === limit,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ import { injectable } from '@cloudbeaver/core-di';
|
||||
import { Executor, ExecutorInterrupter, IExecutionContext, IExecutor } from '@cloudbeaver/core-executor';
|
||||
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
|
||||
import {
|
||||
CACHED_RESOURCE_DEFAULT_PAGE_OFFSET,
|
||||
CachedMapAllKey,
|
||||
CachedMapResource,
|
||||
CachedResourceOffsetPageKey,
|
||||
CachedResourceOffsetPageListKey,
|
||||
CachedResourceOffsetPageTargetKey,
|
||||
getOffsetPageKeyInfo,
|
||||
type ICachedResourceMetadata,
|
||||
isResourceAlias,
|
||||
isResourceKeyList,
|
||||
@@ -275,9 +275,10 @@ export class NavTreeResource extends CachedMapResource<string, string[], Record<
|
||||
parts.splice(parts.length - 1, 1, name);
|
||||
const path = parts.join('/');
|
||||
|
||||
this.markTreeOutdated(parentId);
|
||||
this.markOutdated(parentId);
|
||||
this.markLoaded(node.id);
|
||||
this.onDataOutdated.execute(parentId);
|
||||
|
||||
return path;
|
||||
} finally {
|
||||
this.markLoading(node.id, false);
|
||||
@@ -461,27 +462,25 @@ export class NavTreeResource extends CachedMapResource<string, string[], Record<
|
||||
}
|
||||
|
||||
protected async loader(originalKey: ResourceKey<string>): Promise<Map<string, string[]>> {
|
||||
const pageKey =
|
||||
this.aliases.isAlias(originalKey, CachedResourceOffsetPageKey) || this.aliases.isAlias(originalKey, CachedResourceOffsetPageListKey);
|
||||
const { isPageListKey, pageTargetKey, offset, limit } = getOffsetPageKeyInfo(this, originalKey, undefined, this.childrenLimit);
|
||||
const allKey = this.aliases.isAlias(originalKey, CachedMapAllKey);
|
||||
const pageTarget = this.aliases.isAlias(originalKey, CachedResourceOffsetPageTargetKey);
|
||||
|
||||
if (allKey) {
|
||||
throw new Error('Loading all nodes is prohibited');
|
||||
}
|
||||
|
||||
const offset = pageKey?.options.offset ?? CACHED_RESOURCE_DEFAULT_PAGE_OFFSET;
|
||||
const limit = pageKey?.options.limit ?? this.childrenLimit;
|
||||
const values: NavNodeChildrenQuery[] = [];
|
||||
const pages: Parameters<typeof this.offsetPagination.setPage>[] = [];
|
||||
|
||||
await ResourceKeyUtils.forEachAsync(originalKey, async key => {
|
||||
const nodeId = pageTarget?.options?.target ?? key;
|
||||
const nodeId = pageTargetKey ?? key;
|
||||
const navNodeChildren = await this.loadNodeChildren(nodeId, offset, limit);
|
||||
values.push(navNodeChildren);
|
||||
|
||||
pages.push([
|
||||
CachedResourceOffsetPageKey(offset, navNodeChildren.navNodeChildren.length).setParent(CachedResourceOffsetPageTargetKey(nodeId)),
|
||||
isPageListKey
|
||||
? CachedResourceOffsetPageListKey(offset, navNodeChildren.navNodeChildren.length).setParent(CachedResourceOffsetPageTargetKey(nodeId))
|
||||
: CachedResourceOffsetPageKey(offset, navNodeChildren.navNodeChildren.length).setParent(CachedResourceOffsetPageTargetKey(nodeId)),
|
||||
navNodeChildren.navNodeChildren.map(node => node.id),
|
||||
navNodeChildren.navNodeChildren.length === limit,
|
||||
]);
|
||||
|
||||
@@ -34,7 +34,7 @@ import { isResourceAlias } from './ResourceAlias';
|
||||
import { ResourceError } from './ResourceError';
|
||||
import type { ResourceKey, ResourceKeyFlat } from './ResourceKey';
|
||||
import { resourceKeyAlias } from './ResourceKeyAlias';
|
||||
import { resourceKeyList } from './ResourceKeyList';
|
||||
import { isResourceKeyList, resourceKeyList } from './ResourceKeyList';
|
||||
import { resourceKeyListAlias } from './ResourceKeyListAlias';
|
||||
import { ResourceOffsetPagination } from './ResourceOffsetPagination';
|
||||
|
||||
@@ -91,41 +91,58 @@ export abstract class CachedResource<
|
||||
this.aliases.add(CachedResourceParamKey, () => defaultKey);
|
||||
this.aliases.add(CachedResourceListEmptyKey, () => resourceKeyList([]));
|
||||
this.aliases.add(CachedResourceOffsetPageTargetKey, key => key.options.target);
|
||||
this.aliases.add(CachedResourceOffsetPageKey, key => {
|
||||
const keys = [];
|
||||
const pageInfo = this.offsetPagination.getPageInfo(key);
|
||||
this.aliases.add(
|
||||
CachedResourceOffsetPageListKey,
|
||||
key => key.parent! as any,
|
||||
(param, key) => {
|
||||
if (!isResourceKeyList(key)) {
|
||||
return key as any;
|
||||
}
|
||||
|
||||
if (pageInfo) {
|
||||
const from = key.options.offset;
|
||||
const to = key.options.offset + key.options.limit;
|
||||
const keys = new Set<any>();
|
||||
const pageInfo = this.offsetPagination.getPageInfo(param);
|
||||
|
||||
for (const page of pageInfo.pages) {
|
||||
if (page.isHasCommonSegment(from, to)) {
|
||||
keys.push(...page.get(from, to));
|
||||
if (pageInfo) {
|
||||
const from = param.options.offset;
|
||||
const to = param.options.offset + param.options.limit;
|
||||
|
||||
for (const page of pageInfo.pages) {
|
||||
if (page.isHasCommonSegment(from, to)) {
|
||||
for (const pageKey of page.get(from, to)) {
|
||||
keys.add(pageKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resourceKeyList(key.filter(value => keys.has(value)));
|
||||
},
|
||||
);
|
||||
this.aliases.add(
|
||||
CachedResourceOffsetPageKey,
|
||||
key => key.parent! as any,
|
||||
(param, key) => {
|
||||
if (!isResourceKeyList(key)) {
|
||||
return key as any;
|
||||
}
|
||||
|
||||
// todo: return single element?
|
||||
return resourceKeyList([...new Set(keys)]);
|
||||
});
|
||||
this.aliases.add(CachedResourceOffsetPageListKey, key => {
|
||||
const keys = [];
|
||||
const pageInfo = this.offsetPagination.getPageInfo(key);
|
||||
const keys = new Set<any>();
|
||||
const pageInfo = this.offsetPagination.getPageInfo(param);
|
||||
|
||||
if (pageInfo) {
|
||||
const from = key.options.offset;
|
||||
const to = key.options.offset + key.options.limit;
|
||||
if (pageInfo) {
|
||||
const from = param.options.offset;
|
||||
const to = param.options.offset + param.options.limit;
|
||||
|
||||
for (const page of pageInfo.pages) {
|
||||
if (page.isHasCommonSegment(from, to)) {
|
||||
keys.push(...page.get(from, to));
|
||||
for (const page of pageInfo.pages) {
|
||||
if (page.isHasCommonSegment(from, to)) {
|
||||
for (const pageKey of page.get(from, to)) {
|
||||
keys.add(pageKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourceKeyList([...new Set(keys)]);
|
||||
});
|
||||
return resourceKeyList(key.filter(value => keys.has(value)));
|
||||
},
|
||||
);
|
||||
|
||||
// this.logger.spy(this.beforeLoad, 'beforeLoad');
|
||||
// this.logger.spy(this.onDataOutdated, 'onDataOutdated');
|
||||
@@ -329,6 +346,7 @@ export abstract class CachedResource<
|
||||
}
|
||||
|
||||
const pageKey = this.aliases.isAlias(param, CachedResourceOffsetPageKey) || this.aliases.isAlias(param, CachedResourceOffsetPageListKey);
|
||||
|
||||
if (pageKey) {
|
||||
const pageInfo = this.offsetPagination.getPageInfo(pageKey);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export abstract class ResourceAlias<TKey, TOptions extends ResourceAliasOptions>
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setParent(parent: ResourceAlias<TKey, any>): this {
|
||||
setParent(parent: ResourceAlias<TKey, any> | undefined): this {
|
||||
parent = this.parent ? this.parent.setParent(parent) : parent;
|
||||
const copy = new (this.constructor as any)(this.id, this.options, parent) as this;
|
||||
return copy;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { toJS } from 'mobx';
|
||||
|
||||
import { isResourceAlias, type ResourceAlias, ResourceAliasFactory, type ResourceAliasOptions } from './ResourceAlias';
|
||||
import type { ResourceKey } from './ResourceKey';
|
||||
import type { ResourceKey, ResourceKeySimple } from './ResourceKey';
|
||||
import type { ResourceKeyAlias } from './ResourceKeyAlias';
|
||||
import { isResourceKeyList, ResourceKeyList } from './ResourceKeyList';
|
||||
import type { ResourceKeyListAlias } from './ResourceKeyListAlias';
|
||||
@@ -16,14 +16,25 @@ import type { ResourceLogger } from './ResourceLogger';
|
||||
|
||||
export type IParamAlias<TKey> = {
|
||||
id: string;
|
||||
getAlias: (param: ResourceAlias<TKey, any>) => ResourceKey<TKey>;
|
||||
getAlias: ResourceAliasResolver<TKey, any>;
|
||||
transformKey?: ResourceAliasKeyTransformer<TKey, any>;
|
||||
};
|
||||
|
||||
export type ResourceAliasResolver<TKey, TOptions extends ResourceAliasOptions> = (param: ResourceAlias<TKey, TOptions>) => ResourceKey<TKey>;
|
||||
|
||||
export type ResourceAliasKeyTransformer<TKey, TOptions extends ResourceAliasOptions> = <T extends ResourceKeySimple<TKey>>(
|
||||
param: ResourceAlias<TKey, TOptions>,
|
||||
key: T,
|
||||
) => T;
|
||||
|
||||
export class ResourceAliases<TKey> {
|
||||
protected paramAliases: Array<IParamAlias<TKey>>;
|
||||
private captureAliasGetterExecution: boolean;
|
||||
|
||||
constructor(private readonly logger: ResourceLogger, private readonly validateKey: (key: TKey) => boolean) {
|
||||
constructor(
|
||||
private readonly logger: ResourceLogger,
|
||||
private readonly validateKey: (key: TKey) => boolean,
|
||||
) {
|
||||
this.paramAliases = [];
|
||||
|
||||
this.captureAliasGetterExecution = false;
|
||||
@@ -52,21 +63,23 @@ export class ResourceAliases<TKey> {
|
||||
|
||||
add<TOptions extends ResourceAliasOptions>(
|
||||
param: ResourceAlias<TKey, TOptions> | ResourceAliasFactory<TKey, TOptions>,
|
||||
getAlias: (param: ResourceAlias<TKey, TOptions>) => ResourceKey<TKey>,
|
||||
getAlias: ResourceAliasResolver<TKey, TOptions>,
|
||||
transformKey?: ResourceAliasKeyTransformer<TKey, TOptions>,
|
||||
): void {
|
||||
this.paramAliases.push({ id: param.id, getAlias });
|
||||
this.paramAliases.push({ id: param.id, getAlias, transformKey });
|
||||
}
|
||||
|
||||
replace<TOptions extends ResourceAliasOptions>(
|
||||
param: ResourceAlias<TKey, TOptions> | ResourceAliasFactory<TKey, TOptions>,
|
||||
getAlias: (param: ResourceAlias<TKey, TOptions>) => ResourceKey<TKey>,
|
||||
getAlias: ResourceAliasResolver<TKey, TOptions>,
|
||||
transformKey?: ResourceAliasKeyTransformer<TKey, TOptions>,
|
||||
): void {
|
||||
const indexOf = this.paramAliases.findIndex(aliasInfo => aliasInfo.id === param.id);
|
||||
|
||||
if (indexOf === -1) {
|
||||
this.add(param, getAlias);
|
||||
this.add(param, getAlias, transformKey);
|
||||
} else {
|
||||
this.paramAliases.splice(indexOf, 1, { id: param.id, getAlias });
|
||||
this.paramAliases.splice(indexOf, 1, { id: param.id, getAlias, transformKey });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +124,7 @@ export class ResourceAliases<TKey> {
|
||||
transformToKey(param: ResourceKey<TKey>): TKey | ResourceKeyList<TKey> {
|
||||
let deep = 0;
|
||||
|
||||
const transforms: Array<{ key: ResourceKeyAlias<TKey, any> | ResourceKeyListAlias<TKey, any>; alias: IParamAlias<TKey> }> = [];
|
||||
while (deep < 10) {
|
||||
if (!this.validateResourceKey(param)) {
|
||||
let paramString = JSON.stringify(toJS(param));
|
||||
@@ -124,7 +138,15 @@ export class ResourceAliases<TKey> {
|
||||
if (isResourceAlias(param)) {
|
||||
for (const alias of this.paramAliases) {
|
||||
if (alias.id === param.id) {
|
||||
param = this.captureGetAlias(alias, param);
|
||||
transforms.push({ key: param, alias });
|
||||
const nextParam = this.captureGetAlias(alias, param);
|
||||
|
||||
if (isResourceAlias(nextParam)) {
|
||||
param = nextParam.setParent(param);
|
||||
} else {
|
||||
param = nextParam;
|
||||
}
|
||||
|
||||
deep++;
|
||||
break;
|
||||
}
|
||||
@@ -141,6 +163,11 @@ export class ResourceAliases<TKey> {
|
||||
if (isResourceAlias(param)) {
|
||||
throw new Error(`Alias ${param.toString()} is not registered in ${this.logger.getName()}`);
|
||||
}
|
||||
|
||||
for (const { key, alias } of transforms) {
|
||||
param = alias.transformKey ? alias.transformKey(key, param) : param;
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { DefaultValueGetter, isPrimitive, MetadataMap } from '@cloudbeaver/core-
|
||||
|
||||
import { CachedResourceOffsetPageKey, CachedResourceOffsetPageListKey } from './CachedResourceOffsetPageKeys';
|
||||
import type { ICachedResourceMetadata } from './ICachedResourceMetadata';
|
||||
import { isResourceAlias } from './ResourceAlias';
|
||||
import { isResourceAlias, ResourceAlias } from './ResourceAlias';
|
||||
import type { ResourceAliases } from './ResourceAliases';
|
||||
import type { ResourceKey, ResourceKeyFlat } from './ResourceKey';
|
||||
import { isResourceKeyList, ResourceKeyList } from './ResourceKeyList';
|
||||
@@ -111,6 +111,8 @@ export class ResourceMetadata<TKey, TMetadata extends ICachedResourceMetadata> {
|
||||
if (this.some(param, predicate)) {
|
||||
result = true;
|
||||
}
|
||||
} else if (predicate(this.get(param))) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,11 +196,11 @@ export class ResourceMetadata<TKey, TMetadata extends ICachedResourceMetadata> {
|
||||
if (isResourceAlias(key)) {
|
||||
key = this.aliases.transformToAlias(key);
|
||||
|
||||
if (this.aliases.isAlias(key, CachedResourceOffsetPageKey) || this.aliases.isAlias(key, CachedResourceOffsetPageListKey)) {
|
||||
if (isResourceAlias(key, CachedResourceOffsetPageKey) || isResourceAlias(key, CachedResourceOffsetPageListKey)) {
|
||||
return this.getMetadataKeyRef(key.parent as any);
|
||||
}
|
||||
|
||||
return key.toString() as TKey;
|
||||
return (key as ResourceAlias<TKey, any>).toString() as TKey;
|
||||
}
|
||||
|
||||
if (isPrimitive(key)) {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { CachedResource } from './CachedResource';
|
||||
import {
|
||||
CACHED_RESOURCE_DEFAULT_PAGE_LIMIT,
|
||||
CACHED_RESOURCE_DEFAULT_PAGE_OFFSET,
|
||||
CachedResourceOffsetPageKey,
|
||||
CachedResourceOffsetPageListKey,
|
||||
CachedResourceOffsetPageTargetKey,
|
||||
} from './CachedResourceOffsetPageKeys';
|
||||
import { CachedResourceKey } from './IResource';
|
||||
|
||||
interface IOffsetPageKeyInfo {
|
||||
limit: number;
|
||||
offset: number;
|
||||
isPageListKey: boolean;
|
||||
pageTargetKey?: any;
|
||||
}
|
||||
|
||||
export function getOffsetPageKeyInfo(
|
||||
resource: CachedResource<any, any, any, any>,
|
||||
originalKey: CachedResourceKey<any>,
|
||||
offset = CACHED_RESOURCE_DEFAULT_PAGE_OFFSET,
|
||||
limit = CACHED_RESOURCE_DEFAULT_PAGE_LIMIT,
|
||||
): IOffsetPageKeyInfo {
|
||||
const pageListKey = resource.aliases.isAlias(originalKey, CachedResourceOffsetPageListKey);
|
||||
const pageKey = resource.aliases.isAlias(originalKey, CachedResourceOffsetPageKey) || pageListKey;
|
||||
const pageTargetKey = resource.aliases.isAlias(originalKey, CachedResourceOffsetPageTargetKey);
|
||||
|
||||
if (pageKey) {
|
||||
limit = pageKey.options.limit;
|
||||
offset = pageKey.options.offset;
|
||||
}
|
||||
|
||||
return {
|
||||
limit,
|
||||
offset,
|
||||
isPageListKey: !!pageListKey,
|
||||
pageTargetKey: pageTargetKey?.options.target,
|
||||
};
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export {
|
||||
getNextPageOffset,
|
||||
type ICachedResourceOffsetPageOptions,
|
||||
} from './Resource/CachedResourceOffsetPageKeys';
|
||||
export * from './Resource/getOffsetPageKeyInfo';
|
||||
export * from './Resource/CachedTreeResource/CachedTreeResource';
|
||||
export * from './Resource/CachedTreeResource/ICachedTreeMoveData';
|
||||
export * from './Resource/ICachedResourceMetadata';
|
||||
|
||||
Reference in New Issue
Block a user