2021-01-04 10:16:08 +00:00
|
|
|
import {
|
|
|
|
|
actionChannel,
|
|
|
|
|
call,
|
|
|
|
|
fork,
|
|
|
|
|
put,
|
|
|
|
|
select,
|
|
|
|
|
take,
|
|
|
|
|
} from "redux-saga/effects";
|
2020-12-28 08:42:43 +00:00
|
|
|
|
2020-10-21 04:25:32 +00:00
|
|
|
import {
|
2020-11-03 10:16:11 +00:00
|
|
|
EvaluationReduxAction,
|
2020-10-21 04:25:32 +00:00
|
|
|
ReduxAction,
|
|
|
|
|
ReduxActionErrorTypes,
|
|
|
|
|
ReduxActionTypes,
|
2021-04-26 05:41:32 +00:00
|
|
|
ReduxActionWithoutPayload,
|
2020-10-21 04:25:32 +00:00
|
|
|
} from "constants/ReduxActionConstants";
|
2021-01-14 14:37:21 +00:00
|
|
|
import { getUnevaluatedDataTree } from "selectors/dataTreeSelectors";
|
2020-10-21 04:25:32 +00:00
|
|
|
import WidgetFactory, { WidgetTypeConfigMap } from "../utils/WidgetFactory";
|
2021-02-22 05:00:16 +00:00
|
|
|
import { GracefulWorkerService } from "utils/WorkerUtil";
|
2020-10-21 04:25:32 +00:00
|
|
|
import Worker from "worker-loader!../workers/evaluation.worker";
|
|
|
|
|
import {
|
|
|
|
|
EVAL_WORKER_ACTIONS,
|
|
|
|
|
EvalError,
|
|
|
|
|
EvalErrorTypes,
|
2021-06-21 11:09:51 +00:00
|
|
|
EvaluationError,
|
|
|
|
|
getEvalErrorPath,
|
|
|
|
|
getEvalValuePath,
|
|
|
|
|
PropertyEvalErrorTypeDebugMessage,
|
|
|
|
|
PropertyEvaluationErrorType,
|
2021-02-22 05:00:16 +00:00
|
|
|
} from "utils/DynamicBindingUtils";
|
2020-10-21 04:25:32 +00:00
|
|
|
import log from "loglevel";
|
2021-02-22 05:00:16 +00:00
|
|
|
import { WidgetProps } from "widgets/BaseWidget";
|
2020-11-03 10:16:11 +00:00
|
|
|
import PerformanceTracker, {
|
|
|
|
|
PerformanceTransactionName,
|
|
|
|
|
} from "../utils/PerformanceTracker";
|
2020-11-11 11:32:14 +00:00
|
|
|
import * as Sentry from "@sentry/react";
|
2020-12-30 13:26:44 +00:00
|
|
|
import { Action } from "redux";
|
2021-01-14 11:15:25 +00:00
|
|
|
import _ from "lodash";
|
2021-06-21 11:09:51 +00:00
|
|
|
import { ENTITY_TYPE, Message } from "entities/AppsmithConsole";
|
2021-06-04 07:09:36 +00:00
|
|
|
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
|
|
|
|
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
|
|
|
|
import { AppState } from "reducers";
|
|
|
|
|
import {
|
|
|
|
|
getEntityNameAndPropertyPath,
|
|
|
|
|
isAction,
|
|
|
|
|
isWidget,
|
|
|
|
|
} from "workers/evaluationUtils";
|
|
|
|
|
import moment from "moment/moment";
|
|
|
|
|
import { Toaster } from "components/ads/Toast";
|
|
|
|
|
import { Variant } from "components/ads/common";
|
|
|
|
|
import AppsmithConsole from "utils/AppsmithConsole";
|
|
|
|
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
2021-03-13 14:24:45 +00:00
|
|
|
import {
|
|
|
|
|
createMessage,
|
|
|
|
|
ERROR_EVAL_ERROR_GENERIC,
|
|
|
|
|
ERROR_EVAL_TRIGGER,
|
|
|
|
|
} from "constants/messages";
|
2021-06-21 11:09:51 +00:00
|
|
|
import { getAppMode } from "selectors/applicationSelectors";
|
|
|
|
|
import { APP_MODE } from "reducers/entityReducers/appReducer";
|
2020-10-21 04:25:32 +00:00
|
|
|
|
|
|
|
|
let widgetTypeConfigMap: WidgetTypeConfigMap;
|
|
|
|
|
|
2020-12-28 08:42:43 +00:00
|
|
|
const worker = new GracefulWorkerService(Worker);
|
2020-10-21 04:25:32 +00:00
|
|
|
|
2021-06-04 07:09:36 +00:00
|
|
|
const getDebuggerErrors = (state: AppState) => state.ui.debugger.errors;
|
|
|
|
|
|
|
|
|
|
function getLatestEvalPropertyErrors(
|
|
|
|
|
currentDebuggerErrors: Record<string, Message>,
|
|
|
|
|
dataTree: DataTree,
|
|
|
|
|
evaluationOrder: Array<string>,
|
|
|
|
|
) {
|
|
|
|
|
const updatedDebuggerErrors: Record<string, Message> = {
|
|
|
|
|
...currentDebuggerErrors,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const evaluatedPath of evaluationOrder) {
|
|
|
|
|
const { entityName, propertyPath } = getEntityNameAndPropertyPath(
|
|
|
|
|
evaluatedPath,
|
|
|
|
|
);
|
|
|
|
|
const entity = dataTree[entityName];
|
|
|
|
|
if (isWidget(entity) || isAction(entity)) {
|
|
|
|
|
if (propertyPath in entity.logBlackList) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-06-21 11:09:51 +00:00
|
|
|
const allEvalErrors: EvaluationError[] = _.get(
|
2021-06-04 07:09:36 +00:00
|
|
|
entity,
|
2021-06-21 11:09:51 +00:00
|
|
|
getEvalErrorPath(evaluatedPath, false),
|
|
|
|
|
[],
|
2021-06-04 07:09:36 +00:00
|
|
|
);
|
|
|
|
|
const evaluatedValue = _.get(
|
|
|
|
|
entity,
|
2021-06-21 11:09:51 +00:00
|
|
|
getEvalValuePath(evaluatedPath, false),
|
|
|
|
|
);
|
|
|
|
|
const evalErrors = allEvalErrors.filter(
|
|
|
|
|
(error) => error.errorType !== PropertyEvaluationErrorType.LINT,
|
2021-06-04 07:09:36 +00:00
|
|
|
);
|
|
|
|
|
const idField = isWidget(entity) ? entity.widgetId : entity.actionId;
|
|
|
|
|
const nameField = isWidget(entity) ? entity.widgetName : entity.name;
|
|
|
|
|
const entityType = isWidget(entity)
|
|
|
|
|
? ENTITY_TYPE.WIDGET
|
|
|
|
|
: ENTITY_TYPE.ACTION;
|
|
|
|
|
const debuggerKey = idField + "-" + propertyPath;
|
|
|
|
|
// if dataTree has error but debugger does not -> add
|
|
|
|
|
// if debugger has error and data tree has error -> update error
|
|
|
|
|
// if debugger has error but data tree does not -> remove
|
|
|
|
|
// if debugger or data tree does not have an error -> no change
|
|
|
|
|
|
2021-06-21 11:09:51 +00:00
|
|
|
if (evalErrors.length) {
|
|
|
|
|
// TODO Rank and set the most critical error
|
|
|
|
|
const error = evalErrors[0];
|
2021-06-04 07:09:36 +00:00
|
|
|
// Add or update
|
|
|
|
|
updatedDebuggerErrors[debuggerKey] = {
|
|
|
|
|
logType: LOG_TYPE.EVAL_ERROR,
|
2021-06-21 11:09:51 +00:00
|
|
|
text: PropertyEvalErrorTypeDebugMessage[error.errorType](
|
|
|
|
|
propertyPath,
|
|
|
|
|
),
|
|
|
|
|
message: JSON.stringify(error.errorMessage),
|
|
|
|
|
severity: error.severity,
|
2021-06-04 07:09:36 +00:00
|
|
|
timestamp: moment().format("hh:mm:ss"),
|
|
|
|
|
source: {
|
|
|
|
|
id: idField,
|
|
|
|
|
name: nameField,
|
|
|
|
|
type: entityType,
|
|
|
|
|
propertyPath: propertyPath,
|
|
|
|
|
},
|
|
|
|
|
state: {
|
|
|
|
|
value: evaluatedValue,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
} else if (debuggerKey in updatedDebuggerErrors) {
|
|
|
|
|
// Remove
|
|
|
|
|
delete updatedDebuggerErrors[debuggerKey];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return updatedDebuggerErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function* evalErrorHandler(
|
|
|
|
|
errors: EvalError[],
|
|
|
|
|
dataTree?: DataTree,
|
|
|
|
|
evaluationOrder?: Array<string>,
|
|
|
|
|
): any {
|
|
|
|
|
if (dataTree && evaluationOrder) {
|
|
|
|
|
const currentDebuggerErrors: Record<string, Message> = yield select(
|
|
|
|
|
getDebuggerErrors,
|
|
|
|
|
);
|
|
|
|
|
const evalPropertyErrors = getLatestEvalPropertyErrors(
|
|
|
|
|
currentDebuggerErrors,
|
|
|
|
|
dataTree,
|
|
|
|
|
evaluationOrder,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOGS,
|
|
|
|
|
payload: evalPropertyErrors,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-24 04:32:25 +00:00
|
|
|
errors.forEach((error) => {
|
2021-02-22 05:00:16 +00:00
|
|
|
switch (error.type) {
|
2021-06-04 07:09:36 +00:00
|
|
|
case EvalErrorTypes.CYCLICAL_DEPENDENCY_ERROR: {
|
2021-03-10 06:58:52 +00:00
|
|
|
if (error.context) {
|
|
|
|
|
// Add more info about node for the toast
|
2021-05-13 08:35:39 +00:00
|
|
|
const { entityType, node } = error.context;
|
2021-03-10 06:58:52 +00:00
|
|
|
Toaster.show({
|
|
|
|
|
text: `${error.message} Node was: ${node}`,
|
|
|
|
|
variant: Variant.danger,
|
|
|
|
|
});
|
2021-05-18 13:54:40 +00:00
|
|
|
AppsmithConsole.error({
|
|
|
|
|
text: `${error.message} Node was: ${node}`,
|
|
|
|
|
});
|
2021-03-10 06:58:52 +00:00
|
|
|
// Send the generic error message to sentry for better grouping
|
|
|
|
|
Sentry.captureException(new Error(error.message), {
|
|
|
|
|
tags: {
|
|
|
|
|
node,
|
|
|
|
|
entityType,
|
|
|
|
|
},
|
|
|
|
|
// Level is warning because it could be a user error
|
|
|
|
|
level: Sentry.Severity.Warning,
|
|
|
|
|
});
|
2021-05-05 11:21:15 +00:00
|
|
|
// Log an analytics event for cyclical dep errors
|
|
|
|
|
AnalyticsUtil.logEvent("CYCLICAL_DEPENDENCY_ERROR", {
|
|
|
|
|
node,
|
|
|
|
|
entityType,
|
|
|
|
|
// Level is warning because it could be a user error
|
|
|
|
|
level: Sentry.Severity.Warning,
|
|
|
|
|
});
|
2021-03-10 06:58:52 +00:00
|
|
|
}
|
|
|
|
|
|
2021-02-22 05:00:16 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case EvalErrorTypes.EVAL_TREE_ERROR: {
|
|
|
|
|
Toaster.show({
|
2021-03-13 14:24:45 +00:00
|
|
|
text: createMessage(ERROR_EVAL_ERROR_GENERIC),
|
2021-02-22 05:00:16 +00:00
|
|
|
variant: Variant.danger,
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case EvalErrorTypes.BAD_UNEVAL_TREE_ERROR: {
|
|
|
|
|
Sentry.captureException(error);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case EvalErrorTypes.EVAL_TRIGGER_ERROR: {
|
2021-02-25 11:53:06 +00:00
|
|
|
log.debug(error);
|
2021-02-22 05:00:16 +00:00
|
|
|
Toaster.show({
|
2021-03-13 14:24:45 +00:00
|
|
|
text: createMessage(ERROR_EVAL_TRIGGER, error.message),
|
2021-02-22 05:00:16 +00:00
|
|
|
variant: Variant.danger,
|
2021-05-10 07:43:12 +00:00
|
|
|
showDebugButton: true,
|
|
|
|
|
});
|
|
|
|
|
AppsmithConsole.error({
|
|
|
|
|
text: createMessage(ERROR_EVAL_TRIGGER, error.message),
|
2021-02-22 05:00:16 +00:00
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-06-21 11:09:51 +00:00
|
|
|
case EvalErrorTypes.EVAL_PROPERTY_ERROR: {
|
|
|
|
|
log.debug(error);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-02-22 05:00:16 +00:00
|
|
|
default: {
|
|
|
|
|
Sentry.captureException(error);
|
2021-02-25 11:53:06 +00:00
|
|
|
log.debug(error);
|
2021-02-22 05:00:16 +00:00
|
|
|
}
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
});
|
2021-06-04 07:09:36 +00:00
|
|
|
}
|
2020-10-21 04:25:32 +00:00
|
|
|
|
2021-06-21 11:09:51 +00:00
|
|
|
function* logSuccessfulBindings(
|
|
|
|
|
unEvalTree: DataTree,
|
|
|
|
|
dataTree: DataTree,
|
|
|
|
|
evaluationOrder: string[],
|
|
|
|
|
) {
|
|
|
|
|
const appMode = yield select(getAppMode);
|
|
|
|
|
if (appMode === APP_MODE.PUBLISHED) return;
|
|
|
|
|
if (!evaluationOrder) return;
|
|
|
|
|
evaluationOrder.forEach((evaluatedPath) => {
|
|
|
|
|
const { entityName, propertyPath } = getEntityNameAndPropertyPath(
|
|
|
|
|
evaluatedPath,
|
|
|
|
|
);
|
|
|
|
|
const entity = dataTree[entityName];
|
|
|
|
|
if (isAction(entity) || isWidget(entity)) {
|
|
|
|
|
const unevalValue = _.get(unEvalTree, evaluatedPath);
|
|
|
|
|
const entityType = isAction(entity) ? entity.pluginType : entity.type;
|
|
|
|
|
const isABinding = _.find(entity.dynamicBindingPathList, {
|
|
|
|
|
key: propertyPath,
|
|
|
|
|
});
|
|
|
|
|
const logBlackList = entity.logBlackList;
|
|
|
|
|
const errors: EvaluationError[] = _.get(
|
|
|
|
|
dataTree,
|
|
|
|
|
getEvalErrorPath(evaluatedPath),
|
|
|
|
|
[],
|
|
|
|
|
) as EvaluationError[];
|
|
|
|
|
const criticalErrors = errors.filter(
|
|
|
|
|
(error) => error.errorType !== PropertyEvaluationErrorType.LINT,
|
|
|
|
|
);
|
|
|
|
|
const hasErrors = criticalErrors.length > 0;
|
|
|
|
|
|
|
|
|
|
if (isABinding && !hasErrors && !(propertyPath in logBlackList)) {
|
|
|
|
|
AnalyticsUtil.logEvent("BINDING_SUCCESS", {
|
|
|
|
|
unevalValue,
|
|
|
|
|
entityType,
|
|
|
|
|
propertyPath,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-26 05:41:32 +00:00
|
|
|
function* postEvalActionDispatcher(
|
|
|
|
|
actions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
|
|
|
|
|
) {
|
2020-11-03 10:16:11 +00:00
|
|
|
for (const action of actions) {
|
|
|
|
|
yield put(action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-26 05:41:32 +00:00
|
|
|
function* evaluateTreeSaga(
|
|
|
|
|
postEvalActions?: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
|
|
|
|
|
) {
|
2021-03-31 07:40:59 +00:00
|
|
|
const unevalTree = yield select(getUnevaluatedDataTree);
|
|
|
|
|
log.debug({ unevalTree });
|
2020-11-03 10:16:11 +00:00
|
|
|
PerformanceTracker.startAsyncTracking(
|
|
|
|
|
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
|
|
|
|
);
|
2020-12-28 08:42:43 +00:00
|
|
|
const workerResponse = yield call(
|
|
|
|
|
worker.request,
|
|
|
|
|
EVAL_WORKER_ACTIONS.EVAL_TREE,
|
|
|
|
|
{
|
2021-01-04 10:16:08 +00:00
|
|
|
unevalTree,
|
2020-12-28 08:42:43 +00:00
|
|
|
widgetTypeConfigMap,
|
|
|
|
|
},
|
|
|
|
|
);
|
2021-06-04 07:09:36 +00:00
|
|
|
const {
|
|
|
|
|
dataTree,
|
|
|
|
|
dependencies,
|
|
|
|
|
errors,
|
|
|
|
|
evaluationOrder,
|
|
|
|
|
logs,
|
|
|
|
|
} = workerResponse;
|
2021-03-31 07:40:59 +00:00
|
|
|
PerformanceTracker.stopAsyncTracking(
|
|
|
|
|
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
|
|
|
|
);
|
2021-06-21 11:09:51 +00:00
|
|
|
log.debug({ dataTree: dataTree });
|
|
|
|
|
logs.forEach((evalLog: any) => log.debug(evalLog));
|
|
|
|
|
yield call(evalErrorHandler, errors, dataTree, evaluationOrder);
|
|
|
|
|
yield fork(logSuccessfulBindings, unevalTree, dataTree, evaluationOrder);
|
|
|
|
|
|
2021-03-31 07:40:59 +00:00
|
|
|
PerformanceTracker.startAsyncTracking(
|
|
|
|
|
PerformanceTransactionName.SET_EVALUATED_TREE,
|
|
|
|
|
);
|
2020-10-21 04:25:32 +00:00
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.SET_EVALUATED_TREE,
|
2021-01-04 10:16:08 +00:00
|
|
|
payload: dataTree,
|
|
|
|
|
});
|
2021-04-23 11:34:33 +00:00
|
|
|
PerformanceTracker.stopAsyncTracking(
|
2021-03-31 07:40:59 +00:00
|
|
|
PerformanceTransactionName.SET_EVALUATED_TREE,
|
|
|
|
|
);
|
2021-01-04 10:16:08 +00:00
|
|
|
yield put({
|
2021-01-14 14:37:21 +00:00
|
|
|
type: ReduxActionTypes.SET_EVALUATION_INVERSE_DEPENDENCY_MAP,
|
|
|
|
|
payload: { inverseDependencyMap: dependencies },
|
2020-10-21 04:25:32 +00:00
|
|
|
});
|
2020-11-03 10:16:11 +00:00
|
|
|
if (postEvalActions && postEvalActions.length) {
|
|
|
|
|
yield call(postEvalActionDispatcher, postEvalActions);
|
|
|
|
|
}
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
|
2021-01-14 14:37:21 +00:00
|
|
|
export function* evaluateActionBindings(
|
|
|
|
|
bindings: string[],
|
|
|
|
|
executionParams: Record<string, any> | string = {},
|
2020-12-14 18:48:13 +00:00
|
|
|
) {
|
2020-12-28 08:42:43 +00:00
|
|
|
const workerResponse = yield call(
|
|
|
|
|
worker.request,
|
2021-01-14 14:37:21 +00:00
|
|
|
EVAL_WORKER_ACTIONS.EVAL_ACTION_BINDINGS,
|
2020-12-28 08:42:43 +00:00
|
|
|
{
|
2021-01-14 14:37:21 +00:00
|
|
|
bindings,
|
|
|
|
|
executionParams,
|
2020-12-28 08:42:43 +00:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2021-01-14 14:37:21 +00:00
|
|
|
const { errors, values } = workerResponse;
|
2020-12-28 08:42:43 +00:00
|
|
|
|
2021-06-04 07:09:36 +00:00
|
|
|
yield call(evalErrorHandler, errors);
|
2021-01-14 14:37:21 +00:00
|
|
|
return values;
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function* evaluateDynamicTrigger(
|
|
|
|
|
dynamicTrigger: string,
|
2020-11-20 09:30:50 +00:00
|
|
|
callbackData?: Array<any>,
|
2020-10-21 04:25:32 +00:00
|
|
|
) {
|
2020-12-28 08:42:43 +00:00
|
|
|
const unEvalTree = yield select(getUnevaluatedDataTree);
|
|
|
|
|
|
|
|
|
|
const workerResponse = yield call(
|
|
|
|
|
worker.request,
|
|
|
|
|
EVAL_WORKER_ACTIONS.EVAL_TRIGGER,
|
|
|
|
|
{ dataTree: unEvalTree, dynamicTrigger, callbackData },
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const { errors, triggers } = workerResponse;
|
2021-06-04 07:09:36 +00:00
|
|
|
yield call(evalErrorHandler, errors);
|
2020-12-28 08:42:43 +00:00
|
|
|
return triggers;
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function* clearEvalCache() {
|
2020-12-28 08:42:43 +00:00
|
|
|
yield call(worker.request, EVAL_WORKER_ACTIONS.CLEAR_CACHE);
|
|
|
|
|
|
|
|
|
|
return true;
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function* clearEvalPropertyCache(propertyPath: string) {
|
2020-12-28 08:42:43 +00:00
|
|
|
yield call(worker.request, EVAL_WORKER_ACTIONS.CLEAR_PROPERTY_CACHE, {
|
|
|
|
|
propertyPath,
|
|
|
|
|
});
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-01 05:26:35 +00:00
|
|
|
/**
|
|
|
|
|
* clears all cache keys of a widget
|
|
|
|
|
*
|
|
|
|
|
* @param widgetName
|
|
|
|
|
*/
|
|
|
|
|
export function* clearEvalPropertyCacheOfWidget(widgetName: string) {
|
2020-12-28 08:42:43 +00:00
|
|
|
yield call(
|
|
|
|
|
worker.request,
|
|
|
|
|
EVAL_WORKER_ACTIONS.CLEAR_PROPERTY_CACHE_OF_WIDGET,
|
|
|
|
|
{
|
2020-12-01 05:26:35 +00:00
|
|
|
widgetName,
|
2020-12-28 08:42:43 +00:00
|
|
|
},
|
|
|
|
|
);
|
2020-12-01 05:26:35 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 04:25:32 +00:00
|
|
|
export function* validateProperty(
|
|
|
|
|
property: string,
|
|
|
|
|
value: any,
|
|
|
|
|
props: WidgetProps,
|
|
|
|
|
) {
|
2021-04-21 14:34:25 +00:00
|
|
|
const unevalTree = yield select(getUnevaluatedDataTree);
|
|
|
|
|
const validation = unevalTree[props.widgetName].validationPaths[property];
|
2020-12-28 08:42:43 +00:00
|
|
|
return yield call(worker.request, EVAL_WORKER_ACTIONS.VALIDATE_PROPERTY, {
|
|
|
|
|
property,
|
|
|
|
|
value,
|
|
|
|
|
props,
|
2021-04-21 14:34:25 +00:00
|
|
|
validation,
|
2020-12-28 08:42:43 +00:00
|
|
|
});
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-30 13:26:44 +00:00
|
|
|
const FIRST_EVAL_REDUX_ACTIONS = [
|
|
|
|
|
// Pages
|
|
|
|
|
ReduxActionTypes.FETCH_PAGE_SUCCESS,
|
|
|
|
|
ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
|
|
|
|
|
];
|
|
|
|
|
|
2020-10-21 04:25:32 +00:00
|
|
|
const EVALUATE_REDUX_ACTIONS = [
|
2020-12-30 13:26:44 +00:00
|
|
|
...FIRST_EVAL_REDUX_ACTIONS,
|
2020-10-21 04:25:32 +00:00
|
|
|
// Actions
|
|
|
|
|
ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
|
2021-03-30 05:29:03 +00:00
|
|
|
ReduxActionTypes.FETCH_PLUGIN_FORM_CONFIGS_SUCCESS,
|
2020-10-21 04:25:32 +00:00
|
|
|
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
|
|
|
|
|
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
|
|
|
|
|
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
|
|
|
|
|
ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_SUCCESS,
|
|
|
|
|
ReduxActionTypes.SUBMIT_CURL_FORM_SUCCESS,
|
|
|
|
|
ReduxActionTypes.CREATE_ACTION_SUCCESS,
|
|
|
|
|
ReduxActionTypes.UPDATE_ACTION_PROPERTY,
|
|
|
|
|
ReduxActionTypes.DELETE_ACTION_SUCCESS,
|
|
|
|
|
ReduxActionTypes.COPY_ACTION_SUCCESS,
|
|
|
|
|
ReduxActionTypes.MOVE_ACTION_SUCCESS,
|
|
|
|
|
ReduxActionTypes.RUN_ACTION_SUCCESS,
|
|
|
|
|
ReduxActionErrorTypes.RUN_ACTION_ERROR,
|
|
|
|
|
ReduxActionTypes.EXECUTE_API_ACTION_SUCCESS,
|
|
|
|
|
ReduxActionErrorTypes.EXECUTE_ACTION_ERROR,
|
|
|
|
|
// App Data
|
|
|
|
|
ReduxActionTypes.SET_APP_MODE,
|
|
|
|
|
ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
|
2021-03-24 05:09:47 +00:00
|
|
|
ReduxActionTypes.UPDATE_APP_PERSISTENT_STORE,
|
|
|
|
|
ReduxActionTypes.UPDATE_APP_TRANSIENT_STORE,
|
2020-10-21 04:25:32 +00:00
|
|
|
// Widgets
|
|
|
|
|
ReduxActionTypes.UPDATE_LAYOUT,
|
|
|
|
|
ReduxActionTypes.UPDATE_WIDGET_PROPERTY,
|
|
|
|
|
ReduxActionTypes.UPDATE_WIDGET_NAME_SUCCESS,
|
|
|
|
|
// Widget Meta
|
|
|
|
|
ReduxActionTypes.SET_META_PROP,
|
|
|
|
|
ReduxActionTypes.RESET_WIDGET_META,
|
|
|
|
|
// Batches
|
|
|
|
|
ReduxActionTypes.BATCH_UPDATES_SUCCESS,
|
|
|
|
|
];
|
|
|
|
|
|
2021-01-14 11:15:25 +00:00
|
|
|
const shouldProcessAction = (action: ReduxAction<unknown>) => {
|
|
|
|
|
if (
|
|
|
|
|
action.type === ReduxActionTypes.BATCH_UPDATES_SUCCESS &&
|
|
|
|
|
Array.isArray(action.payload)
|
|
|
|
|
) {
|
|
|
|
|
const batchedActionTypes = action.payload.map(
|
|
|
|
|
(batchedAction) => batchedAction.type,
|
|
|
|
|
);
|
|
|
|
|
return (
|
|
|
|
|
_.intersection(EVALUATE_REDUX_ACTIONS, batchedActionTypes).length > 0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-30 13:26:44 +00:00
|
|
|
function evalQueueBuffer() {
|
2021-01-04 10:16:08 +00:00
|
|
|
let canTake = false;
|
2020-12-30 13:26:44 +00:00
|
|
|
let postEvalActions: any = [];
|
|
|
|
|
const take = () => {
|
2021-01-04 10:16:08 +00:00
|
|
|
if (canTake) {
|
2020-12-30 13:26:44 +00:00
|
|
|
const resp = postEvalActions;
|
|
|
|
|
postEvalActions = [];
|
2021-01-04 10:16:08 +00:00
|
|
|
canTake = false;
|
|
|
|
|
return { postEvalActions: resp, type: "BUFFERED_ACTION" };
|
2020-12-30 13:26:44 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
const flush = () => {
|
2021-01-04 10:16:08 +00:00
|
|
|
if (canTake) {
|
2020-12-30 13:26:44 +00:00
|
|
|
return [take() as Action];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const put = (action: EvaluationReduxAction<unknown | unknown[]>) => {
|
2021-01-14 11:15:25 +00:00
|
|
|
if (!shouldProcessAction(action)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-01-04 10:16:08 +00:00
|
|
|
canTake = true;
|
2021-01-14 11:15:25 +00:00
|
|
|
|
2020-12-30 13:26:44 +00:00
|
|
|
// TODO: If the action is the same as before, we can send only one and ignore duplicates.
|
|
|
|
|
if (action.postEvalActions) {
|
|
|
|
|
postEvalActions.push(...action.postEvalActions);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
take,
|
|
|
|
|
put,
|
|
|
|
|
isEmpty: () => {
|
2021-01-04 10:16:08 +00:00
|
|
|
return !canTake;
|
2020-12-30 13:26:44 +00:00
|
|
|
},
|
|
|
|
|
flush,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function* evaluationChangeListenerSaga() {
|
|
|
|
|
// Explicitly shutdown old worker if present
|
|
|
|
|
yield call(worker.shutdown);
|
|
|
|
|
yield call(worker.start);
|
|
|
|
|
widgetTypeConfigMap = WidgetFactory.getWidgetTypeConfigMap();
|
2021-01-04 10:16:08 +00:00
|
|
|
const initAction = yield take(FIRST_EVAL_REDUX_ACTIONS);
|
|
|
|
|
yield fork(evaluateTreeSaga, initAction.postEvalActions);
|
2020-12-30 13:26:44 +00:00
|
|
|
const evtActionChannel = yield actionChannel(
|
|
|
|
|
EVALUATE_REDUX_ACTIONS,
|
|
|
|
|
evalQueueBuffer(),
|
|
|
|
|
);
|
|
|
|
|
while (true) {
|
|
|
|
|
const action: EvaluationReduxAction<unknown | unknown[]> = yield take(
|
|
|
|
|
evtActionChannel,
|
|
|
|
|
);
|
2021-01-14 11:15:25 +00:00
|
|
|
if (shouldProcessAction(action)) {
|
|
|
|
|
yield call(evaluateTreeSaga, action.postEvalActions);
|
|
|
|
|
}
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|
|
|
|
|
// TODO(hetu) need an action to stop listening and evaluate (exit app)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function* evaluationSagaListeners() {
|
2020-12-30 13:26:44 +00:00
|
|
|
yield take(ReduxActionTypes.START_EVALUATION);
|
|
|
|
|
while (true) {
|
|
|
|
|
try {
|
|
|
|
|
yield call(evaluationChangeListenerSaga);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log.error(e);
|
|
|
|
|
Sentry.captureException(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-21 04:25:32 +00:00
|
|
|
}
|