(core) add more detail to /compare endpoint

Summary:
 * Extends `/api/docs/docId1/compare/docId2` endpoint with a `detail=1` option to include details of what changed in the document content.
 * Adds an `/api/docs/docId/compare?left=HASH&right=HASH` endpoint for comparing two versions of a single document. This is needed to implement the extension to `/api/docs/docId1/compare/docId2`.
 * Adds a `HashUtil` class to allow hash aliases like `HEAD` and `HEAD~`.

Everything is a bit crude:
 * Changes are expressed as ActionSummary objects, which aren't fully fleshed out.
 * Extra data about formula columns is inserted in an inflexible way.

This is extracted and cleaned up from https://phab.getgrist.com/D2600.

Test Plan: added tests

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2614
This commit is contained in:
Paul Fitzpatrick
2020-09-18 14:43:01 -04:00
parent b44e4a94ab
commit 87f2fd15fb
5 changed files with 220 additions and 8 deletions

View File

@@ -1,5 +1,6 @@
import {CellDelta, TabularDiff, TabularDiffs} from 'app/common/TabularDiff';
import toPairs = require('lodash/toPairs');
import { RowRecord } from 'app/plugin/GristData';
/**
* An ActionSummary represents the overall effect of changes that took place
@@ -90,6 +91,13 @@ export interface TableDelta {
/** Partial record of cell-level changes - large bulk changes not included. */
columnDeltas: {[colId: string]: ColumnDelta};
columnRenames: LabelDelta[]; /** a list of column renames/additions/removals */
/*
* A snapshot of row content for added and updated rows, including formula columns.
* Not included in regular summaries, but used in comparisons. Hopefully this
* can evaporate in future.
*/
finalRowContent?: {[rowId: number]: RowRecord};
}
/**

View File

@@ -1,3 +1,4 @@
import {ActionSummary} from 'app/common/ActionSummary';
import {ApplyUAResult} from 'app/common/ActiveDocAPI';
import {BaseAPI, IOptions} from 'app/common/BaseAPI';
import {BillingAPI, BillingAPIImpl} from 'app/common/BillingAPI';
@@ -218,6 +219,18 @@ export interface DocStateComparison {
// both: both documents have changes (possible divergence)
// unrelated: no common history found
summary: 'same' | 'left' | 'right' | 'both' | 'unrelated';
// optionally, details of what changed may be included.
details?: DocStateComparisonDetails;
}
/**
* Detailed comparison between document versions. For now, this
* is provided as a pair of ActionSummary objects, relative to
* the most recent common ancestor.
*/
export interface DocStateComparisonDetails {
leftChanges: ActionSummary;
rightChanges: ActionSummary;
}
export {UserProfile} from 'app/common/LoginSessionAPI';
@@ -289,7 +302,13 @@ export interface DocAPI {
replace(source: DocReplacementOptions): Promise<void>;
getSnapshots(): Promise<DocSnapshots>;
forceReload(): Promise<void>;
compareState(remoteDocId: string): Promise<DocStateComparison>;
// Compare two documents, optionally including details of the changes.
compareDoc(remoteDocId: string, options?: { detail: boolean }): Promise<DocStateComparison>;
// Compare two versions within a document, including details of the changes.
// Versions are identified by action hashes, or aliases understood by HashUtil.
// Currently, leftHash is expected to be an ancestor of rightHash. If rightHash
// is HEAD, the result will contain a copy of any rows added or updated.
compareVersion(leftHash: string, rightHash: string): Promise<DocStateComparison>;
}
// Operations that are supported by a doc worker.
@@ -676,7 +695,17 @@ export class DocAPIImpl extends BaseAPI implements DocAPI {
});
}
public async compareState(remoteDocId: string): Promise<DocStateComparison> {
return this.requestJson(`${this._url}/compare/${remoteDocId}`);
public async compareDoc(remoteDocId: string, options: {
detail?: boolean
} = {}): Promise<DocStateComparison> {
const q = options.detail ? '?detail=true' : '';
return this.requestJson(`${this._url}/compare/${remoteDocId}${q}`);
}
public async compareVersion(leftHash: string, rightHash: string): Promise<DocStateComparison> {
const url = new URL(`${this._url}/compare`);
url.searchParams.append('left', leftHash);
url.searchParams.append('right', rightHash);
return this.requestJson(url.href);
}
}