UNPKG

@finos/legend-application-studio

Version:
216 lines 12.4 kB
/** * 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