budibase/packages/core/src/templateApi/validate.js

183 lines
5.0 KiB
JavaScript
Raw Normal View History

2019-07-15 08:12:52 +02:00
import {
filter, union, constant,
map, flatten, every, uniqBy,
2019-09-28 06:28:11 +02:00
some, includes, isEmpty, has
2019-07-15 08:12:52 +02:00
} from 'lodash/fp';
import { compileExpression, compileCode } from '@nx-js/compiler-util';
import {
$, isSomething, switchCase,
anyTrue, isNonEmptyArray, executesWithoutException,
isNonEmptyString, defaultCase,
} from '../common';
import {
isRecord, isRoot, isaggregateGroup,
isIndex, getFlattenedHierarchy,
} from './hierarchy';
import { eventsList } from '../common/events';
import { validateAllFields } from './fields';
import {
applyRuleSet, makerule, stringNotEmpty,
validationError,
} from '../common/validationCommon';
import { indexRuleSet } from './indexes';
import { validateAllAggregates } from './validateAggregate';
export const ruleSet = (...sets) => constant(flatten([...sets]));
const commonRules = [
makerule('name', 'node name is not set',
node => stringNotEmpty(node.name)),
makerule('type', 'node type not recognised',
anyTrue(isRecord, isRoot, isIndex, isaggregateGroup)),
];
const recordRules = [
makerule('fields', 'no fields have been added to the record',
node => isNonEmptyArray(node.fields)),
makerule('validationRules', "validation rule is missing a 'messageWhenValid' member",
2019-09-28 06:28:11 +02:00
node => every(r => has('messageWhenInvalid')(r))(node.validationRules)),
2019-07-15 08:12:52 +02:00
makerule('validationRules', "validation rule is missing a 'expressionWhenValid' member",
2019-09-28 06:28:11 +02:00
node => every(r => has('expressionWhenValid')(r))(node.validationRules)),
2019-07-15 08:12:52 +02:00
];
const aggregateGroupRules = [
makerule('condition', 'condition does not compile',
a => isEmpty(a.condition)
|| executesWithoutException(
() => compileExpression(a.condition),
)),
];
const getRuleSet = node => switchCase(
[isRecord, ruleSet(
commonRules,
recordRules,
)],
[isIndex, ruleSet(
commonRules,
indexRuleSet,
)],
[isaggregateGroup, ruleSet(
commonRules,
aggregateGroupRules,
)],
[defaultCase, ruleSet(commonRules, [])],
)(node);
export const validateNode = node => applyRuleSet(getRuleSet(node))(node);
export const validateAll = (appHierarchy) => {
const flattened = getFlattenedHierarchy(
appHierarchy,
);
const duplicateNameRule = makerule(
'name', 'node names must be unique under shared parent',
n => filter(f => f.parent() === n.parent()
&& f.name === n.name)(flattened).length === 1,
);
const duplicateNodeKeyErrors = $(flattened, [
map(n => applyRuleSet([duplicateNameRule])(n)),
filter(isSomething),
flatten,
]);
const fieldErrors = $(flattened, [
filter(isRecord),
map(validateAllFields),
flatten,
]);
const aggregateErrors = $(flattened, [
filter(isaggregateGroup),
map(s => validateAllAggregates(
s.aggregates,
)),
flatten,
]);
return $(flattened, [
map(validateNode),
flatten,
union(duplicateNodeKeyErrors),
union(fieldErrors),
union(aggregateErrors),
]);
};
const actionRules = [
makerule('name', 'action must have a name',
a => isNonEmptyString(a.name)),
makerule('behaviourName', 'must supply a behaviour name to the action',
a => isNonEmptyString(a.behaviourName)),
makerule('behaviourSource', 'must supply a behaviour source for the action',
a => isNonEmptyString(a.behaviourSource)),
];
const duplicateActionRule = makerule('', 'action name must be unique', () => {});
const validateAction = action => applyRuleSet(actionRules)(action);
export const validateActions = (allActions) => {
const duplicateActions = $(allActions, [
filter(a => filter(a2 => a2.name === a.name)(allActions).length > 1),
map(a => validationError(duplicateActionRule, a)),
]);
const errors = $(allActions, [
map(validateAction),
flatten,
union(duplicateActions),
uniqBy('name'),
]);
return errors;
};
const triggerRules = actions => ([
makerule('actionName', 'must specify an action',
t => isNonEmptyString(t.actionName)),
makerule('eventName', 'must specify and event',
t => isNonEmptyString(t.eventName)),
makerule('actionName', 'specified action not supplied',
t => !t.actionName
|| some(a => a.name === t.actionName)(actions)),
makerule('eventName', 'invalid Event Name',
t => !t.eventName
|| includes(t.eventName)(eventsList)),
makerule('optionsCreator', 'Options Creator does not compile - check your expression',
(t) => {
if (!t.optionsCreator) return true;
try {
compileCode(t.optionsCreator);
return true;
} catch (_) { return false; }
}),
makerule('condition', 'Trigger condition does not compile - check your expression',
(t) => {
if (!t.condition) return true;
try {
compileExpression(t.condition);
return true;
} catch (_) { return false; }
}),
]);
export const validateTrigger = (trigger, allActions) => {
const errors = applyRuleSet(triggerRules(allActions))(trigger);
return errors;
};
export const validateTriggers = (triggers, allActions) => $(triggers, [
map(t => validateTrigger(t, allActions)),
flatten,
]);