Refactor/cb 3214/suspense (#1530)

* CB-3214 feat: enable strict mode for development

* CB-3214 refactor: loaders

* CB-3214 fix: NodeManagerUtils.parentsFromPath remove node from parents list

* CB-3214 fix: nav tree node preload
This commit is contained in:
Alexey
2023-03-08 15:44:42 +03:00
committed by GitHub
parent 25b72cf341
commit a69fecc652
11 changed files with 75 additions and 36 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ const buttonStyles = css`
}
&[href] {
text-decoration: none;
text-decoration: none !important;
}
}
`;
@@ -18,6 +18,7 @@ const styles = css`
error {
flex: 1;
display: flex;
padding: 24px;
}
error-name {
@@ -55,6 +56,7 @@ const styles = css`
error[|inline] {
align-items: center;
height: 38px;
padding: 0;
& error-data {
display: flex;
+1 -1
View File
@@ -27,7 +27,7 @@ const styles = css`
a[|wrapper] {
&, &:hover, &:focus, &:active {
color: inherit;
text-decoration: none;
text-decoration: none !important;
outline: none;
}
}
@@ -328,16 +328,18 @@ export function useResource<
// throw refObj.loadingPromise;
// }
if (this.loading) {
throw this.resource.waitLoad();
}
if (!this.isLoaded()) {
if (this.loading) {
throw this.resource.waitLoad();
}
if (this.canLoad) {
throw refObj.load();
}
if (this.canLoad) {
throw refObj.load();
}
if (this.isError()) {
throw this.exception;
if (this.isError()) {
throw this.exception;
}
}
//---------------------
@@ -358,7 +360,7 @@ export function useResource<
}
if (this.isError()) {
return false;
return true;
}
return this.resource.isLoaded(propertiesRef.key, propertiesRef.includes);
@@ -7,9 +7,9 @@
*/
import { injectable } from '@cloudbeaver/core-di';
import {
GraphQLService, CachedMapResource, ResourceKey, isResourceKeyList, resourceKeyList, ResourceKeyUtils, ResourceKeyList
} from '@cloudbeaver/core-sdk';
import { ExecutorInterrupter } from '@cloudbeaver/core-executor';
import { GraphQLService, CachedMapResource, ResourceKey, isResourceKeyList, resourceKeyList, ResourceKeyUtils, ResourceKeyList, DetailsError } from '@cloudbeaver/core-sdk';
import { flat } from '@cloudbeaver/core-utils';
import type { DBObject } from './EntityTypes';
import { NavNodeInfoResource } from './NavNodeInfoResource';
@@ -39,6 +39,29 @@ export class DBObjectResource extends CachedMapResource<string, DBObject> {
this.navNodeInfoResource.outdateResource(this);
this.navNodeInfoResource.deleteInResource(this);
this.navNodeInfoResource.onDataOutdated.addHandler(this.outdateChildren.bind(this));
this.beforeLoad.addHandler(async (originalKey, context) => {
await this.navTreeResource.waitLoad();
if (isDBObjectParentKey(originalKey)) {
await this.navTreeResource.load(originalKey.mark);
return;
}
const key = this.transformParam(originalKey);
const parents = [...new Set(ResourceKeyUtils
.mapArray(key, nodeId => this.navNodeInfoResource.get(nodeId)?.parentId)
.filter<string>((nodeId): nodeId is string => nodeId !== undefined))];
const children = await this.navTreeResource.load(resourceKeyList(parents));
if (
ResourceKeyUtils.count(key) > 0
&& !ResourceKeyUtils.includes(resourceKeyList(flat(children)), key)
) {
ExecutorInterrupter.interrupt(context);
const cause = new DetailsError(`Entity not found: ${key.toString()}`);
throw this.markDataError(cause, key);
}
});
}
protected async loader(originalKey: ResourceKey<string>): Promise<Map<string, DBObject>> {
@@ -81,8 +81,10 @@ export class NavNodeInfoResource extends CachedMapResource<string, NavNode> {
});
if (keyList.length > 0) {
this.set(resourceKeyList(keyList), values);
const keyListKey = resourceKeyList(keyList);
this.set(keyListKey, values);
this.markUpdated(key);
this.cleanError(ResourceKeyUtils.join(keyListKey, key));
this.onItemAdd.execute(key);
}
}
@@ -11,7 +11,7 @@ import { action, computed, makeObservable, observable, runInAction } from 'mobx'
import { CoreSettingsService } from '@cloudbeaver/core-app';
import { AppAuthService, UserInfoResource } from '@cloudbeaver/core-authentication';
import { injectable } from '@cloudbeaver/core-di';
import { Executor, ExecutorInterrupter, IExecutor } from '@cloudbeaver/core-executor';
import { Executor, ExecutorInterrupter, IExecutionContext, IExecutor } from '@cloudbeaver/core-executor';
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { SessionDataResource } from '@cloudbeaver/core-root';
import {
@@ -141,8 +141,13 @@ export class NavTreeResource extends CachedMapResource<string, string[]> {
if (parent !== undefined && !children.includes(next)) {
return false;
}
await this.scheduler.waitRelease(next);
children = await this.load(next);
if (this.isLoadable(next)) {
children = await this.load(next);
} else {
children = this.get(next) || [];
}
parent = next;
}
@@ -374,6 +379,7 @@ export class NavTreeResource extends CachedMapResource<string, string[]> {
const children = this.data.get(key) || [];
childrenToRemove.push(...children.filter(navNodeId => !value.includes(navNodeId)));
this.dataSet(key, value);
this.cleanError(resourceKeyList(value));
});
this.delete(resourceKeyList(childrenToRemove));
@@ -405,9 +411,9 @@ export class NavTreeResource extends CachedMapResource<string, string[]> {
}
protected async preLoadData(
key: ResourceKey<string>
key: ResourceKey<string>,
contexts: IExecutionContext<ResourceKey<string>>
): Promise<void> {
// this.performUpdate(key, undefined, async key => {
await ResourceKeyUtils.forEachAsync(key, async nodeId => {
if (!this.navNodeInfoResource.has(nodeId) && nodeId !== ROOT_NODE_PATH) {
await this.navNodeInfoResource.loadNodeParents(nodeId);
@@ -416,10 +422,11 @@ export class NavTreeResource extends CachedMapResource<string, string[]> {
if (!preloaded) {
const cause = new DetailsError(`Entity not found: ${nodeId}`);
throw new ResourceError(this, key, undefined, 'Entity not found', { cause });
const error = new ResourceError(this, key, undefined, 'Entity not found', { cause });
ExecutorInterrupter.interrupt(contexts);
throw this.markDataError(error, key);
}
});
// });
}
protected async loader(key: ResourceKey<string>): Promise<Map<string, string[]>> {
@@ -9,7 +9,7 @@
import { observable, toJS } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import { Executor, IExecutor } from '@cloudbeaver/core-executor';
import { Executor, IExecutionContext, IExecutor } from '@cloudbeaver/core-executor';
import { ProjectsService } from '@cloudbeaver/core-projects';
import { DataSynchronizationService, ServerEventId } from '@cloudbeaver/core-root';
import { CachedMapResource, CachedResourceIncludeArgs, GetResourceListQueryVariables, GraphQLService, ICachedMapResourceMetadata, isResourceKeyList, ResourceKey, resourceKeyList, ResourceKeyUtils, RmResource } from '@cloudbeaver/core-sdk';
@@ -255,6 +255,7 @@ export class ResourceManagerResource
protected async preLoadData(
key: ResourceKey<IResourceManagerParams>,
contexts: IExecutionContext<ResourceKey<IResourceManagerParams>>,
refresh: boolean,
includes: CachedResourceIncludeArgs<RmResourceInfo, ResourceInfoIncludes>
): Promise<void> {
@@ -79,7 +79,6 @@ export abstract class CachedDataResource<
param: TParam,
context?: T
): Promise<CachedResourceValueIncludes<TData, T>> {
await this.preLoadData(param, false, context);
await this.loadData(param, true, context);
return this.data as CachedResourceValueIncludes<TData, T>;
}
@@ -88,7 +87,6 @@ export abstract class CachedDataResource<
param: TParam,
context?: T
): Promise<CachedResourceValueIncludes<TData, T>> {
await this.preLoadData(param, false, context);
await this.loadData(param, false, context);
return this.data as CachedResourceValueIncludes<TData, T>;
}
@@ -240,7 +240,7 @@ export abstract class CachedMapResource<
exception: Error,
key: ResourceKey<TKey>,
includes?: CachedResourceIncludeArgs<TValue, TContext>
): void {
): ResourceError {
if (this.isAlias(key) && !this.isAliasLoaded(key)) {
this.loadedKeys.push(key);
}
@@ -256,6 +256,7 @@ export abstract class CachedMapResource<
});
this.onDataError.execute({ param: key, exception });
return exception as ResourceError;
}
markOutdated(): void;
@@ -405,7 +406,6 @@ export abstract class CachedMapResource<
key: ResourceKey<TKey>,
includes?: T
): Promise<Array<CachedResourceValueIncludes<TValue, T>> | CachedResourceValueIncludes<TValue, T>> {
await this.preLoadData(key, false, includes);
await this.loadData(key, true, includes);
return this.get(key) as Array<CachedResourceValueIncludes<TValue, T>> | CachedResourceValueIncludes<TValue, T>;
}
@@ -426,7 +426,6 @@ export abstract class CachedMapResource<
key: ResourceKey<TKey>,
includes?: T
): Promise<Array<CachedResourceValueIncludes<TValue, T>> | CachedResourceValueIncludes<TValue, T>> {
await this.preLoadData(key, false, includes);
await this.loadData(key, false, includes);
return this.get(key) as Array<CachedResourceValueIncludes<TValue, T>> | CachedResourceValueIncludes<TValue, T>;
}
@@ -9,7 +9,7 @@
import { observable, makeObservable, action, computed, toJS } from 'mobx';
import { Dependency } from '@cloudbeaver/core-di';
import { Executor, ExecutorInterrupter, IExecutor, IExecutorHandler, ISyncExecutor, SyncExecutor, TaskScheduler } from '@cloudbeaver/core-executor';
import { ExecutionContext, Executor, ExecutorInterrupter, IExecutor, IExecutorHandler, IExecutionContextProvider, ISyncExecutor, SyncExecutor, TaskScheduler } from '@cloudbeaver/core-executor';
import { MetadataMap, uuid } from '@cloudbeaver/core-utils';
import { ResourceError } from './ResourceError';
@@ -336,6 +336,10 @@ export abstract class CachedResource<
abstract isLoaded(param: TParam, context?: TContext): boolean;
isLoadable(param: TParam, context?: TContext): boolean {
return !this.isLoaded(param, context) || this.isOutdated(param);
}
isAlias(key: TParam): boolean {
return this.paramAliases.some(alias => {
if ('isEqual' in alias && alias.isEqual) {
@@ -425,7 +429,7 @@ export abstract class CachedResource<
});
}
markDataError(exception: Error, param: TParam, context?: TContext): void {
markDataError(exception: Error, param: TParam, context?: TContext): ResourceError {
if (this.isAlias(param) && !this.isAliasLoaded(param)) {
this.loadedKeys.push(param);
}
@@ -437,6 +441,7 @@ export abstract class CachedResource<
metadata.outdated = false;
});
this.onDataError.execute({ param, exception });
return exception as ResourceError;
}
cleanError(param: TParam): void {
@@ -532,13 +537,11 @@ export abstract class CachedResource<
}
async refresh(param: TParam, context?: TContext): Promise<any> {
await this.preLoadData(param, false, context);
await this.loadData(param, true, context);
return this.data;
}
async load(param: TParam, context?: TContext): Promise<any> {
await this.preLoadData(param, false, context);
await this.loadData(param, false, context);
return this.data;
}
@@ -585,7 +588,6 @@ export abstract class CachedResource<
}
}
});
}
protected setData(data: TData): void {
@@ -721,8 +723,9 @@ export abstract class CachedResource<
protected async preLoadData(
param: TParam,
contexts: IExecutionContextProvider<TParam>,
refresh: boolean,
context?: TContext
context?: TContext,
): Promise<void> { }
protected async loadData(
@@ -730,15 +733,17 @@ export abstract class CachedResource<
refresh: boolean,
context?: TContext
): Promise<void> {
const contexts = new ExecutionContext(param);
await this.preLoadData(param, contexts, refresh, context);
if (!refresh) {
await this.scheduler.waitRelease(param);
if (this.isLoaded(param, context) && !this.isOutdated(param)) {
if (!this.isLoadable(param, context)) {
return;
}
}
const contexts = await this.beforeLoad.execute(param);
await this.beforeLoad.execute(param, contexts);
if (ExecutorInterrupter.isInterrupted(contexts) && !refresh) {
return;
@@ -749,7 +754,7 @@ export abstract class CachedResource<
param,
async () => {
// repeated because previous task maybe has been load requested data
if (this.isLoaded(param, context) && !this.isOutdated(param)) {
if (!this.isLoadable(param, context)) {
return;
}