2020-07-21 13:20:51 +00:00
|
|
|
import {ActionGroup} from 'app/common/ActionGroup';
|
|
|
|
import {CellValue, TableDataAction, UserAction} from 'app/common/DocActions';
|
2021-03-10 14:08:46 +00:00
|
|
|
import {FormulaProperties} from 'app/common/GranularAccessClause';
|
2021-09-30 08:19:22 +00:00
|
|
|
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
2021-10-08 06:32:59 +00:00
|
|
|
import {DocStateComparison, PermissionData, UserAccessData} from 'app/common/UserAPI';
|
2020-07-21 13:20:51 +00:00
|
|
|
import {ParseOptions} from 'app/plugin/FileParserAPI';
|
|
|
|
import {IMessage} from 'grain-rpc';
|
|
|
|
|
|
|
|
export interface ApplyUAOptions {
|
|
|
|
desc?: string; // Overrides the description of the action.
|
|
|
|
otherId?: number; // For undo/redo; the actionNum of the original action to which it applies.
|
|
|
|
linkId?: number; // For bundled actions, actionNum of the previous action in the bundle.
|
2021-06-09 19:31:59 +00:00
|
|
|
bestEffort?: boolean; // If set, action may be applied in part if it cannot be applied completely.
|
2021-12-16 13:45:05 +00:00
|
|
|
parseStrings?: boolean; // If true, parses string values in some actions based on the column
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface ApplyUAResult {
|
|
|
|
actionNum: number; // number of the action that got recorded.
|
|
|
|
retValues: any[]; // array of return values, one for each of the passed-in user actions.
|
|
|
|
isModification: boolean; // true if document was modified.
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface DataSourceTransformed {
|
|
|
|
// Identifies the upload, which may include multiple files.
|
|
|
|
uploadId: number;
|
|
|
|
|
|
|
|
// For each file in the upload, the transform rules for that file.
|
|
|
|
transforms: TransformRuleMap[];
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface TransformRuleMap {
|
|
|
|
[origTableName: string]: TransformRule;
|
|
|
|
}
|
|
|
|
|
2021-12-13 09:11:18 +00:00
|
|
|
// Special values for import destinations; null means "new table", "" means skip table.
|
|
|
|
// Both special options exposed as consts.
|
|
|
|
export type DestId = string | null;
|
|
|
|
export const NEW_TABLE = null;
|
|
|
|
export const SKIP_TABLE = "";
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
export interface TransformRule {
|
2021-12-13 09:11:18 +00:00
|
|
|
destTableId: DestId;
|
2020-07-21 13:20:51 +00:00
|
|
|
destCols: TransformColumn[];
|
|
|
|
sourceCols: string[];
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface TransformColumn {
|
|
|
|
label: string;
|
|
|
|
colId: string|null;
|
|
|
|
type: string;
|
|
|
|
formula: string;
|
2022-03-04 17:37:56 +00:00
|
|
|
widgetOptions: string;
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface ImportResult {
|
|
|
|
options: ParseOptions;
|
|
|
|
tables: ImportTableResult[];
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ImportTableResult {
|
|
|
|
hiddenTableId: string;
|
2022-02-19 09:46:49 +00:00
|
|
|
uploadFileIndex: number; // Index into upload.files array, for the file responsible for this table.
|
2020-07-21 13:20:51 +00:00
|
|
|
origTableName: string;
|
|
|
|
transformSectionRef: number;
|
|
|
|
destTableId: string|null;
|
|
|
|
}
|
|
|
|
|
2021-10-04 16:14:14 +00:00
|
|
|
export interface ImportOptions {
|
|
|
|
parseOptions?: ParseOptions; // Options for parsing the source file.
|
|
|
|
mergeOptionMaps?: MergeOptionsMap[]; // Options for merging fields, indexed by uploadFileIndex.
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface MergeOptionsMap {
|
|
|
|
// Map of original GristTable name of imported table to its merge options, if any.
|
|
|
|
[origTableName: string]: MergeOptions|undefined;
|
2021-09-15 06:12:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface MergeOptions {
|
|
|
|
mergeCols: string[]; // Columns to use as merge keys for incremental imports.
|
|
|
|
mergeStrategy: MergeStrategy; // Determines how matched records should be merged between 2 tables.
|
|
|
|
}
|
|
|
|
|
2021-10-04 16:14:14 +00:00
|
|
|
export interface MergeStrategy {
|
|
|
|
type: 'replace-with-nonblank-source' | 'replace-all-fields' | 'replace-blank-fields-only';
|
2021-09-15 06:12:34 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
/**
|
|
|
|
* Represents a query for Grist data. The tableId is required. An empty set of filters indicates
|
|
|
|
* the full table. Examples:
|
|
|
|
* {tableId: "Projects", filters: {}}
|
|
|
|
* {tableId: "Employees", filters: {Status: ["Active"], Dept: ["Sales", "HR"]}}
|
|
|
|
*/
|
2021-08-10 18:21:03 +00:00
|
|
|
interface BaseQuery {
|
2020-07-21 13:20:51 +00:00
|
|
|
tableId: string;
|
2021-09-15 20:18:00 +00:00
|
|
|
filters: QueryFilters;
|
2021-08-10 18:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Query that can only be used on the client side.
|
|
|
|
* Allows filtering with more complex operations.
|
|
|
|
*/
|
|
|
|
export interface ClientQuery extends BaseQuery {
|
2022-01-27 17:51:37 +00:00
|
|
|
operations: {
|
2021-08-10 18:21:03 +00:00
|
|
|
[colId: string]: QueryOperation;
|
2021-09-20 20:35:14 +00:00
|
|
|
};
|
2021-08-10 18:21:03 +00:00
|
|
|
}
|
2020-07-21 13:20:51 +00:00
|
|
|
|
2021-08-10 18:21:03 +00:00
|
|
|
/**
|
|
|
|
* Query intended to be sent to a server.
|
|
|
|
*/
|
|
|
|
export interface ServerQuery extends BaseQuery {
|
2020-07-21 13:20:51 +00:00
|
|
|
// Queries to server for onDemand tables will set a limit to avoid bringing down the browser.
|
|
|
|
limit?: number;
|
|
|
|
}
|
|
|
|
|
2021-09-15 20:18:00 +00:00
|
|
|
/**
|
|
|
|
* Type of the filters option to queries.
|
|
|
|
*/
|
|
|
|
export interface QueryFilters {
|
|
|
|
// TODO: check if "any" can be replaced with "CellValue".
|
|
|
|
[colId: string]: any[];
|
|
|
|
}
|
|
|
|
|
2021-08-10 18:21:03 +00:00
|
|
|
export type QueryOperation = "in" | "intersects";
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
/**
|
|
|
|
* Response from useQuerySet(). A query returns data AND creates a subscription to receive
|
|
|
|
* DocActions that affect this data. The querySubId field identifies this subscription, and must
|
|
|
|
* be used in a disposeQuerySet() call to unsubscribe.
|
|
|
|
*/
|
|
|
|
export interface QueryResult {
|
|
|
|
querySubId: number; // ID of the subscription, to use with disposeQuerySet.
|
|
|
|
tableData: TableDataAction;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Result of a fork operation, with newly minted ids.
|
|
|
|
* For a document with docId XXXXX and urlId UUUUU, the fork will have a
|
|
|
|
* docId of XXXXX~FORKID[~USERID] and a urlId of UUUUU~FORKID[~USERID].
|
|
|
|
*/
|
|
|
|
export interface ForkResult {
|
|
|
|
docId: string;
|
|
|
|
urlId: string;
|
|
|
|
}
|
|
|
|
|
2021-10-08 14:56:33 +00:00
|
|
|
/**
|
|
|
|
* An extension of PermissionData to cover not just users with whom a document is shared,
|
|
|
|
* but also users mentioned in the document (in user attribute tables), and suggested
|
|
|
|
* example users. This is for use in the "View As" feature of the access rules page.
|
|
|
|
*/
|
|
|
|
export interface PermissionDataWithExtraUsers extends PermissionData {
|
|
|
|
attributeTableUsers: UserAccessData[];
|
|
|
|
exampleUsers: UserAccessData[];
|
|
|
|
}
|
|
|
|
|
(core) Grace period and delete-only mode when exceeding row limit
Summary:
Builds upon https://phab.getgrist.com/D3328
- Add HomeDB column `Document.gracePeriodStart`
- When the row count moves above the limit, set it to the current date. When it moves below, set it to null.
- Add DataLimitStatus type indicating if the document is approaching the limit, is in a grace period, or is in delete only mode if the grace period started at least 14 days ago. Compute it in ActiveDoc and send it to client when opening.
- Only allow certain user actions when in delete-only mode.
Follow-up tasks related to this diff:
- When DataLimitStatus in the client is non-empty, show a banner to the appropriate users.
- Only send DataLimitStatus to users with the appropriate access. There's no risk landing this now since real users will only see null until free team sites are released.
- Update DataLimitStatus immediately in the client when it changes, e.g. when user actions are applied or the product is changed. Right now it's only sent when the document loads.
- Update row limit, grace period start, and data limit status in ActiveDoc when the product changes, i.e. the user upgrades/downgrades.
- Account for data size when computing data limit status, not just row counts.
See also the tasks mentioned in https://phab.getgrist.com/D3331
Test Plan: Extended FreeTeam nbrowser test, testing the 4 statuses.
Reviewers: georgegevoian
Reviewed By: georgegevoian
Differential Revision: https://phab.getgrist.com/D3331
2022-03-24 12:05:51 +00:00
|
|
|
export type DataLimitStatus = null | 'approachingLimit' | 'gracePeriod' | 'deleteOnly';
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
export interface ActiveDocAPI {
|
|
|
|
/**
|
|
|
|
* Closes a document, and unsubscribes from its userAction events.
|
|
|
|
*/
|
|
|
|
closeDoc(): Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches a particular table from the data engine to return to the client.
|
|
|
|
*/
|
|
|
|
fetchTable(tableId: string): Promise<TableDataAction>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches the generated Python code for this document. (TODO rename this misnomer.)
|
|
|
|
*/
|
|
|
|
fetchTableSchema(): Promise<string>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Makes a query (documented elsewhere) and subscribes to it, so that the client receives
|
|
|
|
* docActions that affect this query's results. The subscription remains functional even when
|
|
|
|
* tables or columns get renamed.
|
|
|
|
*/
|
2021-08-10 18:21:03 +00:00
|
|
|
useQuerySet(query: ServerQuery): Promise<QueryResult>;
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the subscription to a Query, identified by QueryResult.querySubId, so that the
|
|
|
|
* client stops receiving docActions relevant only to that query.
|
|
|
|
*/
|
|
|
|
disposeQuerySet(querySubId: number): Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Applies an array of user actions to the document.
|
|
|
|
*/
|
|
|
|
applyUserActions(actions: UserAction[], options?: ApplyUAOptions): Promise<ApplyUAResult>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A variant of applyUserActions where actions are passed in by ids (actionNum, actionHash)
|
|
|
|
* rather than by value.
|
|
|
|
*/
|
|
|
|
applyUserActionsById(actionNums: number[], actionHashes: string[],
|
|
|
|
undo: boolean, options?: ApplyUAOptions): Promise<ApplyUAResult>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Imports files, removes previously created temporary hidden tables and creates the new ones.
|
|
|
|
*/
|
|
|
|
importFiles(dataSource: DataSourceTransformed,
|
|
|
|
parseOptions: ParseOptions, prevTableIds: string[]): Promise<ImportResult>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finishes import files, creates the new tables, and cleans up temporary hidden tables and uploads.
|
|
|
|
*/
|
2021-09-15 06:12:34 +00:00
|
|
|
finishImportFiles(dataSource: DataSourceTransformed, prevTableIds: string[],
|
|
|
|
options: ImportOptions): Promise<ImportResult>;
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Cancels import files, cleans up temporary hidden tables and uploads.
|
|
|
|
*/
|
2022-03-10 00:03:02 +00:00
|
|
|
cancelImportFiles(uploadId: number, prevTableIds: string[]): Promise<void>;
|
2020-07-21 13:20:51 +00:00
|
|
|
|
2021-10-08 06:32:59 +00:00
|
|
|
/**
|
|
|
|
* Returns a diff of changes that will be applied to the destination table from `transformRule`
|
|
|
|
* if the data from `hiddenTableId` is imported with the specified `mergeOptions`.
|
|
|
|
*/
|
|
|
|
generateImportDiff(hiddenTableId: string, transformRule: TransformRule,
|
|
|
|
mergeOptions: MergeOptions): Promise<DocStateComparison>;
|
|
|
|
|
2020-07-21 13:20:51 +00:00
|
|
|
/**
|
|
|
|
* Saves attachments from a given upload and creates an entry for them in the database. It
|
|
|
|
* returns the list of rowIds for the rows created in the _grist_Attachments table.
|
|
|
|
*/
|
|
|
|
addAttachments(uploadId: number): Promise<number[]>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns up to n columns in the document, or a specific table, which contain the given values.
|
|
|
|
* Columns are returned ordered from best to worst based on an estimate for number of matches.
|
|
|
|
*/
|
|
|
|
findColFromValues(values: any[], n: number, optTableId?: string): Promise<number[]>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns cell value with an error message (traceback) for one invalid formula cell.
|
|
|
|
*/
|
|
|
|
getFormulaError(tableId: string, colId: string, rowId: number): Promise<CellValue>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch content at a url.
|
|
|
|
*/
|
2021-09-30 08:19:22 +00:00
|
|
|
fetchURL(url: string, options?: FetchUrlOptions): Promise<UploadResult>;
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Find and return a list of auto-complete suggestions that start with `txt`, when editing a
|
2021-07-07 16:03:01 +00:00
|
|
|
* formula in table `tableId` and column `columnId`.
|
2020-07-21 13:20:51 +00:00
|
|
|
*/
|
2021-07-07 16:03:01 +00:00
|
|
|
autocomplete(txt: string, tableId: string, columnId: string): Promise<string[]>;
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the current instance from the doc.
|
|
|
|
*/
|
|
|
|
removeInstanceFromDoc(): Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get recent actions in ActionGroup format with summaries included.
|
|
|
|
*/
|
|
|
|
getActionSummaries(): Promise<ActionGroup[]>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiates user actions bandling for undo.
|
|
|
|
*/
|
|
|
|
startBundleUserActions(): Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stopes user actions bandling for undo.
|
|
|
|
*/
|
|
|
|
stopBundleUserActions(): Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Forward a grain-rpc message to a given plugin.
|
|
|
|
*/
|
|
|
|
forwardPluginRpc(pluginId: string, msg: IMessage): Promise<any>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reload documents plugins.
|
|
|
|
*/
|
|
|
|
reloadPlugins(): Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Immediately close the document and data engine, to be reloaded from scratch, and cause all
|
|
|
|
* browser clients to reopen it.
|
|
|
|
*/
|
|
|
|
reloadDoc(): Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare a fork of the document, and return the id(s) of the fork.
|
|
|
|
*/
|
2020-07-27 17:26:28 +00:00
|
|
|
fork(): Promise<ForkResult>;
|
2020-12-15 04:19:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if an ACL formula is valid. If not, will throw an error with an explanation.
|
|
|
|
*/
|
2021-03-10 14:08:46 +00:00
|
|
|
checkAclFormula(text: string): Promise<FormulaProperties>;
|
2021-01-14 16:10:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the full set of tableIds, with the list of colIds for each table. This is intended
|
|
|
|
* for editing ACLs. It is only available to users who can edit ACLs, and lists all resources
|
|
|
|
* regardless of rules that may block access to them.
|
|
|
|
*/
|
|
|
|
getAclResources(): Promise<{[tableId: string]: string[]}>;
|
2021-10-01 13:45:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for document to finish initializing.
|
|
|
|
*/
|
|
|
|
waitForInitialization(): Promise<void>;
|
2021-10-08 14:56:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get users that are worth proposing to "View As" for access control purposes.
|
|
|
|
*/
|
|
|
|
getUsersForViewAs(): Promise<PermissionDataWithExtraUsers>;
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|