Improve JS error reporting in the debugger (#4854)
This commit is contained in:
parent
a7a7390d08
commit
179d5ae8a9
|
|
@ -50,12 +50,19 @@ import "codemirror/addon/fold/foldgutter";
|
||||||
import "codemirror/addon/fold/foldgutter.css";
|
import "codemirror/addon/fold/foldgutter.css";
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { removeNewLineChars, getInputValue } from "./codeEditorUtils";
|
import { removeNewLineChars, getInputValue } from "./codeEditorUtils";
|
||||||
|
import { getEntityNameAndPropertyPath } from "workers/evaluationUtils";
|
||||||
|
|
||||||
const LightningMenu = lazy(() =>
|
const LightningMenu = lazy(() =>
|
||||||
retryPromise(() => import("components/editorComponents/LightningMenu")),
|
retryPromise(() => import("components/editorComponents/LightningMenu")),
|
||||||
);
|
);
|
||||||
|
|
||||||
const AUTOCOMPLETE_CLOSE_KEY_CODES = ["Enter", "Tab", "Escape", "Comma"];
|
const AUTOCOMPLETE_CLOSE_KEY_CODES = [
|
||||||
|
"Enter",
|
||||||
|
"Tab",
|
||||||
|
"Escape",
|
||||||
|
"Comma",
|
||||||
|
"Backspace",
|
||||||
|
];
|
||||||
|
|
||||||
interface ReduxStateProps {
|
interface ReduxStateProps {
|
||||||
dynamicData: DataTree;
|
dynamicData: DataTree;
|
||||||
|
|
@ -341,16 +348,26 @@ class CodeEditor extends Component<Props, State> {
|
||||||
if (!dataTreePath) {
|
if (!dataTreePath) {
|
||||||
return { isValid: true, validationMessage: "", jsErrorMessage: "" };
|
return { isValid: true, validationMessage: "", jsErrorMessage: "" };
|
||||||
}
|
}
|
||||||
const isValidPath = dataTreePath.replace("evaluatedValues", "invalidProps");
|
const { entityName, propertyPath } = getEntityNameAndPropertyPath(
|
||||||
const validationMessagePath = dataTreePath.replace(
|
dataTreePath,
|
||||||
|
);
|
||||||
|
let isValidPath, validationMessagePath, jsErrorMessagePath;
|
||||||
|
if (dataTreePath && dataTreePath.match(/evaluatedValues/g)) {
|
||||||
|
isValidPath = dataTreePath.replace("evaluatedValues", "invalidProps");
|
||||||
|
validationMessagePath = dataTreePath.replace(
|
||||||
"evaluatedValues",
|
"evaluatedValues",
|
||||||
"validationMessages",
|
"validationMessages",
|
||||||
);
|
);
|
||||||
const jsErrorMessagePath = dataTreePath.replace(
|
jsErrorMessagePath = dataTreePath.replace(
|
||||||
"evaluatedValues",
|
"evaluatedValues",
|
||||||
"jsErrorMessages",
|
"jsErrorMessages",
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
isValidPath = entityName + "invalidProps" + propertyPath;
|
||||||
|
validationMessagePath =
|
||||||
|
entityName + ".validationMessages." + propertyPath;
|
||||||
|
jsErrorMessagePath = entityName + ".jsErrorMessages." + propertyPath;
|
||||||
|
}
|
||||||
const isValid = !_.get(dataTree, isValidPath, false);
|
const isValid = !_.get(dataTree, isValidPath, false);
|
||||||
const validationMessage = _.get(
|
const validationMessage = _.get(
|
||||||
dataTree,
|
dataTree,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export function InputText(props: {
|
||||||
const dataTreePath = actionPathFromName(actionName, name);
|
const dataTreePath = actionPathFromName(actionName, name);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: "50vh", height: "55px" }}>
|
<div style={{ width: "50vh", minHeight: "55px" }}>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{label} {isRequired && "*"}
|
{label} {isRequired && "*"}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ enum LOG_TYPE {
|
||||||
ACTION_EXECUTION_SUCCESS,
|
ACTION_EXECUTION_SUCCESS,
|
||||||
ENTITY_DELETED,
|
ENTITY_DELETED,
|
||||||
EVAL_ERROR,
|
EVAL_ERROR,
|
||||||
|
ACTION_UPDATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LOG_TYPE;
|
export default LOG_TYPE;
|
||||||
|
|
|
||||||
|
|
@ -45,5 +45,6 @@ export const generateDataTreeAction = (
|
||||||
isLoading: action.isLoading,
|
isLoading: action.isLoading,
|
||||||
bindingPaths: getBindingPathsOfAction(action.config, editorConfig),
|
bindingPaths: getBindingPathsOfAction(action.config, editorConfig),
|
||||||
dependencyMap,
|
dependencyMap,
|
||||||
|
logBlackList: {},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ export interface DataTreeAction
|
||||||
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
||||||
ENTITY_TYPE: ENTITY_TYPE.ACTION;
|
ENTITY_TYPE: ENTITY_TYPE.ACTION;
|
||||||
dependencyMap: DependencyMap;
|
dependencyMap: DependencyMap;
|
||||||
|
jsErrorMessages?: Record<string, string>;
|
||||||
|
logBlackList: Record<string, true>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataTreeWidget extends WidgetProps {
|
export interface DataTreeWidget extends WidgetProps {
|
||||||
|
|
@ -63,6 +65,7 @@ export interface DataTreeWidget extends WidgetProps {
|
||||||
triggerPaths: Record<string, boolean>;
|
triggerPaths: Record<string, boolean>;
|
||||||
validationPaths: Record<string, VALIDATION_TYPES>;
|
validationPaths: Record<string, VALIDATION_TYPES>;
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET;
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET;
|
||||||
|
logBlackList: Record<string, true>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataTreeAppsmith extends Omit<AppDataState, "store"> {
|
export interface DataTreeAppsmith extends Omit<AppDataState, "store"> {
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,10 @@ describe("generateDataTreeWidget", () => {
|
||||||
key: "value",
|
key: "value",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
logBlackList: {
|
||||||
|
isValid: true,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
value: "{{Input1.text}}",
|
value: "{{Input1.text}}",
|
||||||
isDirty: true,
|
isDirty: true,
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ export const generateDataTreeWidget = (
|
||||||
unInitializedDefaultProps[propertyName] = undefined;
|
unInitializedDefaultProps[propertyName] = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const blockedDerivedProps: Record<string, true> = {};
|
||||||
|
Object.keys(derivedProps).forEach((propertyName) => {
|
||||||
|
blockedDerivedProps[propertyName] = true;
|
||||||
|
});
|
||||||
const {
|
const {
|
||||||
bindingPaths,
|
bindingPaths,
|
||||||
triggerPaths,
|
triggerPaths,
|
||||||
|
|
@ -62,6 +66,10 @@ export const generateDataTreeWidget = (
|
||||||
...derivedProps,
|
...derivedProps,
|
||||||
...unInitializedDefaultProps,
|
...unInitializedDefaultProps,
|
||||||
dynamicBindingPathList,
|
dynamicBindingPathList,
|
||||||
|
logBlackList: {
|
||||||
|
...widget.logBlackList,
|
||||||
|
...blockedDerivedProps,
|
||||||
|
},
|
||||||
bindingPaths,
|
bindingPaths,
|
||||||
triggerPaths,
|
triggerPaths,
|
||||||
validationPaths,
|
validationPaths,
|
||||||
|
|
|
||||||
|
|
@ -886,6 +886,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
widgets: { [widgetId: string]: FlattenedWidgetProps },
|
widgets: { [widgetId: string]: FlattenedWidgetProps },
|
||||||
) => {
|
) => {
|
||||||
let template = {};
|
let template = {};
|
||||||
|
const logBlackListMap: any = {};
|
||||||
const container = get(
|
const container = get(
|
||||||
widgets,
|
widgets,
|
||||||
`${get(widget, "children.0.children.0")}`,
|
`${get(widget, "children.0.children.0")}`,
|
||||||
|
|
@ -901,6 +902,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
canvas.children &&
|
canvas.children &&
|
||||||
get(canvas, "children", []).forEach((child: string) => {
|
get(canvas, "children", []).forEach((child: string) => {
|
||||||
const childWidget = cloneDeep(get(widgets, `${child}`));
|
const childWidget = cloneDeep(get(widgets, `${child}`));
|
||||||
|
const logBlackList: { [key: string]: boolean } = {};
|
||||||
const keys = Object.keys(childWidget);
|
const keys = Object.keys(childWidget);
|
||||||
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
|
@ -927,6 +929,12 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.keys(childWidget).map((key) => {
|
||||||
|
logBlackList[key] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
logBlackListMap[childWidget.widgetId] = logBlackList;
|
||||||
|
|
||||||
template = {
|
template = {
|
||||||
...template,
|
...template,
|
||||||
[childWidget.widgetName]: childWidget,
|
[childWidget.widgetName]: childWidget,
|
||||||
|
|
@ -945,6 +953,18 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
propertyValue: template,
|
propertyValue: template,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// add logBlackList to updateProperyMap for all children
|
||||||
|
updatePropertyMap = updatePropertyMap.concat(
|
||||||
|
Object.keys(logBlackListMap).map((logBlackListMapKey) => {
|
||||||
|
return {
|
||||||
|
widgetId: logBlackListMapKey,
|
||||||
|
propertyName: "logBlackList",
|
||||||
|
propertyValue: logBlackListMap[logBlackListMapKey],
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return updatePropertyMap;
|
return updatePropertyMap;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -961,6 +981,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
if (!parentId) return { widgets };
|
if (!parentId) return { widgets };
|
||||||
const widget = { ...widgets[widgetId] };
|
const widget = { ...widgets[widgetId] };
|
||||||
const parent = { ...widgets[parentId] };
|
const parent = { ...widgets[parentId] };
|
||||||
|
const logBlackList: { [key: string]: boolean } = {};
|
||||||
|
|
||||||
const disallowedWidgets = [WidgetTypes.FILE_PICKER_WIDGET];
|
const disallowedWidgets = [WidgetTypes.FILE_PICKER_WIDGET];
|
||||||
|
|
||||||
|
|
@ -998,7 +1019,15 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
|
|
||||||
parent.template = template;
|
parent.template = template;
|
||||||
|
|
||||||
|
// add logBlackList for the children being added
|
||||||
|
Object.keys(widget).map((key) => {
|
||||||
|
logBlackList[key] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
widget.logBlackList = logBlackList;
|
||||||
|
|
||||||
widgets[parentId] = parent;
|
widgets[parentId] = parent;
|
||||||
|
widgets[widgetId] = widget;
|
||||||
|
|
||||||
return { widgets };
|
return { widgets };
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -624,15 +624,21 @@ function* setActionPropertySaga(action: ReduxAction<SetActionPropertyPayload>) {
|
||||||
if (propertyName === "name") return;
|
if (propertyName === "name") return;
|
||||||
|
|
||||||
const actionObj = yield select(getAction, actionId);
|
const actionObj = yield select(getAction, actionId);
|
||||||
|
const fieldToBeUpdated = propertyName.replace(
|
||||||
|
"actionConfiguration",
|
||||||
|
"config",
|
||||||
|
);
|
||||||
AppsmithConsole.info({
|
AppsmithConsole.info({
|
||||||
|
logType: LOG_TYPE.ACTION_UPDATE,
|
||||||
text: "Configuration updated",
|
text: "Configuration updated",
|
||||||
source: {
|
source: {
|
||||||
type: ENTITY_TYPE.ACTION,
|
type: ENTITY_TYPE.ACTION,
|
||||||
name: actionObj.name,
|
name: actionObj.name,
|
||||||
id: actionId,
|
id: actionId,
|
||||||
|
propertyPath: fieldToBeUpdated,
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
[propertyName]: value,
|
[fieldToBeUpdated]: value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,12 @@
|
||||||
import { debuggerLog, errorLog, updateErrorLog } from "actions/debuggerActions";
|
import { debuggerLog, errorLog, updateErrorLog } from "actions/debuggerActions";
|
||||||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import { WidgetTypes } from "constants/WidgetConstants";
|
|
||||||
import { LogActionPayload, Message } from "entities/AppsmithConsole";
|
import { LogActionPayload, Message } from "entities/AppsmithConsole";
|
||||||
import {
|
import { all, call, fork, put, select, takeEvery } from "redux-saga/effects";
|
||||||
all,
|
import { set } from "lodash";
|
||||||
put,
|
|
||||||
takeEvery,
|
|
||||||
select,
|
|
||||||
take,
|
|
||||||
fork,
|
|
||||||
call,
|
|
||||||
} from "redux-saga/effects";
|
|
||||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
|
||||||
import { isEmpty, set, get } from "lodash";
|
|
||||||
import { getDebuggerErrors } from "selectors/debuggerSelectors";
|
import { getDebuggerErrors } from "selectors/debuggerSelectors";
|
||||||
import { getAction } from "selectors/entitiesSelector";
|
import { getAction } from "selectors/entitiesSelector";
|
||||||
import { Action, PluginType } from "entities/Action";
|
import { Action, PluginType } from "entities/Action";
|
||||||
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
||||||
import { DataTree, DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
|
||||||
import { isWidget } from "workers/evaluationUtils";
|
|
||||||
import { getWidget } from "./selectors";
|
|
||||||
import { WidgetProps } from "widgets/BaseWidget";
|
|
||||||
|
|
||||||
function* onWidgetUpdateSaga(payload: LogActionPayload) {
|
|
||||||
if (!payload.source) return;
|
|
||||||
// Wait for data tree update
|
|
||||||
yield take(ReduxActionTypes.SET_EVALUATED_TREE);
|
|
||||||
const dataTree: DataTree = yield select(getDataTree);
|
|
||||||
const widget = dataTree[payload.source.name];
|
|
||||||
|
|
||||||
if (
|
|
||||||
!isWidget(widget) ||
|
|
||||||
!widget.validationMessages ||
|
|
||||||
!widget.jsErrorMessages
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Ignore canvas widget updates
|
|
||||||
if (widget.type === WidgetTypes.CANVAS_WIDGET) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const source = payload.source;
|
|
||||||
|
|
||||||
// If widget properties no longer have validation errors update the same
|
|
||||||
if (payload.state) {
|
|
||||||
const propertyPath = Object.keys(payload.state)[0];
|
|
||||||
|
|
||||||
const validationMessages = widget.validationMessages;
|
|
||||||
const validationMessage = validationMessages[propertyPath];
|
|
||||||
const jsErrorMessages = widget.jsErrorMessages;
|
|
||||||
const jsErrorMessage = jsErrorMessages[propertyPath];
|
|
||||||
const errors = yield select(getDebuggerErrors);
|
|
||||||
const errorId = `${source.id}-${propertyPath}`;
|
|
||||||
const widgetErrorLog = errors[errorId];
|
|
||||||
if (!widgetErrorLog) return;
|
|
||||||
|
|
||||||
const noError = isEmpty(validationMessage);
|
|
||||||
const noJsError = isEmpty(jsErrorMessage);
|
|
||||||
|
|
||||||
if (noError && noJsError) {
|
|
||||||
delete errors[errorId];
|
|
||||||
|
|
||||||
yield put({
|
|
||||||
type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOGS,
|
|
||||||
payload: errors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function* formatActionRequestSaga(payload: LogActionPayload, request?: any) {
|
function* formatActionRequestSaga(payload: LogActionPayload, request?: any) {
|
||||||
if (!payload.source || !payload.state || !request || !request.headers) {
|
if (!payload.source || !payload.state || !request || !request.headers) {
|
||||||
|
|
@ -128,7 +67,9 @@ function* debuggerLogSaga(action: ReduxAction<Message>) {
|
||||||
|
|
||||||
switch (payload.logType) {
|
switch (payload.logType) {
|
||||||
case LOG_TYPE.WIDGET_UPDATE:
|
case LOG_TYPE.WIDGET_UPDATE:
|
||||||
yield call(onWidgetUpdateSaga, payload);
|
yield put(debuggerLog(payload));
|
||||||
|
return;
|
||||||
|
case LOG_TYPE.ACTION_UPDATE:
|
||||||
yield put(debuggerLog(payload));
|
yield put(debuggerLog(payload));
|
||||||
return;
|
return;
|
||||||
case LOG_TYPE.EVAL_ERROR:
|
case LOG_TYPE.EVAL_ERROR:
|
||||||
|
|
@ -178,42 +119,6 @@ function* debuggerLogSaga(action: ReduxAction<Message>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass through error list once after on page load actions executions are complete
|
|
||||||
function* onExecutePageActionsCompleteSaga() {
|
|
||||||
yield take(ReduxActionTypes.SET_EVALUATED_TREE);
|
|
||||||
|
|
||||||
const dataTree: DataTree = yield select(getDataTree);
|
|
||||||
const errors = yield select(getDebuggerErrors);
|
|
||||||
const updatedErrors = { ...errors };
|
|
||||||
const errorIds = Object.keys(errors);
|
|
||||||
|
|
||||||
for (const id of errorIds) {
|
|
||||||
const splits = id.split("-");
|
|
||||||
const entityId = splits[0];
|
|
||||||
const propertyName = splits[1];
|
|
||||||
const widget: WidgetProps | null = yield select(getWidget, entityId);
|
|
||||||
|
|
||||||
if (widget) {
|
|
||||||
const dataTreeWidget = dataTree[widget.widgetName] as DataTreeWidget;
|
|
||||||
|
|
||||||
if (!get(dataTreeWidget.invalidProps, propertyName, null)) {
|
|
||||||
delete updatedErrors[id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yield put({
|
|
||||||
type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOGS,
|
|
||||||
payload: updatedErrors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function* debuggerSagasListeners() {
|
export default function* debuggerSagasListeners() {
|
||||||
yield all([
|
yield all([takeEvery(ReduxActionTypes.DEBUGGER_LOG_INIT, debuggerLogSaga)]);
|
||||||
takeEvery(ReduxActionTypes.DEBUGGER_LOG_INIT, debuggerLogSaga),
|
|
||||||
takeEvery(
|
|
||||||
ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS_COMPLETE,
|
|
||||||
onExecutePageActionsCompleteSaga,
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,29 +28,127 @@ import { WidgetProps } from "widgets/BaseWidget";
|
||||||
import PerformanceTracker, {
|
import PerformanceTracker, {
|
||||||
PerformanceTransactionName,
|
PerformanceTransactionName,
|
||||||
} from "../utils/PerformanceTracker";
|
} from "../utils/PerformanceTracker";
|
||||||
import { Variant } from "components/ads/common";
|
|
||||||
import { Toaster } from "components/ads/Toast";
|
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { Action } from "redux";
|
import { Action } from "redux";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { ENTITY_TYPE, Message, Severity } from "entities/AppsmithConsole";
|
||||||
|
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";
|
||||||
import {
|
import {
|
||||||
createMessage,
|
createMessage,
|
||||||
ERROR_EVAL_ERROR_GENERIC,
|
ERROR_EVAL_ERROR_GENERIC,
|
||||||
ERROR_EVAL_TRIGGER,
|
ERROR_EVAL_TRIGGER,
|
||||||
} from "constants/messages";
|
} from "constants/messages";
|
||||||
import AppsmithConsole from "utils/AppsmithConsole";
|
|
||||||
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
|
||||||
|
|
||||||
let widgetTypeConfigMap: WidgetTypeConfigMap;
|
let widgetTypeConfigMap: WidgetTypeConfigMap;
|
||||||
|
|
||||||
const worker = new GracefulWorkerService(Worker);
|
const worker = new GracefulWorkerService(Worker);
|
||||||
|
|
||||||
const evalErrorHandler = (errors: EvalError[]) => {
|
const getDebuggerErrors = (state: AppState) => state.ui.debugger.errors;
|
||||||
if (!errors) return;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
const jsError = _.get(entity, `jsErrorMessages.${propertyPath}`, "");
|
||||||
|
const validationError = _.get(
|
||||||
|
entity,
|
||||||
|
`validationMessages.${propertyPath}`,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
const evaluatedValue = _.get(
|
||||||
|
entity,
|
||||||
|
`evaluatedValues.${propertyPath}`,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
const error = jsError || validationError;
|
||||||
|
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
|
||||||
|
|
||||||
|
if (_.isString(error) && error !== "") {
|
||||||
|
// Add or update
|
||||||
|
updatedDebuggerErrors[debuggerKey] = {
|
||||||
|
logType: LOG_TYPE.EVAL_ERROR,
|
||||||
|
text: `The value at ${propertyPath} is invalid`,
|
||||||
|
message: error,
|
||||||
|
severity: Severity.ERROR,
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
errors.forEach((error) => {
|
errors.forEach((error) => {
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
case EvalErrorTypes.DEPENDENCY_ERROR: {
|
case EvalErrorTypes.CYCLICAL_DEPENDENCY_ERROR: {
|
||||||
if (error.context) {
|
if (error.context) {
|
||||||
// Add more info about node for the toast
|
// Add more info about node for the toast
|
||||||
const { entityType, node } = error.context;
|
const { entityType, node } = error.context;
|
||||||
|
|
@ -104,33 +202,13 @@ const evalErrorHandler = (errors: EvalError[]) => {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EvalErrorTypes.EVAL_ERROR: {
|
|
||||||
log.debug(error);
|
|
||||||
AppsmithConsole.error({
|
|
||||||
logType: LOG_TYPE.EVAL_ERROR,
|
|
||||||
text: `The value at ${error.context?.source.propertyPath} is invalid`,
|
|
||||||
message: error.message,
|
|
||||||
source: error.context?.source,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EvalErrorTypes.WIDGET_PROPERTY_VALIDATION_ERROR: {
|
|
||||||
AppsmithConsole.error({
|
|
||||||
logType: LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR,
|
|
||||||
text: `The value at ${error.context?.source.propertyPath} is invalid`,
|
|
||||||
message: error.message,
|
|
||||||
source: error.context?.source,
|
|
||||||
state: error.context?.state,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
Sentry.captureException(error);
|
Sentry.captureException(error);
|
||||||
log.debug(error);
|
log.debug(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function* postEvalActionDispatcher(
|
function* postEvalActionDispatcher(
|
||||||
actions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
|
actions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
|
||||||
|
|
@ -156,10 +234,16 @@ function* evaluateTreeSaga(
|
||||||
widgetTypeConfigMap,
|
widgetTypeConfigMap,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const { dataTree, dependencies, errors, logs } = workerResponse;
|
const {
|
||||||
|
dataTree,
|
||||||
|
dependencies,
|
||||||
|
errors,
|
||||||
|
evaluationOrder,
|
||||||
|
logs,
|
||||||
|
} = workerResponse;
|
||||||
log.debug({ dataTree: dataTree });
|
log.debug({ dataTree: dataTree });
|
||||||
logs.forEach((evalLog: any) => log.debug(evalLog));
|
logs.forEach((evalLog: any) => log.debug(evalLog));
|
||||||
evalErrorHandler(errors);
|
yield call(evalErrorHandler, errors, dataTree, evaluationOrder);
|
||||||
PerformanceTracker.stopAsyncTracking(
|
PerformanceTracker.stopAsyncTracking(
|
||||||
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
||||||
);
|
);
|
||||||
|
|
@ -197,7 +281,7 @@ export function* evaluateActionBindings(
|
||||||
|
|
||||||
const { errors, values } = workerResponse;
|
const { errors, values } = workerResponse;
|
||||||
|
|
||||||
evalErrorHandler(errors);
|
yield call(evalErrorHandler, errors);
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -214,7 +298,7 @@ export function* evaluateDynamicTrigger(
|
||||||
);
|
);
|
||||||
|
|
||||||
const { errors, triggers } = workerResponse;
|
const { errors, triggers } = workerResponse;
|
||||||
evalErrorHandler(errors);
|
yield call(evalErrorHandler, errors);
|
||||||
return triggers;
|
return triggers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,7 +386,6 @@ const EVALUATE_REDUX_ACTIONS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const shouldProcessAction = (action: ReduxAction<unknown>) => {
|
const shouldProcessAction = (action: ReduxAction<unknown>) => {
|
||||||
// debugger;
|
|
||||||
if (
|
if (
|
||||||
action.type === ReduxActionTypes.BATCH_UPDATES_SUCCESS &&
|
action.type === ReduxActionTypes.BATCH_UPDATES_SUCCESS &&
|
||||||
Array.isArray(action.payload)
|
Array.isArray(action.payload)
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,7 @@ const createLoadingWidget = (
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
triggerPaths: {},
|
triggerPaths: {},
|
||||||
validationPaths: {},
|
validationPaths: {},
|
||||||
|
logBlackList: {},
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -84,12 +84,10 @@ export const getDynamicBindings = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum EvalErrorTypes {
|
export enum EvalErrorTypes {
|
||||||
DEPENDENCY_ERROR = "DEPENDENCY_ERROR",
|
CYCLICAL_DEPENDENCY_ERROR = "CYCLICAL_DEPENDENCY_ERROR",
|
||||||
EVAL_PROPERTY_ERROR = "EVAL_PROPERTY_ERROR",
|
EVAL_PROPERTY_ERROR = "EVAL_PROPERTY_ERROR",
|
||||||
WIDGET_PROPERTY_VALIDATION_ERROR = "WIDGET_PROPERTY_VALIDATION_ERROR",
|
WIDGET_PROPERTY_VALIDATION_ERROR = "WIDGET_PROPERTY_VALIDATION_ERROR",
|
||||||
EVAL_TREE_ERROR = "EVAL_TREE_ERROR",
|
EVAL_TREE_ERROR = "EVAL_TREE_ERROR",
|
||||||
UNESCAPE_STRING_ERROR = "UNESCAPE_STRING_ERROR",
|
|
||||||
EVAL_ERROR = "EVAL_ERROR",
|
|
||||||
UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
||||||
BAD_UNEVAL_TREE_ERROR = "BAD_UNEVAL_TREE_ERROR",
|
BAD_UNEVAL_TREE_ERROR = "BAD_UNEVAL_TREE_ERROR",
|
||||||
EVAL_TRIGGER_ERROR = "EVAL_TRIGGER_ERROR",
|
EVAL_TRIGGER_ERROR = "EVAL_TRIGGER_ERROR",
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import defaultTemplate from "templates/default";
|
||||||
import { generateReactKey } from "./generators";
|
import { generateReactKey } from "./generators";
|
||||||
import { ChartDataPoint } from "widgets/ChartWidget";
|
import { ChartDataPoint } from "widgets/ChartWidget";
|
||||||
import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
|
import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||||
import { has, isString, omit, set } from "lodash";
|
import { get, has, isString, omit, set } from "lodash";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import {
|
import {
|
||||||
migrateTablePrimaryColumnsBindings,
|
migrateTablePrimaryColumnsBindings,
|
||||||
|
|
@ -764,6 +764,11 @@ const transformDSL = (currentDSL: ContainerWidgetProps<WidgetProps>) => {
|
||||||
currentDSL.version = LATEST_PAGE_VERSION;
|
currentDSL.version = LATEST_PAGE_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentDSL.version === 22) {
|
||||||
|
currentDSL = addLogBlackListToAllListWidgetChildren(currentDSL);
|
||||||
|
currentDSL.version = 23;
|
||||||
|
}
|
||||||
|
|
||||||
return currentDSL;
|
return currentDSL;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1164,3 +1169,41 @@ export const generateWidgetProps = (
|
||||||
} else throw Error("Failed to create widget: Parent was not provided ");
|
} else throw Error("Failed to create widget: Parent was not provided ");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds logBlackList key for all list widget children
|
||||||
|
*
|
||||||
|
* @param currentDSL
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const addLogBlackListToAllListWidgetChildren = (
|
||||||
|
currentDSL: ContainerWidgetProps<WidgetProps>,
|
||||||
|
) => {
|
||||||
|
currentDSL.children = currentDSL.children?.map((children: WidgetProps) => {
|
||||||
|
if (children.type === WidgetTypes.LIST_WIDGET) {
|
||||||
|
const widgets = get(
|
||||||
|
children,
|
||||||
|
"children.0.children.0.children.0.children",
|
||||||
|
);
|
||||||
|
|
||||||
|
widgets.map((widget: any, index: number) => {
|
||||||
|
const logBlackList: { [key: string]: boolean } = {};
|
||||||
|
|
||||||
|
Object.keys(widget).map((key) => {
|
||||||
|
logBlackList[key] = true;
|
||||||
|
});
|
||||||
|
if (!widget.logBlackList) {
|
||||||
|
set(
|
||||||
|
children,
|
||||||
|
`children.0.children.0.children.0.children.${index}.logBlackList`,
|
||||||
|
logBlackList,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentDSL;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,10 @@ export default class DataTreeEvaluator {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
this.logs.push({ timeTakenForFirstTree });
|
this.logs.push({ timeTakenForFirstTree });
|
||||||
|
return {
|
||||||
|
dataTree: this.evalTree,
|
||||||
|
evaluationOrder: this.sortedDependencies,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
isDynamicLeaf(unEvalTree: DataTree, propertyPath: string) {
|
isDynamicLeaf(unEvalTree: DataTree, propertyPath: string) {
|
||||||
|
|
@ -206,7 +210,10 @@ export default class DataTreeEvaluator {
|
||||||
evaluate: (evalStop - evalStart).toFixed(2),
|
evaluate: (evalStop - evalStart).toFixed(2),
|
||||||
};
|
};
|
||||||
this.logs.push({ timeTakenForSubTreeEval });
|
this.logs.push({ timeTakenForSubTreeEval });
|
||||||
return this.evalTree;
|
return {
|
||||||
|
dataTree: this.evalTree,
|
||||||
|
evaluationOrder: evaluationOrder,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompleteSortOrder(
|
getCompleteSortOrder(
|
||||||
|
|
@ -502,7 +509,7 @@ export default class DataTreeEvaluator {
|
||||||
entityType = entity.pluginType;
|
entityType = entity.pluginType;
|
||||||
}
|
}
|
||||||
this.errors.push({
|
this.errors.push({
|
||||||
type: EvalErrorTypes.DEPENDENCY_ERROR,
|
type: EvalErrorTypes.CYCLICAL_DEPENDENCY_ERROR,
|
||||||
message: "Cyclic dependency found while evaluating.",
|
message: "Cyclic dependency found while evaluating.",
|
||||||
context: {
|
context: {
|
||||||
node,
|
node,
|
||||||
|
|
@ -612,22 +619,14 @@ export default class DataTreeEvaluator {
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
);
|
);
|
||||||
_.set(data, `${entityName}.jsErrorMessages.${propertyPath}`, e.message);
|
_.set(data, `${entityName}.jsErrorMessages.${propertyPath}`, e.message);
|
||||||
const entity = data[entityName];
|
} else {
|
||||||
if (isWidget(entity)) {
|
// TODO clean up
|
||||||
|
// This is to handle situations with evaluation of triggers for execution
|
||||||
this.errors.push({
|
this.errors.push({
|
||||||
type: EvalErrorTypes.EVAL_ERROR,
|
type: EvalErrorTypes.EVAL_PROPERTY_ERROR,
|
||||||
message: e.message,
|
message: e.message,
|
||||||
context: {
|
|
||||||
source: {
|
|
||||||
id: entity.widgetId,
|
|
||||||
name: entity.widgetName,
|
|
||||||
type: ENTITY_TYPE.WIDGET,
|
|
||||||
propertyPath: propertyPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return { result: undefined, triggers: [] };
|
return { result: undefined, triggers: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -668,23 +667,7 @@ export default class DataTreeEvaluator {
|
||||||
: transformed;
|
: transformed;
|
||||||
const safeEvaluatedValue = removeFunctions(evaluatedValue);
|
const safeEvaluatedValue = removeFunctions(evaluatedValue);
|
||||||
_.set(widget, `evaluatedValues.${propertyPath}`, safeEvaluatedValue);
|
_.set(widget, `evaluatedValues.${propertyPath}`, safeEvaluatedValue);
|
||||||
const jsError = _.get(widget, `jsErrorMessages.${propertyPath}`);
|
if (!isValid) {
|
||||||
if (!isValid && !jsError) {
|
|
||||||
this.errors.push({
|
|
||||||
type: EvalErrorTypes.WIDGET_PROPERTY_VALIDATION_ERROR,
|
|
||||||
message: message || "",
|
|
||||||
context: {
|
|
||||||
source: {
|
|
||||||
id: widget.widgetId,
|
|
||||||
name: widget.widgetName,
|
|
||||||
type: ENTITY_TYPE.WIDGET,
|
|
||||||
propertyPath: propertyPath,
|
|
||||||
},
|
|
||||||
state: {
|
|
||||||
value: safeEvaluatedValue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
_.set(widget, `invalidProps.${propertyPath}`, true);
|
_.set(widget, `invalidProps.${propertyPath}`, true);
|
||||||
_.set(widget, `validationMessages.${propertyPath}`, message);
|
_.set(widget, `validationMessages.${propertyPath}`, message);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -384,9 +384,9 @@ describe("DataTreeEvaluator", () => {
|
||||||
text: "Hey there",
|
text: "Hey there",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
const { dataTree } = evaluator.updateDataTree(updatedUnEvalTree);
|
||||||
expect(updatedEvalTree).toHaveProperty("Text2.text", "Hey there");
|
expect(dataTree).toHaveProperty("Text2.text", "Hey there");
|
||||||
expect(updatedEvalTree).toHaveProperty("Text3.text", "Hey there");
|
expect(dataTree).toHaveProperty("Text3.text", "Hey there");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Evaluates a dependency change in update run", () => {
|
it("Evaluates a dependency change in update run", () => {
|
||||||
|
|
@ -397,10 +397,10 @@ describe("DataTreeEvaluator", () => {
|
||||||
text: "Label 3",
|
text: "Label 3",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
const { dataTree } = evaluator.updateDataTree(updatedUnEvalTree);
|
||||||
const updatedDependencyMap = evaluator.dependencyMap;
|
const updatedDependencyMap = evaluator.dependencyMap;
|
||||||
expect(updatedEvalTree).toHaveProperty("Text2.text", "Label");
|
expect(dataTree).toHaveProperty("Text2.text", "Label");
|
||||||
expect(updatedEvalTree).toHaveProperty("Text3.text", "Label 3");
|
expect(dataTree).toHaveProperty("Text3.text", "Label 3");
|
||||||
expect(updatedDependencyMap).toStrictEqual({
|
expect(updatedDependencyMap).toStrictEqual({
|
||||||
Text1: ["Text1.text"],
|
Text1: ["Text1.text"],
|
||||||
Text2: ["Text2.text"],
|
Text2: ["Text2.text"],
|
||||||
|
|
@ -445,8 +445,8 @@ describe("DataTreeEvaluator", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
const { dataTree } = evaluator.updateDataTree(updatedUnEvalTree);
|
||||||
expect(updatedEvalTree).toHaveProperty("Input1.text", "Default value");
|
expect(dataTree).toHaveProperty("Input1.text", "Default value");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Evaluates for value changes in nested diff paths", () => {
|
it("Evaluates for value changes in nested diff paths", () => {
|
||||||
|
|
@ -481,11 +481,8 @@ describe("DataTreeEvaluator", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
const { dataTree } = evaluator.updateDataTree(updatedUnEvalTree);
|
||||||
expect(updatedEvalTree).toHaveProperty(
|
expect(dataTree).toHaveProperty("Dropdown2.options.0.label", "newValue");
|
||||||
"Dropdown2.options.0.label",
|
|
||||||
"newValue",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Adds an entity with a complicated binding", () => {
|
it("Adds an entity with a complicated binding", () => {
|
||||||
|
|
@ -504,9 +501,9 @@ describe("DataTreeEvaluator", () => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
const { dataTree } = evaluator.updateDataTree(updatedUnEvalTree);
|
||||||
const updatedDependencyMap = evaluator.dependencyMap;
|
const updatedDependencyMap = evaluator.dependencyMap;
|
||||||
expect(updatedEvalTree).toHaveProperty("Table1.tableData", [
|
expect(dataTree).toHaveProperty("Table1.tableData", [
|
||||||
{
|
{
|
||||||
test: "Hey",
|
test: "Hey",
|
||||||
raw: "Label",
|
raw: "Label",
|
||||||
|
|
@ -568,9 +565,9 @@ describe("DataTreeEvaluator", () => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
const { dataTree } = evaluator.updateDataTree(updatedUnEvalTree);
|
||||||
const updatedDependencyMap = evaluator.dependencyMap;
|
const updatedDependencyMap = evaluator.dependencyMap;
|
||||||
expect(updatedEvalTree).toHaveProperty("Table1.tableData", [
|
expect(dataTree).toHaveProperty("Table1.tableData", [
|
||||||
{
|
{
|
||||||
test: "Hey",
|
test: "Hey",
|
||||||
raw: "Label",
|
raw: "Label",
|
||||||
|
|
@ -580,7 +577,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
raw: "Label",
|
raw: "Label",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(updatedEvalTree).toHaveProperty("Text4.text", "Hey");
|
expect(dataTree).toHaveProperty("Text4.text", "Hey");
|
||||||
expect(updatedDependencyMap).toStrictEqual({
|
expect(updatedDependencyMap).toStrictEqual({
|
||||||
Api1: ["Api1.data"],
|
Api1: ["Api1.data"],
|
||||||
Text1: ["Text1.text"],
|
Text1: ["Text1.text"],
|
||||||
|
|
@ -657,14 +654,14 @@ describe("DataTreeEvaluator", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const evaluatedDataTree2 = evaluator.updateDataTree(updatedTree2);
|
const { dataTree } = evaluator.updateDataTree(updatedTree2);
|
||||||
expect(evaluator.dependencyMap["Api2.config.body"]).toStrictEqual([
|
expect(evaluator.dependencyMap["Api2.config.body"]).toStrictEqual([
|
||||||
"Text1.text",
|
"Text1.text",
|
||||||
"Api2.config.pluginSpecifiedTemplates[0].value",
|
"Api2.config.pluginSpecifiedTemplates[0].value",
|
||||||
]);
|
]);
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(evaluatedDataTree2.Api2.config.body).toBe("{ 'name': Test }");
|
expect(dataTree.Api2.config.body).toBe("{ 'name': Test }");
|
||||||
const updatedTree3 = {
|
const updatedTree3 = {
|
||||||
...updatedTree2,
|
...updatedTree2,
|
||||||
Api2: {
|
Api2: {
|
||||||
|
|
@ -683,13 +680,14 @@ describe("DataTreeEvaluator", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const evaluatedDataTree3 = evaluator.updateDataTree(updatedTree3);
|
const evaluatedDataTreeObject = evaluator.updateDataTree(updatedTree3);
|
||||||
|
const dataTree3 = evaluatedDataTreeObject.dataTree;
|
||||||
expect(evaluator.dependencyMap["Api2.config.body"]).toStrictEqual([
|
expect(evaluator.dependencyMap["Api2.config.body"]).toStrictEqual([
|
||||||
"Text1.text",
|
"Text1.text",
|
||||||
"Api2.config.pluginSpecifiedTemplates[0].value",
|
"Api2.config.pluginSpecifiedTemplates[0].value",
|
||||||
]);
|
]);
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(evaluatedDataTree3.Api2.config.body).toBe("{ 'name': \"Test\" }");
|
expect(dataTree3.Api2.config.body).toBe("{ 'name': \"Test\" }");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,18 +47,24 @@ ctx.addEventListener(
|
||||||
let errors: EvalError[] = [];
|
let errors: EvalError[] = [];
|
||||||
let logs: any[] = [];
|
let logs: any[] = [];
|
||||||
let dependencies: DependencyMap = {};
|
let dependencies: DependencyMap = {};
|
||||||
|
let dataTreeObject: any = {};
|
||||||
|
let evaluationOrder: Array<string> = [];
|
||||||
try {
|
try {
|
||||||
if (!dataTreeEvaluator) {
|
if (!dataTreeEvaluator) {
|
||||||
dataTreeEvaluator = new DataTreeEvaluator(widgetTypeConfigMap);
|
dataTreeEvaluator = new DataTreeEvaluator(widgetTypeConfigMap);
|
||||||
dataTreeEvaluator.createFirstTree(unevalTree);
|
dataTreeObject = dataTreeEvaluator.createFirstTree(unevalTree);
|
||||||
dataTree = dataTreeEvaluator.evalTree;
|
dataTree = dataTreeObject.dataTree;
|
||||||
|
evaluationOrder = dataTreeObject.evaluationOrder;
|
||||||
|
// dataTreeEvaluator.sortedDepedencies
|
||||||
} else {
|
} else {
|
||||||
dataTree = dataTreeEvaluator.updateDataTree(unevalTree);
|
dataTreeObject = dataTreeEvaluator.updateDataTree(unevalTree);
|
||||||
|
dataTree = dataTreeObject.dataTree;
|
||||||
|
evaluationOrder = dataTreeObject.evaluationOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to clean it to remove any possible functions inside the tree.
|
// We need to clean it to remove any possible functions inside the tree.
|
||||||
// If functions exist, it will crash the web worker
|
// If functions exist, it will crash the web worker
|
||||||
dataTree = JSON.parse(JSON.stringify(dataTree));
|
dataTree = dataTree && JSON.parse(JSON.stringify(dataTree));
|
||||||
dependencies = dataTreeEvaluator.inverseDependencyMap;
|
dependencies = dataTreeEvaluator.inverseDependencyMap;
|
||||||
errors = dataTreeEvaluator.errors;
|
errors = dataTreeEvaluator.errors;
|
||||||
dataTreeEvaluator.clearErrors();
|
dataTreeEvaluator.clearErrors();
|
||||||
|
|
@ -79,10 +85,12 @@ ctx.addEventListener(
|
||||||
dataTree = getSafeToRenderDataTree(unevalTree, widgetTypeConfigMap);
|
dataTree = getSafeToRenderDataTree(unevalTree, widgetTypeConfigMap);
|
||||||
dataTreeEvaluator = undefined;
|
dataTreeEvaluator = undefined;
|
||||||
}
|
}
|
||||||
|
// step 6: eval order
|
||||||
return {
|
return {
|
||||||
dataTree,
|
dataTree,
|
||||||
dependencies,
|
dependencies,
|
||||||
errors,
|
errors,
|
||||||
|
evaluationOrder,
|
||||||
logs,
|
logs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +116,8 @@ ctx.addEventListener(
|
||||||
if (!dataTreeEvaluator) {
|
if (!dataTreeEvaluator) {
|
||||||
return { triggers: [], errors: [] };
|
return { triggers: [], errors: [] };
|
||||||
}
|
}
|
||||||
const evalTree = dataTreeEvaluator.updateDataTree(dataTree);
|
dataTreeEvaluator.updateDataTree(dataTree);
|
||||||
|
const evalTree = dataTreeEvaluator.evalTree;
|
||||||
const triggers = dataTreeEvaluator.getDynamicValue(
|
const triggers = dataTreeEvaluator.getDynamicValue(
|
||||||
dynamicTrigger,
|
dynamicTrigger,
|
||||||
evalTree,
|
evalTree,
|
||||||
|
|
@ -120,7 +129,7 @@ ctx.addEventListener(
|
||||||
// Transforming eval errors into eval trigger errors. Since trigger
|
// Transforming eval errors into eval trigger errors. Since trigger
|
||||||
// errors occur less, we want to treat it separately
|
// errors occur less, we want to treat it separately
|
||||||
const errors = dataTreeEvaluator.errors.map((error) => {
|
const errors = dataTreeEvaluator.errors.map((error) => {
|
||||||
if (error.type === EvalErrorTypes.EVAL_ERROR) {
|
if (error.type === EvalErrorTypes.EVAL_PROPERTY_ERROR) {
|
||||||
return {
|
return {
|
||||||
...error,
|
...error,
|
||||||
type: EvalErrorTypes.EVAL_TRIGGER_ERROR,
|
type: EvalErrorTypes.EVAL_TRIGGER_ERROR,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user