mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) updates from grist-core
This commit is contained in:
@@ -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(', ')}`;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user