(core) updates from grist-core

This commit is contained in:
Paul Fitzpatrick
2024-10-21 10:07:40 -04:00
58 changed files with 887 additions and 126 deletions

View File

@@ -1084,6 +1084,16 @@ abstract class ObsRuleSet extends Disposable {
public getCustomRules(): ObsRulePart[] {
return this._body.get().filter(rule => !rule.isBuiltInOrEmpty());
}
/**
* If the set applies to a special column, return its name.
*/
public getSpecialColumn(): string|undefined {
if (this._ruleSet?.tableId === SPECIAL_RULES_TABLE_ID &&
this._ruleSet.colIds.length === 1) {
return this._ruleSet.colIds[0];
}
}
}
class ColumnObsRuleSet extends ObsRuleSet {
@@ -1635,6 +1645,14 @@ class ObsRulePart extends Disposable {
!isEqual(use(this._permissions), this._rulePart?.permissions ?? emptyPerms)
);
});
// The formula may be invalid from the beginning. Make sure we show errors in this
// case.
const text = this._aclFormula.get();
if (text) {
this._setAclFormula(text, true).catch(e => {
console.error(e);
});
}
}
public getRulePart(): RuleRec {
@@ -1790,8 +1808,8 @@ class ObsRulePart extends Disposable {
return this.isBuiltIn() && this._ruleSet.getFirstBuiltIn() !== this;
}
private async _setAclFormula(text: string) {
if (text === this._aclFormula.get()) { return; }
private async _setAclFormula(text: string, initial: boolean = false) {
if (text === this._aclFormula.get() && !initial) { return; }
this._aclFormula.set(text);
this._checkPending.set(true);
this._formulaProperties.set({});
@@ -1809,6 +1827,12 @@ class ObsRulePart extends Disposable {
private _warnInvalidColIds(colIds?: string[]) {
if (!colIds || !colIds.length) { return false; }
const allValid = new Set(this._ruleSet.getValidColIds());
const specialColumn = this._ruleSet.getSpecialColumn();
if (specialColumn === 'SeedRule') {
// We allow seed rules to refer to columns without checking
// them (until the seed rules are used).
return false;
}
const invalid = colIds.filter(c => !allValid.has(c));
if (invalid.length > 0) {
return `Invalid columns: ${invalid.join(', ')}`;

View File

@@ -71,7 +71,13 @@ export class GristClientSocket {
}
private _createWSSocket() {
if (typeof WebSocket !== 'undefined') {
// We used to check if WebSocket was defined here, and use it
// if so, secure in the fact that we were in the browser and
// the browser would pass along cookie information. But recent
// node defines WebSocket, so we narrow down this path to when
// a global document is defined (window doesn't work because
// some tests mock it).
if (typeof document !== 'undefined') {
this._wsSocket = new WebSocket(this._url);
} else {
this._wsSocket = new WS(this._url, undefined, this._options);

View File

@@ -12,14 +12,28 @@ import mapValues = require('lodash/mapValues');
import {ActionGroupOptions, ActionHistory, ActionHistoryUndoInfo, asActionGroup,
asMinimalActionGroup} from './ActionHistory';
import {ISQLiteDB, ResultRow} from './SQLiteDB';
import { appSettings } from './AppSettings';
const section = appSettings.section('history').section('action');
// History will from time to time be pruned back to within these limits
// on rows and the maximum total number of bytes in the "body" column.
// Pruning is done when the history has grown above these limits, to
// the specified factor.
const ACTION_HISTORY_MAX_ROWS = 1000;
const ACTION_HISTORY_MAX_BYTES = 1000 * 1000 * 1000; // 1 GB.
const ACTION_HISTORY_GRACE_FACTOR = 1.25; // allow growth to 1250 rows / 1.25 GB.
const ACTION_HISTORY_MAX_ROWS = section.flag('maxRows').requireInt({
envVar: 'GRIST_ACTION_HISTORY_MAX_ROWS',
defaultValue: 1000,
minValue: 1,
});
const ACTION_HISTORY_MAX_BYTES = section.flag('maxBytes').requireInt({
envVar: 'GRIST_ACTION_HISTORY_MAX_BYTES',
defaultValue: 1e9, // 1 GB.
minValue: 1, // 1 B.
});
const ACTION_HISTORY_GRACE_FACTOR = 1.25; // allow growth to 1.25 times the above limits.
const ACTION_HISTORY_CHECK_PERIOD = 10; // number of actions between size checks.
/**

View File

@@ -99,7 +99,7 @@ export class AppSettings {
/**
* As for readInt() but fail if nothing was found.
*/
public requireInt(query: AppSettingQuery): number {
public requireInt(query: AppSettingQueryInt): number {
const result = this.readInt(query);
if (result === undefined) {
throw new Error(`missing environment variable: ${query.envVar}`);
@@ -122,9 +122,19 @@ export class AppSettings {
* As for read() but type (and store, and report) the result as
* an integer (well, a number).
*/
public readInt(query: AppSettingQuery): number|undefined {
public readInt(query: AppSettingQueryInt): number|undefined {
this.readString(query);
const result = this.getAsInt();
if (result !== undefined) {
if (query.minValue !== undefined && result < query.minValue) {
throw new Error(`value ${result} is less than minimum ${query.minValue}`);
}
if (query.maxValue !== undefined && result > query.maxValue) {
throw new Error(`value ${result} is greater than maximum ${query.maxValue}`);
}
}
this._value = result;
return result;
}
@@ -213,11 +223,39 @@ export const appSettings = new AppSettings('grist');
* environment variables and default values.
*/
export interface AppSettingQuery {
envVar: string|string[]; // environment variable(s) to check.
preferredEnvVar?: string; // "Canonical" environment variable to suggest.
// Should be in envVar (though this is not checked).
defaultValue?: JSONValue; // value to use if variable(s) unavailable.
censor?: boolean; // should the value of the setting be obscured when printed.
/**
* Environment variable(s) to check.
*/
envVar: string|string[];
/**
* "Canonical" environment variable to suggest. Should be in envVar (though this is not checked).
*/
preferredEnvVar?: string;
/**
* Value to use if the variable(s) is/are unavailable.
*/
defaultValue?: JSONValue;
/**
* When set to true, the value is obscured when printed.
*/
censor?: boolean;
}
export interface AppSettingQueryInt extends AppSettingQuery {
/**
* Value to use if variable(s) unavailable.
*/
defaultValue?: number;
/**
* Minimum value allowed. Raises an error if the value is lower than this.
* If the value is undefined, the setting is not checked.
*/
minValue?: number;
/**
* Maximum value allowed. Raises an error if the value is greater than this.
* If the value is undefined, the setting is not checked.
*/
maxValue?: number;
}
/**

View File

@@ -141,6 +141,7 @@ export class OIDCConfig {
});
const httpTimeout = section.flag('httpTimeout').readInt({
envVar: 'GRIST_OIDC_SP_HTTP_TIMEOUT',
minValue: 0, // 0 means no timeout
});
this._namePropertyKey = section.flag('namePropertyKey').readString({
envVar: 'GRIST_OIDC_SP_PROFILE_NAME_ATTR',