@finos/legend-application-studio
Version:
Legend Studio application core
216 lines • 12.4 kB
TypeScript
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type GeneratorFn, LogEvent, ActionState } from '@finos/legend-shared';
import type { EditorStore } from './EditorStore.js';
import type { EditorGraphState } from './EditorGraphState.js';
import type { Entity } from '@finos/legend-storage';
import { type EntityChangeConflictResolution, EntityChangeConflict, EntityDiff } from '@finos/legend-server-sdlc';
import { ObserverContext } from '@finos/legend-graph';
declare class RevisionChangeDetectionState {
editorStore: EditorStore;
graphState: EditorGraphState;
changes: EntityDiff[];
entityHashesIndex: Map<string, string>;
isBuildingEntityHashesIndex: boolean;
entities: Entity[];
currentEntityHashesIndex: Map<string, string>;
setEntityHashesIndex(hashesIndex: Map<string, string>): void;
setIsBuildingEntityHashesIndex(building: boolean): void;
setEntities(entities: Entity[]): void;
constructor(editorStore: EditorStore, graphState: EditorGraphState);
computeChanges(quiet?: boolean): GeneratorFn<void>;
computeChangesInTextMode(currentEntities: Entity[], quiet?: boolean): GeneratorFn<void>;
buildEntityHashesIndex(entities: Entity[], logEvent: LogEvent, quiet?: boolean): GeneratorFn<void>;
}
/**
* In the SDLC flow of the app, there are several important revision that we want to keep track of. See diagram below:
*
* (1. PJL)
* |
* |
* (2. CRB) ------- (3. CRH) ------ (4. CRL)
* |
* |
* (5. WSB) ------- (6. WSH) ------ (7. WSL)
* |
* ... (earlier revisions in the project)
*
* Annotations:
* 1. PJL: Project HEAD revision
* 2. CRB: Conflict resolution BASE revision
* 3. CRH: Conflict resolution HEAD revision
* 4. CRL: Conflict resolution LIVE revision (i.e. local graph state in conflict resolution mode)
* 5. WSB: Workspace BASE revision
* 6. WSH: Workspace HEAD revision
* 7. WSL: Workspace LIVE revision (i.e. local graph state in standard mode)
*/
export declare class ChangeDetectionState {
editorStore: EditorStore;
graphState: EditorGraphState;
graphObserveState: ActionState;
initState: ActionState;
/**
* Keep the list of disposers to deactivate `keepAlive` for computed value of element hash code.
* See {@link preComputeGraphElementHashes} for more details
*/
private graphElementHashCodeKeepAliveComputationDisposers;
private changeDetectionReaction?;
/**
* [1. PJL] Store the entities from project HEAD (i.e. project latest revision)
* This can be used to compute changes for a review as well as changes and potential conflicts when updating workspace
*/
projectLatestRevisionState: RevisionChangeDetectionState;
/**
* [2. CRB] Store the entities from the BASE revision of workspace with conflict resolution (this is different from the current workspace the user is on)
* NOTE: the flow for conflict resolution is briefly like this (assume current user workspace is `w1`):
* 1. When the user chooses to update workspace `w1`, the backend will compute changes between `w1` HEAD and `w1` BASE
* 2. Create a new conflict resolution branch on top of project HEAD
* 3. Apply the changes on this branch.
*
* So we now have 2 branchs normal `w1` and `w1` in conflict resolution. From the user perspective, they might not need to know this
* So in the app, we have to check for the existence of the conflict resolution branch and make it supercede the original `w1` branch
*
* This branch, thus, will be used to show the users all the changes they have on top of conflict resolution BASE revision
* during conflict resolution stage
*/
conflictResolutionBaseRevisionState: RevisionChangeDetectionState;
/**
* [3. CRH] Store the entities from the conflict resolution HEAD revision
* This is used for computing the live diff, so that when we mark conflicts as resolved and accept conflict resolution
* we can compute the entity changes
*/
conflictResolutionHeadRevisionState: RevisionChangeDetectionState;
/**
* [5. WSB] Store the entities from workspace BASE revision
* This can be used for conflict resolution
*/
workspaceBaseRevisionState: RevisionChangeDetectionState;
/**
* [6. WSH] Store the entities from LOCAL workspace HEAD revision.
* This can be used for computing local changes/live diffs (i.e. changes between local graph and workspace HEAD)
*/
workspaceLocalLatestRevisionState: RevisionChangeDetectionState;
/**
* Store the entities from remote workspace HEAD revision.
* This can be used for computing the diffs between local workspace and remote workspace to check if the local workspace is out-of-sync
*/
workspaceRemoteLatestRevisionState: RevisionChangeDetectionState;
aggregatedWorkspaceChanges: EntityDiff[];
aggregatedProjectLatestChanges: EntityDiff[];
aggregatedWorkspaceRemoteChanges: EntityDiff[];
potentialWorkspaceUpdateConflicts: EntityChangeConflict[];
potentialWorkspacePullConflicts: EntityChangeConflict[];
/**
* For conflict resolution, the procedure is split into 2 steps:
* 1. The user resolves conflicts (no graph is built at this point)
* 2. The user marks all conflicts as resolved and starts building the graph to fix any residual problems
*
* Ideally, we would like to use the live diff (conflict resolution BASE <-> local graph), but since for step 1
* we do not build the graph, this is not possible, so we have to use the following to store the diff until we move to step 2
*/
aggregatedConflictResolutionChanges: EntityDiff[];
conflicts: EntityChangeConflict[];
resolutions: EntityChangeConflictResolution[];
currentGraphHash: string | undefined;
observerContext: ObserverContext;
constructor(editorStore: EditorStore, graphState: EditorGraphState);
setAggregatedProjectLatestChanges(diffs: EntityDiff[]): void;
setAggregatedWorkspaceRemoteChanges(diffs: EntityDiff[]): void;
setPotentialWorkspaceUpdateConflicts(conflicts: EntityChangeConflict[]): void;
setPotentialWorkspacePullConflicts(conflicts: EntityChangeConflict[]): void;
getCurrentGraphHash(): string;
stop(force?: boolean): void;
/**
* The change detection check is not free, although due to the power of mobx's computed, this is really fast
* but we want to use a reaction here instead of having changes as a computed getter is that:
* 1. We want to debounce the action
* 2. We want to control when we would start observing for changes (this is useful since we need to compute the initial
* hashes index before this, otherwise users will get false report about the number of changes)
* This function might cause the UI to jank the since it involves expensive computation of the all the elements' hashes
* for the first time. Currently there is no workaround so we might need to comeback and evaluate this
*/
start(): void;
snapshotLocalEntityHashesIndex(quiet?: boolean): Map<string, string>;
private computeAggregatedChangesBetweenStates;
computeAggregatedWorkspaceChanges(quiet?: boolean): GeneratorFn<void>;
computeAggregatedWorkspaceRemoteChanges(quiet?: boolean): GeneratorFn<void>;
computeAggregatedProjectLatestChanges(quiet?: boolean): GeneratorFn<void>;
computeAggregatedConflictResolutionChanges(quiet?: boolean): GeneratorFn<void>;
/**
* Workspace update conflicts are computed between 2 sets of changes:
* 1. Incoming changes: changes between workspace BASE revision and project LATEST revision
* 2. Current changes: changes between workspace BASE revision and workspace HEAD revision
*/
computeWorkspaceUpdateConflicts(quiet?: boolean): GeneratorFn<void>;
/**
* Conflict resolution conflicts are computed between 2 sets of changes:
* 1. Incoming changes: changes between workspace BASE revision and conflict resolution BASE revision
* 2. Current changes: changes between workspace BASE revision and workspace HEAD revision
*/
computeConflictResolutionConflicts(quiet?: boolean): GeneratorFn<void>;
/**
* This function computes the entity change conflicts between 2 set of entity changes (let's call them incoming changes and current changes).
* For a more comprehensive explanation, we take a look at how we can use this to compute potential conflicts during workspace update:
*
* To compute potential conflicts during workspace update, we must base off the project latest changes [incChg] (workspace BASE <-> project HEAD)
* and the merge request changes [currChng] (workspace BASE <-> workspace HEAD). We have a case table below (`N.A.` means it's impossible cases)
* For cases we with `conflict` there might be potential conflicts as the change to the entity appear in both [incChg] and [currChng]. But we must
* note that this is `potential` because we cannot be too sure how SDCL server handle merging these during update.
*
* NOTE: it's important to remember that these are truly potential conflicts, because of git merge mechanism,
* it will apply one intermediate commit at a time, this means that if, we have a file A:
*
* Workspace change: 1. A is deleted; 2. A is created with content `a`
* Project latest change: 1. A is modified with content `a`
*
* These could mean no conflict from our computation but is a conflict when Git tries to merge.
*
* NOTE: Also, there could be strange case for SVN that a file can be DELETED and CREATED, it's called "replace".
*
* | [incChg] | | | |
* -----------------------------------------------------------
* [currChng] | | CREATE | DELETE | MODIFY |
* -----------------------------------------------------------
* | CREATE | conflict | N.A. | N.A. |
* -----------------------------------------------------------
* | DELETE | N.A. | none | conflict |
* -----------------------------------------------------------
* | MODIFY | N.A. | conflict | conflict |
* -----------------------------------------------------------
*/
computeEntityChangeConflicts(currentChanges: EntityDiff[], incomingChanges: EntityDiff[], currentChangeEntityHashesIndex: Map<string, string>, incomingChangeEntityHashesIndex: Map<string, string>): GeneratorFn<EntityChangeConflict[]>;
/**
* NOTE: here we have not dealt with non-entity changes like project dependency for example.
* We will have to count that as part of the change in the future.
*/
computeLocalChanges(quiet?: boolean): GeneratorFn<void>;
computeLocalChangesInTextMode(currentEntities: Entity[], quiet?: boolean): GeneratorFn<void>;
observeGraph(): GeneratorFn<void>;
/**
* Call `get hashCode()` on each element once so we trigger the first time we compute the hash for that element.
* Notice that we do this in asynchronous manner to not block the main execution thread, as this is quiet an expensive
* task.
*
* We also want to take advantage of `mobx computed` here so we save time when starting change detection. However,
* since `mobx computed` does not track async contexts, we have to use the `keepAlive` option for `computed`
*
* To avoid memory leak potentially caused by `keepAlive`, we use `keepAlive` utility from `mobx-utils`
* so we could manually dispose `keepAlive` later after we already done with starting change detection.
*/
preComputeGraphElementHashes(): Promise<void>;
}
export {};
//# sourceMappingURL=ChangeDetectionState.d.ts.map