mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Show invalid columns as an error when entering rules
Summary: Auto-complete helps enter correct column names, and when incorrect ones are entered, we now show an error and prevent saving the rules. In an unrelated tweak, fix focusing of ACLFormula when clicking into scroll area. Test Plan: Added a test case for showing invalid columns Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2815
This commit is contained in:
parent
3a0ec7b103
commit
dee487684e
@ -71,6 +71,10 @@ export function aclFormulaEditor(options: ACLFormulaOptions) {
|
|||||||
|
|
||||||
return cssConditionInputAce(
|
return cssConditionInputAce(
|
||||||
cssConditionInputAce.cls('-disabled', options.readOnly),
|
cssConditionInputAce.cls('-disabled', options.readOnly),
|
||||||
|
// ACE editor calls preventDefault on clicks into the scrollbar area, which prevents focus
|
||||||
|
// being set when the click happens to be into there. To ensure we can focus on such clicks
|
||||||
|
// anyway, listen to the mousedown event in the capture phase.
|
||||||
|
dom.on('mousedown', () => { editor.focus(); }, {useCapture: true}),
|
||||||
dom.onDispose(() => editor.destroy()),
|
dom.onDispose(() => editor.destroy()),
|
||||||
editorElem,
|
editorElem,
|
||||||
);
|
);
|
||||||
|
@ -1118,6 +1118,7 @@ class ObsRulePart extends Disposable {
|
|||||||
}
|
}
|
||||||
this._error = Computed.create(this, (use) => {
|
this._error = Computed.create(this, (use) => {
|
||||||
return use(this._formulaError) ||
|
return use(this._formulaError) ||
|
||||||
|
this._warnInvalidColIds(use(this._formulaProperties).usedColIds) ||
|
||||||
( !this._ruleSet.isLastCondition(use, this) &&
|
( !this._ruleSet.isLastCondition(use, this) &&
|
||||||
use(this._aclFormula) === '' &&
|
use(this._aclFormula) === '' &&
|
||||||
permissionSetToText(use(this._permissions)) !== '' ?
|
permissionSetToText(use(this._permissions)) !== '' ?
|
||||||
@ -1231,6 +1232,7 @@ class ObsRulePart extends Disposable {
|
|||||||
if (text === this._aclFormula.get()) { return; }
|
if (text === this._aclFormula.get()) { return; }
|
||||||
this._aclFormula.set(text);
|
this._aclFormula.set(text);
|
||||||
this._checkPending.set(true);
|
this._checkPending.set(true);
|
||||||
|
this._formulaProperties.set({});
|
||||||
this._formulaError.set('');
|
this._formulaError.set('');
|
||||||
try {
|
try {
|
||||||
this._formulaProperties.set(await this._ruleSet.accessRules.checkAclFormula(text));
|
this._formulaProperties.set(await this._ruleSet.accessRules.checkAclFormula(text));
|
||||||
@ -1241,6 +1243,15 @@ class ObsRulePart extends Disposable {
|
|||||||
this._checkPending.set(false);
|
this._checkPending.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _warnInvalidColIds(colIds?: string[]) {
|
||||||
|
if (!colIds || !colIds.length) { return false; }
|
||||||
|
const allValid = new Set(this._ruleSet.getValidColIds());
|
||||||
|
const invalid = colIds.filter(c => !allValid.has(c));
|
||||||
|
if (invalid.length > 0) {
|
||||||
|
return `Invalid columns: ${invalid.join(', ')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,13 +55,15 @@ export type AclMatchFunc = (input: AclMatchInput) => boolean;
|
|||||||
/**
|
/**
|
||||||
* Representation of a parsed ACL formula.
|
* Representation of a parsed ACL formula.
|
||||||
*/
|
*/
|
||||||
export type ParsedAclFormula = [string, ...Array<ParsedAclFormula|CellValue>];
|
type PrimitiveCellValue = number|string|boolean|null;
|
||||||
|
export type ParsedAclFormula = [string, ...Array<ParsedAclFormula|PrimitiveCellValue>];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observations about a formula.
|
* Observations about a formula.
|
||||||
*/
|
*/
|
||||||
export interface FormulaProperties {
|
export interface FormulaProperties {
|
||||||
hasRecOrNewRec?: boolean;
|
hasRecOrNewRec?: boolean;
|
||||||
|
usedColIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserAttributeRule {
|
export interface UserAttributeRule {
|
||||||
@ -78,6 +80,9 @@ export interface UserAttributeRule {
|
|||||||
export function getFormulaProperties(formula: ParsedAclFormula) {
|
export function getFormulaProperties(formula: ParsedAclFormula) {
|
||||||
const result: FormulaProperties = {}
|
const result: FormulaProperties = {}
|
||||||
if (usesRec(formula)) { result.hasRecOrNewRec = true; }
|
if (usesRec(formula)) { result.hasRecOrNewRec = true; }
|
||||||
|
const colIds = new Set<string>();
|
||||||
|
collectRecColIds(formula, colIds);
|
||||||
|
result.usedColIds = Array.from(colIds);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +91,27 @@ export function getFormulaProperties(formula: ParsedAclFormula) {
|
|||||||
*/
|
*/
|
||||||
export function usesRec(formula: ParsedAclFormula): boolean {
|
export function usesRec(formula: ParsedAclFormula): boolean {
|
||||||
if (!Array.isArray(formula)) { throw new Error('expected a list'); }
|
if (!Array.isArray(formula)) { throw new Error('expected a list'); }
|
||||||
if (formula[0] === 'Name' && (formula[1] === 'rec' || formula[1] === 'newRec')) {
|
if (isRecOrNewRec(formula)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return formula.some(el => {
|
return formula.some(el => {
|
||||||
if (!Array.isArray(el)) { return false; }
|
if (!Array.isArray(el)) { return false; }
|
||||||
return usesRec(el as any);
|
return usesRec(el);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRecOrNewRec(formula: ParsedAclFormula|PrimitiveCellValue): boolean {
|
||||||
|
return Array.isArray(formula) &&
|
||||||
|
formula[0] === 'Name' &&
|
||||||
|
(formula[1] === 'rec' || formula[1] === 'newRec');
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectRecColIds(formula: ParsedAclFormula, colIds: Set<string>): void {
|
||||||
|
if (!Array.isArray(formula)) { throw new Error('expected a list'); }
|
||||||
|
if (formula[0] === 'Attr' && isRecOrNewRec(formula[1])) {
|
||||||
|
const colId = formula[2];
|
||||||
|
colIds.add(String(colId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formula.forEach(el => Array.isArray(el) && collectRecColIds(el, colIds));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user