mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Configure more comprehensive eslint rules for Typescript
Summary: - Update rules to be more like we've had with tslint - Switch tsserver plugin to eslint (tsserver makes for a much faster way to lint in editors) - Apply suggested auto-fixes - Fix all lint errors and warnings in core/, app/, test/ Test Plan: Some behavior may change subtly (e.g. added missing awaits), relying on existing tests to catch problems. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2785
This commit is contained in:
parent
91fdef58ac
commit
526b0ad33e
@ -241,9 +241,9 @@ AceEditor.prototype._getContentHeight = function() {
|
||||
|
||||
|
||||
let _RangeConstructor = null; //singleton, load it lazily
|
||||
AceEditor.makeRange = function(a,b,c,d) {
|
||||
AceEditor.makeRange = function(a, b, c, d) {
|
||||
_RangeConstructor = _RangeConstructor || ace.acequire('ace/range').Range;
|
||||
return new _RangeConstructor(a,b,c,d);
|
||||
return new _RangeConstructor(a, b, c, d);
|
||||
};
|
||||
|
||||
module.exports = AceEditor;
|
||||
|
@ -262,7 +262,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
|
||||
if (this._loaded || !this._gristDoc) { return; }
|
||||
this._loading(true);
|
||||
// Returned actions are ordered with earliest actions first.
|
||||
const result = await this._gristDoc!.docComm.getActionSummaries();
|
||||
const result = await this._gristDoc.docComm.getActionSummaries();
|
||||
this._loading(false);
|
||||
this._loaded = true;
|
||||
// Add the actions to our action log.
|
||||
|
@ -15,7 +15,7 @@ import * as ko from 'knockout';
|
||||
import noop = require('lodash/noop');
|
||||
|
||||
// To simplify diff (avoid rearranging methods to satisfy private/public order).
|
||||
// tslint:disable:member-ordering
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
|
||||
type AceEditor = any;
|
||||
|
||||
@ -130,7 +130,7 @@ export class ColumnTransform extends Disposable {
|
||||
this.transformColumn = this.field.column();
|
||||
this.transformColumn.origColRef(this.origColumn.getRowId());
|
||||
this._setTransforming(true);
|
||||
return await this.postAddTransformColumn();
|
||||
return this.postAddTransformColumn();
|
||||
} finally {
|
||||
this.isCallPending(false);
|
||||
}
|
||||
@ -167,7 +167,7 @@ export class ColumnTransform extends Disposable {
|
||||
/**
|
||||
* A derived class can override to do some processing after this.transformColumn has been set.
|
||||
*/
|
||||
protected postAddTransformColumn() {
|
||||
protected postAddTransformColumn(): void {
|
||||
// Nothing in base class.
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ export class ColumnTransform extends Disposable {
|
||||
} finally {
|
||||
// Wait until the change completed to set column back, to avoid value flickering.
|
||||
field.colRef(origRef);
|
||||
tableData.sendTableAction(['RemoveColumn', transformColId]);
|
||||
void tableData.sendTableAction(['RemoveColumn', transformColId]);
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -350,11 +350,11 @@ GridView.prototype.fillSelectionDown = function() {
|
||||
}).filter(colId => colId);
|
||||
|
||||
var colInfo = _.object(colIds, colIds.map(colId => {
|
||||
var val = this.tableModel.tableData.getValue(rowIds[0],colId);
|
||||
var val = this.tableModel.tableData.getValue(rowIds[0], colId);
|
||||
return rowIds.map(() => val);
|
||||
}));
|
||||
|
||||
this.tableModel.sendTableAction(["BulkUpdateRecord",rowIds,colInfo]);
|
||||
this.tableModel.sendTableAction(["BulkUpdateRecord", rowIds, colInfo]);
|
||||
};
|
||||
|
||||
|
||||
|
@ -153,7 +153,7 @@ export class Importer extends Disposable {
|
||||
} else if (item.kind === "url") {
|
||||
uploadResult = await fetchURL(this._docComm, item.url);
|
||||
} else {
|
||||
throw new Error(`Import source of kind ${item!.kind} are not yet supported!`);
|
||||
throw new Error(`Import source of kind ${(item as any).kind} are not yet supported!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -391,7 +391,7 @@ export class Importer extends Disposable {
|
||||
}
|
||||
|
||||
function getSourceDescription(sourceInfo: SourceInfo, upload: UploadResult) {
|
||||
const origName = upload!.files[sourceInfo.uploadFileIndex].origName;
|
||||
const origName = upload.files[sourceInfo.uploadFileIndex].origName;
|
||||
return sourceInfo.origTableName ? origName + ' - ' + sourceInfo.origTableName : origName;
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,8 @@ export function makeSearchToolbarGroup(gristDoc: GristDoc) {
|
||||
// Active normally.
|
||||
const commandGroup = createGroup({
|
||||
find: () => { input.focus(); },
|
||||
findNext: () => { searcher.findNext(); }, // tslint:disable-line:no-floating-promises TODO
|
||||
findPrev: () => { searcher.findPrev(); }, // tslint:disable-line:no-floating-promises TODO
|
||||
findNext: () => { searcher.findNext(); }, // eslint-disable-line @typescript-eslint/no-floating-promises
|
||||
findPrev: () => { searcher.findPrev(); }, // eslint-disable-line @typescript-eslint/no-floating-promises
|
||||
}, null, true);
|
||||
|
||||
// Return an array of one item (for a toolbar group of a single item). The item is an array of
|
||||
@ -49,7 +49,7 @@ export function makeSearchToolbarGroup(gristDoc: GristDoc) {
|
||||
// the searchbox is created so early that the actions like accept/cancel get overridden).
|
||||
dom.on('keydown', (e: KeyboardEvent) => {
|
||||
switch (e.keyCode) {
|
||||
case 13: searcher.findNext(); break; // tslint:disable-line:no-floating-promises TODO
|
||||
case 13: searcher.findNext(); break; // eslint-disable-line @typescript-eslint/no-floating-promises
|
||||
case 27: input.blur(); break;
|
||||
}
|
||||
})
|
||||
|
@ -264,7 +264,7 @@ CellSelector.prototype.rowCount = function() {
|
||||
return this.rowUpper() - this.rowLower() + 1;
|
||||
};
|
||||
|
||||
CellSelector.prototype.selectArea = function(rowStartIdx,colStartIdx,rowEndIdx,colEndIdx) {
|
||||
CellSelector.prototype.selectArea = function(rowStartIdx, colStartIdx, rowEndIdx, colEndIdx) {
|
||||
this.row.start(rowStartIdx);
|
||||
this.col.start(colStartIdx);
|
||||
this.row.end(rowEndIdx);
|
||||
|
@ -22,7 +22,7 @@ import isEmpty = require('lodash/isEmpty');
|
||||
import pickBy = require('lodash/pickBy');
|
||||
|
||||
// To simplify diff (avoid rearranging methods to satisfy private/public order).
|
||||
// tslint:disable:member-ordering
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
|
||||
/**
|
||||
* Creates an instance of TypeTransform for a single field. Extends ColumnTransform.
|
||||
|
@ -58,7 +58,7 @@ function ViewConfigTab(options) {
|
||||
}) || self.viewSectionData.at(0);
|
||||
}));
|
||||
this.isDetail = this.autoDispose(ko.computed(function() {
|
||||
return ['detail','single'].includes(this.viewModel.activeSection().parentKey());
|
||||
return ['detail', 'single'].includes(this.viewModel.activeSection().parentKey());
|
||||
}, this));
|
||||
this.isChart = this.autoDispose(ko.computed(function() {
|
||||
return this.viewModel.activeSection().parentKey() === 'chart';}, this));
|
||||
|
10
app/client/declarations.d.ts
vendored
10
app/client/declarations.d.ts
vendored
@ -35,7 +35,6 @@ declare module "app/client/components/BaseView" {
|
||||
import {Cursor, CursorPos} from 'app/client/components/Cursor';
|
||||
import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import {Disposable} from 'app/client/lib/dispose';
|
||||
import {KoArray} from "app/client/lib/koArray";
|
||||
import * as BaseRowModel from "app/client/models/BaseRowModel";
|
||||
import {DataRowModel} from 'app/client/models/DataRowModel';
|
||||
import {LazyArrayModel} from "app/client/models/DataTableModel";
|
||||
@ -72,10 +71,8 @@ declare module "app/client/components/BaseView" {
|
||||
}
|
||||
|
||||
declare module "app/client/components/RefSelect" {
|
||||
import {GristDoc, TabContent} from 'app/client/components/GristDoc';
|
||||
import {Disposable} from 'app/client/lib/dispose';
|
||||
import {ColumnRec} from "app/client/models/DocModel";
|
||||
import {DomArg} from 'grainjs';
|
||||
import {DocModel} from "app/client/models/DocModel";
|
||||
import {FieldBuilder} from "app/client/widgets/FieldBuilder";
|
||||
|
||||
@ -161,11 +158,11 @@ declare module "app/client/models/BaseRowModel" {
|
||||
class BaseRowModel extends Disposable {
|
||||
public id: ko.Computed<number>;
|
||||
public _index: ko.Observable<number|null>;
|
||||
public getRowId(): number;
|
||||
public updateColValues(colValues: ColValues): Promise<void>;
|
||||
public _table: TableModel;
|
||||
protected _rowId: number | 'new' | null;
|
||||
protected _fields: string[];
|
||||
public getRowId(): number;
|
||||
public updateColValues(colValues: ColValues): Promise<void>;
|
||||
}
|
||||
export = BaseRowModel;
|
||||
}
|
||||
@ -286,10 +283,9 @@ declare module "app/client/models/DataTableModel" {
|
||||
import * as BaseRowModel from "app/client/models/BaseRowModel";
|
||||
import {DocModel, TableRec} from "app/client/models/DocModel";
|
||||
import {TableQuerySets} from 'app/client/models/QuerySet';
|
||||
import {RowSource, SortedRowSet} from "app/client/models/rowset";
|
||||
import {SortedRowSet} from "app/client/models/rowset";
|
||||
import {TableData} from "app/client/models/TableData";
|
||||
import * as TableModel from "app/client/models/TableModel";
|
||||
import {CellValue} from "app/common/DocActions";
|
||||
|
||||
namespace DataTableModel {
|
||||
interface LazyArrayModel<T> extends KoArray<T | null> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Implements an autocomplete dropdown.
|
||||
*/
|
||||
import {createPopper, Instance as Popper, Modifier, Options as PopperOptions} from '@popperjs/core';
|
||||
import {createPopper, Modifier, Instance as Popper, Options as PopperOptions} from '@popperjs/core';
|
||||
import {ACItem, ACResults, HighlightFunc} from 'app/client/lib/ACIndex';
|
||||
import {reportError} from 'app/client/models/errors';
|
||||
import {Disposable, dom, EventCB, IDisposable} from 'grainjs';
|
||||
|
@ -174,7 +174,7 @@ export class BillingModelImpl extends Disposable implements BillingModel {
|
||||
await this._billingAPI.updateAddress(newAddr || undefined, newSettings || undefined);
|
||||
}
|
||||
// If there is an org update, re-initialize the org in the client.
|
||||
if (newSettings) { await this._appModel.topAppModel.initialize(); }
|
||||
if (newSettings) { this._appModel.topAppModel.initialize(); }
|
||||
} else {
|
||||
throw new Error('BillingPage _submit error: no task in progress');
|
||||
}
|
||||
|
@ -27,13 +27,6 @@ const ROW_ID_SKIP = -1;
|
||||
* This should be the only part of the code that knows that.
|
||||
*/
|
||||
export class ExtraRows {
|
||||
readonly leftTableDelta?: TableDelta;
|
||||
readonly rightTableDelta?: TableDelta;
|
||||
readonly rightAddRows: Set<number>;
|
||||
readonly rightRemoveRows: Set<number>;
|
||||
readonly leftAddRows: Set<number>;
|
||||
readonly leftRemoveRows: Set<number>;
|
||||
|
||||
/**
|
||||
* Map back from a possibly synthetic row id to an original strictly-positive row id.
|
||||
*/
|
||||
@ -44,7 +37,14 @@ export class ExtraRows {
|
||||
return { type: 'local-remove', id: -(rowId + 2) / 2 };
|
||||
}
|
||||
|
||||
public constructor(readonly tableId: string, readonly comparison?: DocStateComparisonDetails) {
|
||||
public readonly leftTableDelta?: TableDelta;
|
||||
public readonly rightTableDelta?: TableDelta;
|
||||
public readonly rightAddRows: Set<number>;
|
||||
public readonly rightRemoveRows: Set<number>;
|
||||
public readonly leftAddRows: Set<number>;
|
||||
public readonly leftRemoveRows: Set<number>;
|
||||
|
||||
public constructor(public readonly tableId: string, public readonly comparison?: DocStateComparisonDetails) {
|
||||
const remoteTableId = getRemoteTableId(tableId, comparison);
|
||||
this.leftTableDelta = this.comparison?.leftChanges?.tableDeltas[tableId];
|
||||
if (remoteTableId) {
|
||||
|
@ -100,7 +100,7 @@ export class DocData extends BaseDocData {
|
||||
this._nextDesc = options.description;
|
||||
this._lastActionNum = null;
|
||||
this._triggerBundleFinalize = triggerFinalize;
|
||||
await prepareResolve(options.prepare());
|
||||
prepareResolve(options.prepare());
|
||||
this._shouldIncludeInBundle = options.shouldIncludeInBundle;
|
||||
|
||||
await triggerFinalizePromise;
|
||||
@ -162,7 +162,7 @@ export class DocData extends BaseDocData {
|
||||
public sendActions(actions: UserAction[], optDesc?: string): Promise<any[]> {
|
||||
// Some old code relies on this promise being a bluebird Promise.
|
||||
// TODO Remove bluebird and this cast.
|
||||
return bluebird.Promise.resolve(this._sendActionsImpl(actions, optDesc)) as any;
|
||||
return bluebird.Promise.resolve(this._sendActionsImpl(actions, optDesc)) as unknown as Promise<any[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,7 +94,8 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
|
||||
public readonly isReadonly = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isReadonly : false);
|
||||
public readonly isPrefork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isPreFork : false);
|
||||
public readonly isFork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isFork : false);
|
||||
public readonly isRecoveryMode = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isRecoveryMode : false);
|
||||
public readonly isRecoveryMode = Computed.create(this, this.currentDoc,
|
||||
(use, doc) => doc ? doc.isRecoveryMode : false);
|
||||
public readonly userOverride = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.userOverride : null);
|
||||
public readonly isBareFork = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isBareFork : false);
|
||||
public readonly isSample = Computed.create(this, this.currentDoc, (use, doc) => doc ? doc.isSample : false);
|
||||
@ -133,8 +134,9 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
|
||||
if (!urlId) {
|
||||
this._openerHolder.clear();
|
||||
} else {
|
||||
FlowRunner.create(this._openerHolder, (flow: AsyncFlow) => this._openDoc(flow, urlId, urlOpenMode,
|
||||
state.params?.compare, linkParameters))
|
||||
FlowRunner.create(this._openerHolder,
|
||||
(flow: AsyncFlow) => this._openDoc(flow, urlId, urlOpenMode, state.params?.compare, linkParameters)
|
||||
)
|
||||
.resultPromise.catch(err => this._onOpenError(err));
|
||||
}
|
||||
}
|
||||
@ -305,8 +307,10 @@ function addMenu(importSources: ImportSource[], gristDoc: GristDoc, isReadonly:
|
||||
menuIcon("Widget"), "Add Widget to Page", testId('dp-add-widget-to-page'),
|
||||
dom.cls('disabled', isReadonly)
|
||||
),
|
||||
menuItem(() => gristDoc.addEmptyTable().catch(reportError), menuIcon("TypeTable"), "Add Empty Table", testId('dp-empty-table'),
|
||||
dom.cls('disabled', isReadonly)),
|
||||
menuItem(() => gristDoc.addEmptyTable().catch(reportError),
|
||||
menuIcon("TypeTable"), "Add Empty Table", testId('dp-empty-table'),
|
||||
dom.cls('disabled', isReadonly)
|
||||
),
|
||||
menuDivider(),
|
||||
...importSources.map((importSource, i) =>
|
||||
menuItem(importSource.action,
|
||||
|
@ -314,7 +314,7 @@ function convertQueryToRefs(docModel: DocModel, query: Query): QueryRefs {
|
||||
const tableRec: any = docModel.dataTables[query.tableId].tableMetaRow;
|
||||
|
||||
const colRefsByColId: {[colId: string]: number} = {};
|
||||
for (const col of (tableRec as any).columns.peek().peek()) {
|
||||
for (const col of tableRec.columns.peek().peek()) {
|
||||
colRefsByColId[col.colId.peek()] = col.getRowId();
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ export class SearchModelImpl extends Disposable implements SearchModel {
|
||||
|
||||
// Listen to input value changes (debounced) to activate searching.
|
||||
const findFirst = debounce((_value: string) => this._findFirst(_value), 100);
|
||||
this.autoDispose(this.value.addListener(v => { findFirst(v); }));
|
||||
this.autoDispose(this.value.addListener(v => { void findFirst(v); }));
|
||||
}
|
||||
|
||||
public async findNext() {
|
||||
|
@ -7,7 +7,7 @@ import {DocData} from 'app/client/models/DocData';
|
||||
import {DocAction, ReplaceTableData, TableDataAction, UserAction} from 'app/common/DocActions';
|
||||
import {isRaisedException} from 'app/common/gristTypes';
|
||||
import {countIf} from 'app/common/gutil';
|
||||
import {ColTypeMap, TableData as BaseTableData} from 'app/common/TableData';
|
||||
import {TableData as BaseTableData, ColTypeMap} from 'app/common/TableData';
|
||||
import {BaseFormatter} from 'app/common/ValueFormatter';
|
||||
import {Emitter} from 'grainjs';
|
||||
|
||||
|
@ -153,7 +153,7 @@ class BillingPaymentForm extends BillingSubForm {
|
||||
}) {
|
||||
super();
|
||||
const autofill = this._options.autofill;
|
||||
const stripeAPIKey = (G.window as any).gristConfig.stripeAPIKey;
|
||||
const stripeAPIKey = G.window.gristConfig.stripeAPIKey;
|
||||
try {
|
||||
this._stripe = G.Stripe(stripeAPIKey);
|
||||
this._elements = this._stripe.elements();
|
||||
@ -462,7 +462,7 @@ function checkRequired(propertyName: string) {
|
||||
// if the current observable value is valid.
|
||||
function createValidated(
|
||||
owner: IDisposableOwnerT<any>,
|
||||
checkValidity: (value: string) => void
|
||||
checkValidity: (value: string) => void|Promise<void>,
|
||||
): IValidated<string> {
|
||||
const value = Observable.create(owner, '');
|
||||
const isInvalid = Observable.create<boolean>(owner, false);
|
||||
|
@ -352,7 +352,7 @@ function getFieldNewPosition(fields: KoArray<ViewFieldRec>, item: IField,
|
||||
return tableUtil.fieldInsertPositions(fields, index, 1)[0];
|
||||
}
|
||||
|
||||
function getItemIndex<T>(collection: KoArray<ViewFieldRec>, item: ViewFieldRec|null): number {
|
||||
function getItemIndex(collection: KoArray<ViewFieldRec>, item: ViewFieldRec|null): number {
|
||||
if (item !== null) {
|
||||
return collection.peek().indexOf(item);
|
||||
}
|
||||
|
@ -194,8 +194,8 @@ export const cssHideForNarrowScreen = styled('div', `
|
||||
* Attaches the global css properties to the document's root to them available in the page.
|
||||
*/
|
||||
export function attachCssRootVars(productFlavor: ProductFlavor, varsOnly: boolean = false) {
|
||||
dom.update(document.documentElement!, varsOnly ? dom.cls(cssVarsOnly.className) : dom.cls(cssRootVars));
|
||||
document.documentElement!.classList.add(cssRoot.className);
|
||||
dom.update(document.documentElement, varsOnly ? dom.cls(cssVarsOnly.className) : dom.cls(cssRootVars));
|
||||
document.documentElement.classList.add(cssRoot.className);
|
||||
document.body.classList.add(cssBody.className);
|
||||
const theme = getTheme(productFlavor);
|
||||
if (theme.bodyClassName) {
|
||||
|
@ -4,7 +4,7 @@ import { CellValue } from 'app/common/DocActions';
|
||||
import { isVersions } from 'app/common/gristTypes';
|
||||
import { inlineStyle } from 'app/common/gutil';
|
||||
import { BaseFormatter } from 'app/common/ValueFormatter';
|
||||
import { Diff, DIFF_DELETE, DIFF_INSERT, diff_match_patch as DiffMatchPatch, DIFF_EQUAL } from 'diff-match-patch';
|
||||
import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch as DiffMatchPatch } from 'diff-match-patch';
|
||||
import { Computed, dom } from 'grainjs';
|
||||
|
||||
/**
|
||||
|
@ -27,7 +27,7 @@ import * as gristTypes from 'app/common/gristTypes';
|
||||
import * as gutil from 'app/common/gutil';
|
||||
import { CellValue } from 'app/plugin/GristData';
|
||||
import { delay } from 'bluebird';
|
||||
import { Computed, Disposable, dom as grainjsDom, fromKo, Holder, IDisposable, makeTestId } from 'grainjs';
|
||||
import { Computed, Disposable, fromKo, dom as grainjsDom, Holder, IDisposable, makeTestId } from 'grainjs';
|
||||
import * as ko from 'knockout';
|
||||
import * as _ from 'underscore';
|
||||
|
||||
@ -77,7 +77,7 @@ export class FieldBuilder extends Disposable {
|
||||
private readonly widgetCons: ko.Computed<{create: (...args: any[]) => NewAbstractWidget}>;
|
||||
private readonly docModel: DocModel;
|
||||
|
||||
public constructor(readonly gristDoc: GristDoc, readonly field: ViewFieldRec,
|
||||
public constructor(public readonly gristDoc: GristDoc, public readonly field: ViewFieldRec,
|
||||
private _cursor: Cursor) {
|
||||
super();
|
||||
|
||||
|
@ -315,7 +315,7 @@ function readAclRules(docData: DocData, {log, compile}: ReadAclOptions): ReadAcl
|
||||
}
|
||||
|
||||
for (const [resourceId, rules] of rulesByResource.entries()) {
|
||||
const resourceRec = resourcesTable.getRecord(resourceId as number);
|
||||
const resourceRec = resourcesTable.getRecord(resourceId);
|
||||
if (!resourceRec) {
|
||||
throw new Error(`ACLRule ${rules[0].id} refers to an invalid ACLResource ${resourceId}`);
|
||||
continue;
|
||||
|
@ -82,7 +82,8 @@ const SCHEMA_ACTIONS = new Set(['AddTable', 'RemoveTable', 'RenameTable', 'AddCo
|
||||
/**
|
||||
* Determines whether a given action is a schema action or not.
|
||||
*/
|
||||
export function isSchemaAction(action: DocAction): action is AddTable | RemoveTable | RenameTable | AddColumn | RemoveColumn | RenameColumn | ModifyColumn {
|
||||
export function isSchemaAction(action: DocAction):
|
||||
action is AddTable | RemoveTable | RenameTable | AddColumn | RemoveColumn | RenameColumn | ModifyColumn {
|
||||
return SCHEMA_ACTIONS.has(action[0]);
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ export interface OpenLocalDocResult {
|
||||
userOverride?: UserOverride;
|
||||
}
|
||||
|
||||
export class UserOverride {
|
||||
export interface UserOverride {
|
||||
user: FullUser|null;
|
||||
access: Role|null;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ export abstract class BaseComponent implements IForwarderDest {
|
||||
public async forwardMessage(msg: IMsgCustom): Promise<any> {
|
||||
if (!this._activated) { await this.activate(); }
|
||||
this.inactivityTimer.ping();
|
||||
this.doForwardMessage(msg); // tslint:disable-line:no-floating-promises TODO
|
||||
this.doForwardMessage(msg); // eslint-disable-line @typescript-eslint/no-floating-promises
|
||||
}
|
||||
|
||||
protected abstract doForwardCall(c: IMsgRpcCall): Promise<any>;
|
||||
|
@ -629,7 +629,7 @@ export class UserAPIImpl extends BaseAPI implements UserAPI {
|
||||
}
|
||||
|
||||
export class DocWorkerAPIImpl extends BaseAPI implements DocWorkerAPI {
|
||||
constructor(readonly url: string, _options: IOptions = {}) {
|
||||
constructor(public readonly url: string, _options: IOptions = {}) {
|
||||
super(_options);
|
||||
}
|
||||
|
||||
@ -682,7 +682,7 @@ export class DocWorkerAPIImpl extends BaseAPI implements DocWorkerAPI {
|
||||
export class DocAPIImpl extends BaseAPI implements DocAPI {
|
||||
private _url: string;
|
||||
|
||||
constructor(url: string, readonly docId: string, options: IOptions = {}) {
|
||||
constructor(url: string, public readonly docId: string, options: IOptions = {}) {
|
||||
super(options);
|
||||
this._url = `${url}/api/docs/${docId}`;
|
||||
}
|
||||
|
@ -8,7 +8,9 @@
|
||||
export function tbind<T, R, Args extends any[]>(func: (this: T, ...a: Args) => R, context: T): (...a: Args) => R;
|
||||
|
||||
// Bind context and first arg for a function of up to 5 args.
|
||||
export function tbind<T, R, X, Args extends any[]>(func: (this: T, x: X, ...a: Args) => R, context: T, x: X): (...a: Args) => R;
|
||||
export function tbind<T, R, X, Args extends any[]>(
|
||||
func: (this: T, x: X, ...a: Args) => R, context: T, x: X
|
||||
): (...a: Args) => R;
|
||||
|
||||
export function tbind(func: any, context: any, ...boundArgs: any[]): any {
|
||||
return func.bind(context, ...boundArgs);
|
||||
|
@ -12,7 +12,8 @@ type PromisifiedFunction<T extends AnyFunction> =
|
||||
T extends (a1: infer A1) => infer U ? (a1: A1) => Promise<Unpacked<U>> :
|
||||
T extends (a1: infer A1, a2: infer A2) => infer U ? (a1: A1, a2: A2) => Promise<Unpacked<U>> :
|
||||
T extends (a1: infer A1, a2: infer A2, a3: infer A3) => infer U ? (a1: A1, a2: A2, a3: A3) => Promise<Unpacked<U>> :
|
||||
T extends (a1: infer A1, a2: infer A2, a3: infer A3, a4: infer A4) => infer U ? (a1: A1, a2: A2, a3: A3, a4: A4) => Promise<Unpacked<U>> :
|
||||
T extends (a1: infer A1, a2: infer A2, a3: infer A3, a4: infer A4) =>
|
||||
infer U ? (a1: A1, a2: A2, a3: A3, a4: A4) => Promise<Unpacked<U>> :
|
||||
// ...
|
||||
T extends (...args: any[]) => infer U ? (...args: any[]) => Promise<Unpacked<U>> : T;
|
||||
|
||||
|
@ -55,7 +55,9 @@ export class DocApiForwarder {
|
||||
app.use('^/api/docs$', withoutDoc);
|
||||
}
|
||||
|
||||
private async _forwardToDocWorker(withDocId: boolean, role: 'viewers'|null, req: express.Request, res: express.Response): Promise<void> {
|
||||
private async _forwardToDocWorker(
|
||||
withDocId: boolean, role: 'viewers'|null, req: express.Request, res: express.Response,
|
||||
): Promise<void> {
|
||||
let docId: string|null = null;
|
||||
if (withDocId) {
|
||||
const docAuth = await getOrSetDocAuth(req as RequestWithLogin, this._dbManager, req.params.docId);
|
||||
|
@ -424,7 +424,7 @@ export class DocWorkerMap implements IDocWorkerMap {
|
||||
}
|
||||
|
||||
public async updateDocStatus(docId: string, checksum: string): Promise<void> {
|
||||
this.updateChecksum('doc', docId, checksum);
|
||||
return this.updateChecksum('doc', docId, checksum);
|
||||
}
|
||||
|
||||
public async updateChecksum(family: string, key: string, checksum: string) {
|
||||
|
@ -8,8 +8,8 @@ import {checkSubdomainValidity} from 'app/common/orgNameUtils';
|
||||
import * as roles from 'app/common/roles';
|
||||
// TODO: API should implement UserAPI
|
||||
import {ANONYMOUS_USER_EMAIL, DocumentProperties, EVERYONE_EMAIL,
|
||||
ManagerDelta, NEW_DOCUMENT_CODE, Organization as OrgInfo,
|
||||
OrganizationProperties, PermissionData, PermissionDelta, SUPPORT_EMAIL, UserAccessData,
|
||||
ManagerDelta, NEW_DOCUMENT_CODE, OrganizationProperties,
|
||||
Organization as OrgInfo, PermissionData, PermissionDelta, SUPPORT_EMAIL, UserAccessData,
|
||||
WorkspaceProperties} from "app/common/UserAPI";
|
||||
import {AclRule, AclRuleDoc, AclRuleOrg, AclRuleWs} from "app/gen-server/entity/AclRule";
|
||||
import {Alias} from "app/gen-server/entity/Alias";
|
||||
@ -1903,7 +1903,7 @@ export class HomeDBManager extends EventEmitter {
|
||||
name: u.name,
|
||||
email: u.logins.map((login: Login) => login.displayEmail)[0],
|
||||
picture: u.picture,
|
||||
access: userRoleMap[u.id] as roles.Role
|
||||
access: userRoleMap[u.id]
|
||||
}));
|
||||
return {
|
||||
status: 200,
|
||||
@ -2811,7 +2811,7 @@ export class HomeDBManager extends EventEmitter {
|
||||
let effectiveUserId = userId;
|
||||
let threshold = options.markPermissions;
|
||||
if (options.allowSpecialPermit && scope.specialPermit && scope.specialPermit.docId) {
|
||||
query = query.andWhere('docs.id = :docId', {docId: scope.specialPermit.docId!});
|
||||
query = query.andWhere('docs.id = :docId', {docId: scope.specialPermit.docId});
|
||||
effectiveUserId = this.getPreviewerUserId();
|
||||
threshold = Permissions.VIEW;
|
||||
}
|
||||
@ -3051,7 +3051,7 @@ export class HomeDBManager extends EventEmitter {
|
||||
if (!groupProps.orgOnly || !inherit) {
|
||||
// Skip this group if it's an org only group and the resource inherits from a parent.
|
||||
const group = new Group();
|
||||
group.name = groupProps.name as roles.Role;
|
||||
group.name = groupProps.name;
|
||||
if (inherit) {
|
||||
this._setInheritance(group, inherit);
|
||||
}
|
||||
@ -3333,7 +3333,10 @@ export class HomeDBManager extends EventEmitter {
|
||||
`${everyoneId} IN (gu0.user_id, gu1.user_id, gu2.user_id, gu3.user_id) ` +
|
||||
`then ${everyoneContribution} else (case when ` +
|
||||
`${anonId} IN (gu0.user_id, gu1.user_id, gu2.user_id, gu3.user_id) ` +
|
||||
`then ${Permissions.PUBLIC} | acl_rules.permissions else acl_rules.permissions end) end)`, 8), 'permissions');
|
||||
`then ${Permissions.PUBLIC} | acl_rules.permissions else acl_rules.permissions end) end)`, 8
|
||||
),
|
||||
'permissions'
|
||||
);
|
||||
}
|
||||
q = q.from('acl_rules', 'acl_rules');
|
||||
q = this._getUsersAcls(q, users, accessStyle);
|
||||
|
@ -210,7 +210,9 @@ export class Housekeeper {
|
||||
|
||||
// Call a document endpoint with a permit, cleaning up after the call.
|
||||
// Checks that the user is the support user.
|
||||
private _withSupport(callback: (docId: string, headers: Record<string, string>) => Promise<Fetch.Response>): express.RequestHandler {
|
||||
private _withSupport(
|
||||
callback: (docId: string, headers: Record<string, string>) => Promise<Fetch.Response>
|
||||
): express.RequestHandler {
|
||||
return expressWrap(async (req, res) => {
|
||||
const userId = getAuthorizedUserId(req);
|
||||
if (userId !== this._dbManager.getSupportUserId()) {
|
||||
|
@ -133,7 +133,7 @@ export async function addImporter(name: string, path: string, mode: 'fullscreen'
|
||||
*/
|
||||
export function ready(): void {
|
||||
rpc.processIncoming();
|
||||
rpc.sendReadyMessage();
|
||||
void rpc.sendReadyMessage();
|
||||
}
|
||||
|
||||
function getPluginPath(location: Location) {
|
||||
|
@ -715,7 +715,7 @@ export class ActiveDoc extends EventEmitter {
|
||||
*/
|
||||
public async getFormulaError(docSession: DocSession, tableId: string, colId: string,
|
||||
rowId: number): Promise<CellValue> {
|
||||
if (!this._granularAccess.hasTableAccess(docSession, tableId)) { return null; }
|
||||
if (!await this._granularAccess.hasTableAccess(docSession, tableId)) { return null; }
|
||||
this.logInfo(docSession, "getFormulaError(%s, %s, %s, %s)",
|
||||
docSession, tableId, colId, rowId);
|
||||
await this.waitForInitialization();
|
||||
@ -1094,7 +1094,7 @@ export class ActiveDoc extends EventEmitter {
|
||||
]);
|
||||
|
||||
// Migrate the document if needed.
|
||||
const values = marshal.loads(docInfoData!);
|
||||
const values = marshal.loads(docInfoData);
|
||||
const versionCol = values.schemaVersion;
|
||||
const docSchemaVersion = (versionCol && versionCol.length === 1 ? versionCol[0] : 0);
|
||||
if (docSchemaVersion < schemaVersion) {
|
||||
@ -1126,7 +1126,7 @@ export class ActiveDoc extends EventEmitter {
|
||||
const tableNames: string[] = await this._rawPyCall('load_meta_tables', tablesData, columnsData);
|
||||
|
||||
// Figure out which tables are on-demand.
|
||||
const tablesParsed: BulkColValues = marshal.loads(tablesData!);
|
||||
const tablesParsed: BulkColValues = marshal.loads(tablesData);
|
||||
const onDemandMap = zipObject(tablesParsed.tableId as string[], tablesParsed.onDemand);
|
||||
const onDemandNames = remove(tableNames, (t) => onDemandMap[t]);
|
||||
|
||||
@ -1183,7 +1183,7 @@ export class ActiveDoc extends EventEmitter {
|
||||
|
||||
const result: ApplyUAResult = await new Promise<ApplyUAResult>(
|
||||
(resolve, reject) =>
|
||||
this._sharing!.addUserAction({action, docSession, resolve, reject}));
|
||||
this._sharing.addUserAction({action, docSession, resolve, reject}));
|
||||
this.logDebug(docSession, "_applyUserActions returning %s", shortDesc(result));
|
||||
|
||||
if (result.isModification) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* of the client-side code.
|
||||
*/
|
||||
import * as express from 'express';
|
||||
import fetch, {RequestInit, Response as FetchResponse} from 'node-fetch';
|
||||
import fetch, {Response as FetchResponse, RequestInit} from 'node-fetch';
|
||||
|
||||
import {ApiError} from 'app/common/ApiError';
|
||||
import {getSlugIfNeeded, parseSubdomainStrictly} from 'app/common/gristUrls';
|
||||
|
@ -215,10 +215,9 @@ export class DocWorkerApi {
|
||||
|
||||
// Initiate a fork. Used internally to implement ActiveDoc.fork. Only usable via a Permit.
|
||||
this._app.post('/api/docs/:docId/create-fork', canEdit, throttled(async (req, res) => {
|
||||
const mreq = req as RequestWithLogin;
|
||||
const docId = stringParam(req.params.docId);
|
||||
const srcDocId = stringParam(req.body.srcDocId);
|
||||
if (srcDocId !== mreq.specialPermit?.otherDocId) { throw new Error('access denied'); }
|
||||
if (srcDocId !== req.specialPermit?.otherDocId) { throw new Error('access denied'); }
|
||||
await this._docManager.storageManager.prepareFork(srcDocId, docId);
|
||||
res.json({srcDocId, docId});
|
||||
}));
|
||||
@ -249,7 +248,7 @@ export class DocWorkerApi {
|
||||
this._app.post('/api/docs/:docId/recover', canEdit, throttled(async (req, res) => {
|
||||
const recoveryModeRaw = req.body.recoveryMode;
|
||||
const recoveryMode = (typeof recoveryModeRaw === 'boolean') ? recoveryModeRaw : undefined;
|
||||
if (!this._isOwner(req)) { throw new Error('Only owners can control recovery mode'); }
|
||||
if (!await this._isOwner(req)) { throw new Error('Only owners can control recovery mode'); }
|
||||
const activeDoc = await this._docManager.fetchDoc(docSessionFromRequest(req), getDocId(req), recoveryMode);
|
||||
res.json({
|
||||
recoveryMode: activeDoc.recoveryMode
|
||||
|
@ -16,7 +16,8 @@ import {HomeDBManager} from 'app/gen-server/lib/HomeDBManager';
|
||||
import {assertAccess, Authorizer, DocAuthorizer, DummyAuthorizer,
|
||||
isSingleUserMode} from 'app/server/lib/Authorizer';
|
||||
import {Client} from 'app/server/lib/Client';
|
||||
import {getDocSessionCachedDoc, makeExceptionalDocSession, makeOptDocSession, OptDocSession} from 'app/server/lib/DocSession';
|
||||
import {getDocSessionCachedDoc, makeExceptionalDocSession, makeOptDocSession} from 'app/server/lib/DocSession';
|
||||
import {OptDocSession} from 'app/server/lib/DocSession';
|
||||
import * as docUtils from 'app/server/lib/docUtils';
|
||||
import {GristServer} from 'app/server/lib/GristServer';
|
||||
import {IDocStorageManager} from 'app/server/lib/IDocStorageManager';
|
||||
@ -498,7 +499,7 @@ export class DocManager extends EventEmitter {
|
||||
// TODO: We should be skeptical of the upload file to close a possible
|
||||
// security vulnerability. See https://phab.getgrist.com/T457.
|
||||
const docName = await this._createNewDoc(id);
|
||||
const docPath = await this.storageManager.getPath(docName);
|
||||
const docPath: string = this.storageManager.getPath(docName);
|
||||
await docUtils.copyFile(uploadInfo.files[0].absPath, docPath);
|
||||
await this.storageManager.addToStorage(docName);
|
||||
return {title: basename, id: docName};
|
||||
|
@ -65,7 +65,12 @@ export class DocPluginManager {
|
||||
private _pluginInstances: PluginInstance[];
|
||||
|
||||
|
||||
constructor(private _localPlugins: LocalPlugin[], private _appRoot: string, private _activeDoc: ActiveDoc, private _server: GristServer) {
|
||||
constructor(
|
||||
private _localPlugins: LocalPlugin[],
|
||||
private _appRoot: string,
|
||||
private _activeDoc: ActiveDoc,
|
||||
private _server: GristServer
|
||||
) {
|
||||
this.gristDocAPI = new GristDocAPIImpl(_activeDoc);
|
||||
this._pluginInstances = [];
|
||||
this.ready = this._initialize();
|
||||
|
@ -439,7 +439,9 @@ export class DocStorage implements ISQLiteDB {
|
||||
* Note that SQLite may contain tables that aren't used for Grist data (e.g. attachments), for
|
||||
* which such encoding/marshalling is not used, and e.g. binary data is stored to BLOBs directly.
|
||||
*/
|
||||
private static _encodeValue(marshaller: marshal.Marshaller, sqlType: string, val: any): Uint8Array|string|number|boolean {
|
||||
private static _encodeValue(
|
||||
marshaller: marshal.Marshaller, sqlType: string, val: any
|
||||
): Uint8Array|string|number|boolean {
|
||||
const marshalled = () => {
|
||||
marshaller.marshal(val);
|
||||
return marshaller.dump();
|
||||
@ -1376,7 +1378,7 @@ export class DocStorage implements ISQLiteDB {
|
||||
// columns, or adding or removing or changing default values on a column."
|
||||
const row = await this.get("PRAGMA schema_version");
|
||||
assert(row && row.schema_version, "Could not retrieve schema_version.");
|
||||
const newSchemaVersion = row!.schema_version + 1;
|
||||
const newSchemaVersion = row.schema_version + 1;
|
||||
const tmpTableId = DocStorage._makeTmpTableId(tableId);
|
||||
await this._getDB().runEach(
|
||||
"PRAGMA writable_schema=ON",
|
||||
@ -1462,7 +1464,7 @@ export class DocStorage implements ISQLiteDB {
|
||||
* should be reasonably fast:
|
||||
* https://sqlite.org/tempfiles.html#temp_databases
|
||||
*/
|
||||
public async _fetchQueryWithManyParameters(query: ExpandedQuery): Promise<Buffer> {
|
||||
private async _fetchQueryWithManyParameters(query: ExpandedQuery): Promise<Buffer> {
|
||||
const db = this._getDB();
|
||||
return db.execTransaction(async () => {
|
||||
const tableNames: string[] = [];
|
||||
@ -1490,7 +1492,7 @@ export class DocStorage implements ISQLiteDB {
|
||||
* Construct SQL for an ExpandedQuery. Expects that filters have been converted into
|
||||
* a set of WHERE terms that should be ANDed.
|
||||
*/
|
||||
public _getSqlForQuery(query: ExpandedQuery, whereParts: string[]) {
|
||||
private _getSqlForQuery(query: ExpandedQuery, whereParts: string[]) {
|
||||
const whereClause = whereParts.length > 0 ? `WHERE ${whereParts.join(' AND ')}` : '';
|
||||
const limitClause = (typeof query.limit === 'number') ? `LIMIT ${query.limit}` : '';
|
||||
const joinClauses = query.joins ? query.joins.join(' ') : '';
|
||||
|
@ -196,7 +196,7 @@ export class DocStorageManager implements IDocStorageManager {
|
||||
* Electron version only. Shows the given doc in the file explorer.
|
||||
*/
|
||||
public async showItemInFolder(docName: string): Promise<void> {
|
||||
this._shell.showItemInFolder(await this.getPath(docName));
|
||||
this._shell.showItemInFolder(this.getPath(docName));
|
||||
}
|
||||
|
||||
public async closeStorage() {
|
||||
|
@ -133,7 +133,7 @@ export class KeyMappedExternalStorage implements ExternalStorage {
|
||||
export class ChecksummedExternalStorage implements ExternalStorage {
|
||||
private _closed: boolean = false;
|
||||
|
||||
constructor(readonly label: string, private _ext: ExternalStorage, private _options: {
|
||||
constructor(public readonly label: string, private _ext: ExternalStorage, private _options: {
|
||||
maxRetries: number, // how many time to retry inconsistent downloads
|
||||
initialDelayMs: number, // how long to wait before retrying
|
||||
localHash: PropStorage, // key/value store for hashes of downloaded content
|
||||
|
@ -145,7 +145,7 @@ export class FlexServer implements GristServer {
|
||||
private _sendAppPage: (req: express.Request, resp: express.Response, options: ISendAppPageOptions) => Promise<void>;
|
||||
|
||||
constructor(public port: number, public name: string = 'flexServer',
|
||||
readonly options: FlexServerOptions = {}) {
|
||||
public readonly options: FlexServerOptions = {}) {
|
||||
this.app = express();
|
||||
this.app.set('port', port);
|
||||
this.appRoot = getAppRoot();
|
||||
|
@ -724,7 +724,9 @@ export class GranularAccess implements GranularAccessForBundle {
|
||||
const ruler = await this._getRuler(cursor);
|
||||
const tableId = getTableId(action);
|
||||
const ruleSets = ruler.ruleCollection.getAllColumnRuleSets(tableId);
|
||||
const colIds = new Set(([] as string[]).concat(...ruleSets.map(ruleSet => ruleSet.colIds === '*' ? [] : ruleSet.colIds)));
|
||||
const colIds = new Set(([] as string[]).concat(
|
||||
...ruleSets.map(ruleSet => ruleSet.colIds === '*' ? [] : ruleSet.colIds)
|
||||
));
|
||||
const access = await ruler.getAccess(cursor.docSession);
|
||||
// Check columns in a consistent order, for determinism (easier testing).
|
||||
// TODO: could pool some work between columns by doing them together rather than one by one.
|
||||
@ -1164,7 +1166,9 @@ export class GranularAccess implements GranularAccessForBundle {
|
||||
const rowsBefore = cloneDeep(tableData?.getTableDataAction() || ['TableData', '', [], {}] as TableDataAction);
|
||||
docData.receiveAction(docAction);
|
||||
// If table is deleted, state afterwards doesn't matter.
|
||||
const rowsAfter = docData.getTable(tableId) ? cloneDeep(tableData?.getTableDataAction() || ['TableData', '', [], {}] as TableDataAction) : rowsBefore;
|
||||
const rowsAfter = docData.getTable(tableId) ?
|
||||
cloneDeep(tableData?.getTableDataAction() || ['TableData', '', [], {}] as TableDataAction) :
|
||||
rowsBefore;
|
||||
const step: ActionStep = {action: docAction, rowsBefore, rowsAfter};
|
||||
steps.push(step);
|
||||
}
|
||||
@ -1208,7 +1212,7 @@ export class GranularAccess implements GranularAccessForBundle {
|
||||
if (applied) {
|
||||
// Rules may have changed - back them off to a copy of their original state.
|
||||
ruler = new Ruler(this);
|
||||
ruler.update(metaDocData);
|
||||
await ruler.update(metaDocData);
|
||||
}
|
||||
let replaceRuler = false;
|
||||
for (const docAction of docActions) {
|
||||
@ -1228,7 +1232,7 @@ export class GranularAccess implements GranularAccessForBundle {
|
||||
replaceRuler = true;
|
||||
} else if (replaceRuler) {
|
||||
ruler = new Ruler(this);
|
||||
ruler.update(metaDocData);
|
||||
await ruler.update(metaDocData);
|
||||
replaceRuler = false;
|
||||
}
|
||||
step.ruler = ruler;
|
||||
@ -1625,7 +1629,8 @@ export class CensorshipInfo {
|
||||
const tableId = tableRefToTableId.get(tableRef);
|
||||
if (!tableId) { throw new Error('table not found'); }
|
||||
const colId = rec.get('colId') as string;
|
||||
if (this.censoredTables.has(tableRef) || (colId !== 'manualSort' && permInfo.getColumnAccess(tableId, colId).perms.read === 'deny')) {
|
||||
if (this.censoredTables.has(tableRef) ||
|
||||
(colId !== 'manualSort' && permInfo.getColumnAccess(tableId, colId).perms.read === 'deny')) {
|
||||
censoredColumnCodes.add(columnCode(tableRef, colId));
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ export class HostedStorageManager implements IDocStorageManager {
|
||||
await this.prepareLocalDoc(docName, 'new');
|
||||
if (this._inventory) {
|
||||
await this._inventory.create(docName);
|
||||
this._onInventoryChange(docName);
|
||||
await this._onInventoryChange(docName);
|
||||
}
|
||||
this.markAsChanged(docName);
|
||||
}
|
||||
|
@ -10,18 +10,14 @@ export const ITestingHooks = t.iface([], {
|
||||
"updateAuthToken": t.func("void", t.param("instId", "string"), t.param("authToken", "string")),
|
||||
"getAuthToken": t.func(t.union("string", "null"), t.param("instId", "string")),
|
||||
"useTestToken": t.func("void", t.param("instId", "string"), t.param("token", "string")),
|
||||
"setLoginSessionProfile": t.func("void", t.param("gristSidCookie", "string"),
|
||||
t.param("profile", t.union("UserProfile", "null")), t.param("org", "string", true)),
|
||||
"setLoginSessionProfile": t.func("void", t.param("gristSidCookie", "string"), t.param("profile", t.union("UserProfile", "null")), t.param("org", "string", true)),
|
||||
"setServerVersion": t.func("void", t.param("version", t.union("string", "null"))),
|
||||
"disconnectClients": t.func("void"),
|
||||
"commShutdown": t.func("void"),
|
||||
"commRestart": t.func("void"),
|
||||
"commSetClientPersistence": t.func("void", t.param("ttlMs", "number")),
|
||||
"closeDocs": t.func("void"),
|
||||
"setDocWorkerActivation": t.func("void", t.param("workerId", "string"),
|
||||
t.param("active", t.union(t.lit('active'),
|
||||
t.lit('inactive'),
|
||||
t.lit('crash')))),
|
||||
"setDocWorkerActivation": t.func("void", t.param("workerId", "string"), t.param("active", t.union(t.lit('active'), t.lit('inactive'), t.lit('crash')))),
|
||||
"flushAuthorizerCache": t.func("void"),
|
||||
"getDocClientCounts": t.func(t.array(t.tuple("string", "number"))),
|
||||
"setActiveDocTimeout": t.func("number", t.param("seconds", "number")),
|
||||
@ -29,6 +25,5 @@ export const ITestingHooks = t.iface([], {
|
||||
|
||||
const exportedTypeSuite: t.ITypeSuite = {
|
||||
ITestingHooks,
|
||||
UserProfile: t.name("object"),
|
||||
};
|
||||
export default exportedTypeSuite;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {UserProfile} from 'app/common/LoginSessionAPI';
|
||||
|
||||
export interface ITestingHooks {
|
||||
getOwnPort(): number;
|
||||
getPort(): number;
|
||||
getOwnPort(): Promise<number>;
|
||||
getPort(): Promise<number>;
|
||||
updateAuthToken(instId: string, authToken: string): Promise<void>;
|
||||
getAuthToken(instId: string): Promise<string|null>;
|
||||
useTestToken(instId: string, token: string): Promise<void>;
|
||||
|
@ -62,7 +62,7 @@ export class OnDemandActions {
|
||||
// Check that the actionType can be applied without the sandbox and also that the action
|
||||
// is on a data table.
|
||||
const isOnDemandAction = ACTION_TYPES.has(a[0] as string);
|
||||
const isDataTableAction = typeof a[1] === 'string' && !(a[1] as string).startsWith('_grist_');
|
||||
const isDataTableAction = typeof a[1] === 'string' && !a[1].startsWith('_grist_');
|
||||
if (a[0] === 'ApplyUndoActions') {
|
||||
// Split actions inside the undo action array.
|
||||
const [undoNormal, undoOnDemand] = this.splitByOnDemand(a[1] as UserAction[]);
|
||||
|
@ -155,7 +155,7 @@ export class Sharing {
|
||||
private async _rebaseLocalActions(): Promise<void> {
|
||||
const rebaseQueue: Deque<UserActionBundle> = new Deque<UserActionBundle>();
|
||||
try {
|
||||
await this.createCheckpoint();
|
||||
this.createCheckpoint();
|
||||
const actions: LocalActionBundle[] = await this._actionHistory.fetchAllLocal();
|
||||
assert(actions.length > 0);
|
||||
await this.doApplyUserActionBundle(this._createUndo(actions), null);
|
||||
@ -163,7 +163,7 @@ export class Sharing {
|
||||
await this._actionHistory.clearLocalActions();
|
||||
} catch (e) {
|
||||
log.error("Can't undo local actions; sharing is off");
|
||||
await this.rollbackToCheckpoint();
|
||||
this.rollbackToCheckpoint();
|
||||
// TODO this.disconnect();
|
||||
// TODO errorState = true;
|
||||
return;
|
||||
@ -185,11 +185,11 @@ export class Sharing {
|
||||
}
|
||||
}
|
||||
if (rebaseFailures.length > 0) {
|
||||
await this.createBackupAtCheckpoint();
|
||||
this.createBackupAtCheckpoint();
|
||||
// TODO we should notify the user too.
|
||||
log.error('Rebase failed to reapply some of your actions, backup of local at...');
|
||||
}
|
||||
await this.releaseCheckpoint();
|
||||
this.releaseCheckpoint();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@ -374,7 +374,9 @@ export class Sharing {
|
||||
const docActions = getEnvContent(sandboxActionBundle.stored).concat(
|
||||
getEnvContent(sandboxActionBundle.calc));
|
||||
|
||||
const accessControl = this._activeDoc.getGranularAccessForBundle(docSession || makeExceptionalDocSession('share'), docActions, undo, userActions);
|
||||
const accessControl = this._activeDoc.getGranularAccessForBundle(
|
||||
docSession || makeExceptionalDocSession('share'), docActions, undo, userActions
|
||||
);
|
||||
try {
|
||||
// TODO: see if any of the code paths that have no docSession are relevant outside
|
||||
// of tests.
|
||||
|
@ -6,13 +6,15 @@ import * as Comm from 'app/server/lib/Comm';
|
||||
import {ILoginSession} from 'app/server/lib/ILoginSession';
|
||||
import * as log from 'app/server/lib/log';
|
||||
import {IMessage, Rpc} from 'grain-rpc';
|
||||
import {createCheckers} from 'ts-interface-checker';
|
||||
import * as t from 'ts-interface-checker';
|
||||
import {FlexServer} from './FlexServer';
|
||||
import {IInstanceManager} from './IInstanceManager';
|
||||
import {ITestingHooks} from './ITestingHooks';
|
||||
import ITestingHooksTI from './ITestingHooks-ti';
|
||||
import {connect, fromCallback} from './serverUtils';
|
||||
|
||||
const tiCheckers = t.createCheckers(ITestingHooksTI, {UserProfile: t.name("object")});
|
||||
|
||||
export function startTestingHooks(socketPath: string, port: number, instanceManager: IInstanceManager,
|
||||
comm: Comm, flexServer: FlexServer,
|
||||
workerServers: FlexServer[]): Promise<net.Server> {
|
||||
@ -27,7 +29,7 @@ export function startTestingHooks(socketPath: string, port: number, instanceMana
|
||||
// Register the testing implementation.
|
||||
rpc.registerImpl('testing',
|
||||
new TestingHooks(port, instanceManager, comm, flexServer, workerServers),
|
||||
createCheckers(ITestingHooksTI).ITestingHooks);
|
||||
tiCheckers.ITestingHooks);
|
||||
});
|
||||
server.listen(socketPath);
|
||||
});
|
||||
@ -47,7 +49,7 @@ export interface TestingHooksClient extends ITestingHooks {
|
||||
export async function connectTestingHooks(socketPath: string): Promise<TestingHooksClient> {
|
||||
const socket = await connect(socketPath);
|
||||
const rpc = connectToSocket(new Rpc({logger: {}}), socket);
|
||||
return Object.assign(rpc.getStub<TestingHooks>('testing', createCheckers(ITestingHooksTI).ITestingHooks), {
|
||||
return Object.assign(rpc.getStub<TestingHooks>('testing', tiCheckers.ITestingHooks), {
|
||||
close: () => socket.end(),
|
||||
});
|
||||
}
|
||||
@ -61,12 +63,12 @@ export class TestingHooks implements ITestingHooks {
|
||||
private _workerServers: FlexServer[]
|
||||
) {}
|
||||
|
||||
public getOwnPort(): number {
|
||||
public async getOwnPort(): Promise<number> {
|
||||
log.info("TestingHooks.getOwnPort called");
|
||||
return this._server.getOwnPort();
|
||||
}
|
||||
|
||||
public getPort(): number {
|
||||
public async getPort(): Promise<number> {
|
||||
log.info("TestingHooks.getPort called");
|
||||
return this._port;
|
||||
}
|
||||
@ -100,7 +102,7 @@ export class TestingHooks implements ITestingHooks {
|
||||
log.info("TestingHooks.setServerVersion called with", version);
|
||||
this._comm.setServerVersion(version);
|
||||
for (const server of this._workerServers) {
|
||||
await server.comm.setServerVersion(version);
|
||||
server.comm.setServerVersion(version);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +110,7 @@ export class TestingHooks implements ITestingHooks {
|
||||
log.info("TestingHooks.disconnectClients called");
|
||||
this._comm.destroyAllClients();
|
||||
for (const server of this._workerServers) {
|
||||
await server.comm.destroyAllClients();
|
||||
server.comm.destroyAllClients();
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +136,7 @@ export class TestingHooks implements ITestingHooks {
|
||||
log.info("TestingHooks.setClientPersistence called with", ttlMs);
|
||||
this._comm.testSetClientPersistence(ttlMs);
|
||||
for (const server of this._workerServers) {
|
||||
await server.comm.testSetClientPersistence(ttlMs);
|
||||
server.comm.testSetClientPersistence(ttlMs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +176,7 @@ export class TestingHooks implements ITestingHooks {
|
||||
log.info("TestingHooks.flushAuthorizerCache called");
|
||||
this._server.dbManager.flushDocAuthCache();
|
||||
for (const server of this._workerServers) {
|
||||
await server.dbManager.flushDocAuthCache();
|
||||
server.dbManager.flushDocAuthCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ export async function createTmpDir(options: tmp.Options): Promise<TmpDirResult>
|
||||
try {
|
||||
// Still call the original callback, so that `tmp` module doesn't keep remembering about
|
||||
// this directory and doesn't try to delete it again on exit.
|
||||
tmpCleanup();
|
||||
await tmpCleanup();
|
||||
} catch (err) {
|
||||
// OK if it fails because the dir is already removed.
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
},
|
||||
"composite": true,
|
||||
"plugins": [{
|
||||
"name": "typescript-tslint-plugin"
|
||||
"name": "typescript-eslint-language-service"
|
||||
}],
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
|
@ -4,7 +4,7 @@ export type ProductFlavor = string;
|
||||
export interface CustomTheme {
|
||||
bodyClassName?: string;
|
||||
wideLogo?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export function getTheme(flavor: string): CustomTheme {
|
||||
return {
|
||||
|
@ -15,7 +15,7 @@ import { decodeUrl } from 'app/common/gristUrls';
|
||||
import { FullUser, UserProfile } from 'app/common/LoginSessionAPI';
|
||||
import { resetOrg } from 'app/common/resetOrg';
|
||||
import { TestState } from 'app/common/TestState';
|
||||
import { DocStateComparison, Organization as APIOrganization, UserAPIImpl, Workspace } from 'app/common/UserAPI';
|
||||
import { Organization as APIOrganization, DocStateComparison, UserAPIImpl, Workspace } from 'app/common/UserAPI';
|
||||
import { Organization } from 'app/gen-server/entity/Organization';
|
||||
import { Product } from 'app/gen-server/entity/Product';
|
||||
import { create } from 'app/server/lib/create';
|
||||
@ -163,7 +163,7 @@ export async function selectAll() {
|
||||
* Returns a WebElementPromise for the .viewsection_content element for the section which contains
|
||||
* the given RegExp content.
|
||||
*/
|
||||
export function getSection(sectionOrTitle: string|WebElement): WebElement {
|
||||
export function getSection(sectionOrTitle: string|WebElement): WebElement|WebElementPromise {
|
||||
if (typeof sectionOrTitle !== 'string') { return sectionOrTitle; }
|
||||
return driver.find(`.test-viewsection-title[value="${sectionOrTitle}" i]`)
|
||||
.findClosest('.viewsection_content');
|
||||
@ -1187,7 +1187,7 @@ export class Session {
|
||||
|
||||
// Wipe the current site. The current user ends up being its only owner and manager.
|
||||
public async resetSite() {
|
||||
return resetOrg(await this.createHomeApi(), this.settings.org);
|
||||
return resetOrg(this.createHomeApi(), this.settings.org);
|
||||
}
|
||||
|
||||
// Return a session configured for the current session's site but a different user.
|
||||
|
@ -55,7 +55,7 @@ export class TestServerMerged implements IMochaServer {
|
||||
public async restart(reset: boolean = false) {
|
||||
if (this.isExternalServer()) { return; }
|
||||
if (this._starts > 0) {
|
||||
await this.resume();
|
||||
this.resume();
|
||||
await this.stop();
|
||||
}
|
||||
this._starts++;
|
||||
|
Loading…
Reference in New Issue
Block a user