2021-07-08 05:31:08 +00:00
|
|
|
import {
|
2022-12-07 10:28:29 +00:00
|
|
|
addErrorLogs,
|
2021-07-08 05:31:08 +00:00
|
|
|
debuggerLog,
|
2021-08-16 11:03:27 +00:00
|
|
|
debuggerLogInit,
|
|
|
|
|
deleteErrorLog,
|
2021-07-08 05:31:08 +00:00
|
|
|
LogDebuggerErrorAnalyticsPayload,
|
|
|
|
|
} from "actions/debuggerActions";
|
2022-04-12 10:50:01 +00:00
|
|
|
import {
|
|
|
|
|
ReduxAction,
|
|
|
|
|
ReduxActionTypes,
|
|
|
|
|
} from "@appsmith/constants/ReduxActionConstants";
|
2022-09-16 19:18:54 +00:00
|
|
|
import {
|
|
|
|
|
ENTITY_TYPE,
|
|
|
|
|
Log,
|
|
|
|
|
LogActionPayload,
|
2022-09-30 12:59:02 +00:00
|
|
|
LogObject,
|
2022-09-16 19:18:54 +00:00
|
|
|
LOG_CATEGORY,
|
|
|
|
|
} from "entities/AppsmithConsole";
|
2021-06-21 08:20:25 +00:00
|
|
|
import {
|
|
|
|
|
all,
|
|
|
|
|
call,
|
|
|
|
|
fork,
|
|
|
|
|
put,
|
|
|
|
|
select,
|
|
|
|
|
take,
|
|
|
|
|
takeEvery,
|
|
|
|
|
} from "redux-saga/effects";
|
2022-12-07 10:28:29 +00:00
|
|
|
import { findIndex, flatten, get, isEmpty, isMatch, set } from "lodash";
|
2021-04-23 13:50:55 +00:00
|
|
|
import { getDebuggerErrors } from "selectors/debuggerSelectors";
|
2021-09-08 17:32:22 +00:00
|
|
|
import {
|
|
|
|
|
getAction,
|
|
|
|
|
getPlugin,
|
|
|
|
|
getJSCollection,
|
|
|
|
|
} from "selectors/entitiesSelector";
|
2021-04-23 13:50:55 +00:00
|
|
|
import { Action, PluginType } from "entities/Action";
|
2021-09-08 17:32:22 +00:00
|
|
|
import { JSCollection } from "entities/JSCollection";
|
2021-04-23 13:50:55 +00:00
|
|
|
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
2021-06-21 08:20:25 +00:00
|
|
|
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
|
|
|
|
import {
|
|
|
|
|
getDataTree,
|
|
|
|
|
getEvaluationInverseDependencyMap,
|
|
|
|
|
} from "selectors/dataTreeSelectors";
|
2022-09-30 12:59:02 +00:00
|
|
|
import {
|
|
|
|
|
createLogTitleString,
|
|
|
|
|
getDependencyChain,
|
|
|
|
|
} from "components/editorComponents/Debugger/helpers";
|
2021-06-21 08:20:25 +00:00
|
|
|
import {
|
|
|
|
|
ACTION_CONFIGURATION_UPDATED,
|
|
|
|
|
createMessage,
|
|
|
|
|
WIDGET_PROPERTIES_UPDATED,
|
2022-02-11 18:08:46 +00:00
|
|
|
} from "@appsmith/constants/messages";
|
2021-09-09 15:10:22 +00:00
|
|
|
import AppsmithConsole from "utils/AppsmithConsole";
|
|
|
|
|
import { getWidget } from "./selectors";
|
2021-07-08 05:31:08 +00:00
|
|
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
|
|
|
|
import { Plugin } from "api/PluginApi";
|
|
|
|
|
import { getCurrentPageId } from "selectors/editorSelectors";
|
|
|
|
|
import { WidgetProps } from "widgets/BaseWidget";
|
2021-11-05 05:49:19 +00:00
|
|
|
import * as log from "loglevel";
|
2022-06-21 13:57:34 +00:00
|
|
|
import { DependencyMap } from "utils/DynamicBindingUtils";
|
2022-12-22 06:34:28 +00:00
|
|
|
import { TriggerMeta } from "@appsmith/sagas/ActionExecution/ActionExecutionSagas";
|
2022-11-03 09:23:15 +00:00
|
|
|
import {
|
|
|
|
|
getEntityNameAndPropertyPath,
|
|
|
|
|
isAction,
|
|
|
|
|
isWidget,
|
2022-12-22 06:34:28 +00:00
|
|
|
} from "@appsmith/workers/Evaluation/evaluationUtils";
|
2021-04-23 13:50:55 +00:00
|
|
|
|
2021-08-09 05:27:58 +00:00
|
|
|
// Saga to format action request values to be shown in the debugger
|
|
|
|
|
function* formatActionRequestSaga(
|
|
|
|
|
payload: LogActionPayload,
|
|
|
|
|
requestPath?: any,
|
|
|
|
|
) {
|
|
|
|
|
// If there are no headers or body we don't format anything.
|
|
|
|
|
if (!payload.source || !payload.state || !requestPath) {
|
|
|
|
|
return payload;
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
|
|
|
|
|
2021-08-09 05:27:58 +00:00
|
|
|
const request = get(payload, requestPath);
|
2021-04-23 13:50:55 +00:00
|
|
|
|
|
|
|
|
const source = payload.source;
|
2021-07-13 09:53:02 +00:00
|
|
|
const action: Action | undefined = yield select(getAction, source.id);
|
2021-08-09 05:27:58 +00:00
|
|
|
// Only formatting for apis and not queries
|
2021-07-13 09:53:02 +00:00
|
|
|
if (action && action.pluginType === PluginType.API) {
|
2021-08-09 05:27:58 +00:00
|
|
|
// Formatting api headers here
|
|
|
|
|
if (request.headers) {
|
|
|
|
|
let formattedHeaders = [];
|
2021-04-23 13:50:55 +00:00
|
|
|
|
2021-08-09 05:27:58 +00:00
|
|
|
// Convert headers from Record<string, array>[] to Record<string, string>[]
|
|
|
|
|
// for showing in the logs
|
|
|
|
|
formattedHeaders = Object.keys(request.headers).map((key: string) => {
|
|
|
|
|
const value = request.headers[key];
|
|
|
|
|
return {
|
|
|
|
|
[key]: value[0],
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
set(payload, `${requestPath}.headers`, formattedHeaders);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Formatting api body
|
|
|
|
|
if (request.body) {
|
|
|
|
|
let body = request.body;
|
2021-04-23 13:50:55 +00:00
|
|
|
|
2021-08-09 05:27:58 +00:00
|
|
|
try {
|
|
|
|
|
body = JSON.parse(body);
|
|
|
|
|
set(payload, `${requestPath}.body`, body);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// Nothing to do here, we show the api body as it is if it cannot be shown as
|
|
|
|
|
// an object
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the final payload to be logged
|
|
|
|
|
return payload;
|
2021-04-23 13:50:55 +00:00
|
|
|
} else {
|
2021-08-09 05:27:58 +00:00
|
|
|
return payload;
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
function* onEntityDeleteSaga(payload: Log[]) {
|
|
|
|
|
const sortedLogs = payload.reduce(
|
|
|
|
|
(
|
|
|
|
|
sortedLogs: {
|
|
|
|
|
withSource: Log[];
|
|
|
|
|
withoutSource: Log[];
|
|
|
|
|
},
|
|
|
|
|
log,
|
|
|
|
|
) => {
|
|
|
|
|
return log.source
|
|
|
|
|
? { ...sortedLogs, withSource: [...sortedLogs.withSource, log] }
|
|
|
|
|
: { ...sortedLogs, withSource: [...sortedLogs.withoutSource, log] };
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
withSource: [],
|
|
|
|
|
withoutSource: [],
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
if (!isEmpty(sortedLogs.withoutSource)) {
|
|
|
|
|
yield put(debuggerLog(sortedLogs.withoutSource));
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
2022-12-07 10:28:29 +00:00
|
|
|
if (isEmpty(sortedLogs.withSource)) return;
|
2021-04-23 13:50:55 +00:00
|
|
|
|
2021-08-16 11:03:27 +00:00
|
|
|
const errors: Record<string, Log> = yield select(getDebuggerErrors);
|
2021-04-23 13:50:55 +00:00
|
|
|
const errorIds = Object.keys(errors);
|
2022-12-07 10:28:29 +00:00
|
|
|
const logSourceIds = sortedLogs.withSource.map((log) => log.source?.id);
|
|
|
|
|
|
|
|
|
|
const errorsToDelete = errorIds.reduce((errorList: Log[], currentId) => {
|
|
|
|
|
const isPresent = logSourceIds.some((id) => id && currentId.includes(id));
|
|
|
|
|
return isPresent ? [...errorList, errors[currentId]] : errorList;
|
|
|
|
|
}, []);
|
2021-04-23 13:50:55 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
sortedLogs.withSource.filter(
|
|
|
|
|
(log) => log.source && errorIds.includes(log.source.id),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!isEmpty(errorsToDelete)) {
|
|
|
|
|
const errorPayload = errorsToDelete.map((log) => ({
|
|
|
|
|
id: log.id as string,
|
|
|
|
|
analytics: log.analytics,
|
|
|
|
|
}));
|
|
|
|
|
AppsmithConsole.deleteErrors(errorPayload);
|
|
|
|
|
}
|
|
|
|
|
yield put(debuggerLog(sortedLogs.withSource));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getLogsFromDependencyChain(
|
|
|
|
|
dependencyChain: string[],
|
|
|
|
|
payload: Log,
|
|
|
|
|
dataTree: DataTree,
|
|
|
|
|
) {
|
|
|
|
|
return dependencyChain.map((path) => {
|
|
|
|
|
const entityInfo = getEntityNameAndPropertyPath(path);
|
|
|
|
|
const entity = dataTree[entityInfo.entityName];
|
|
|
|
|
let log = {
|
|
|
|
|
...payload,
|
|
|
|
|
state: {
|
|
|
|
|
[entityInfo.propertyPath]: get(dataTree, path),
|
|
|
|
|
},
|
|
|
|
|
};
|
2021-04-23 13:50:55 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
if (isAction(entity)) {
|
|
|
|
|
log = {
|
|
|
|
|
...log,
|
|
|
|
|
text: createMessage(ACTION_CONFIGURATION_UPDATED),
|
|
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.ACTION,
|
|
|
|
|
name: entityInfo.entityName,
|
|
|
|
|
id: entity.actionId,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
} else if (isWidget(entity)) {
|
|
|
|
|
log = {
|
|
|
|
|
...log,
|
|
|
|
|
text: createMessage(WIDGET_PROPERTIES_UPDATED),
|
|
|
|
|
source: {
|
|
|
|
|
type: ENTITY_TYPE.WIDGET,
|
|
|
|
|
name: entityInfo.entityName,
|
|
|
|
|
id: entity.widgetId,
|
|
|
|
|
},
|
|
|
|
|
};
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
return log;
|
|
|
|
|
});
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
function* logDependentEntityProperties(payload: Log[]) {
|
|
|
|
|
const validLogs = payload.filter((log) => log.state && log.source);
|
|
|
|
|
if (isEmpty(validLogs)) return;
|
2021-06-21 08:20:25 +00:00
|
|
|
|
|
|
|
|
yield take(ReduxActionTypes.SET_EVALUATED_TREE);
|
|
|
|
|
const dataTree: DataTree = yield select(getDataTree);
|
2022-06-21 13:57:34 +00:00
|
|
|
const inverseDependencyMap: DependencyMap = yield select(
|
|
|
|
|
getEvaluationInverseDependencyMap,
|
|
|
|
|
);
|
2022-12-07 10:28:29 +00:00
|
|
|
const finalPayload: Log[][] = [];
|
2021-06-21 08:20:25 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
for (const log of validLogs) {
|
|
|
|
|
const propertyPath = `${log.source?.name}.` + log.source?.propertyPath;
|
|
|
|
|
const dependencyChain = getDependencyChain(
|
|
|
|
|
propertyPath,
|
|
|
|
|
inverseDependencyMap,
|
|
|
|
|
);
|
|
|
|
|
const payloadValue = getLogsFromDependencyChain(
|
|
|
|
|
dependencyChain,
|
|
|
|
|
log,
|
|
|
|
|
dataTree,
|
|
|
|
|
);
|
|
|
|
|
finalPayload.push(payloadValue);
|
|
|
|
|
}
|
2022-09-08 08:16:28 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
//logging them all at once rather than updating them individually
|
|
|
|
|
yield put(debuggerLog(flatten(finalPayload)));
|
2021-06-21 08:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
function* onTriggerPropertyUpdates(payload: Log[]) {
|
2021-09-15 05:11:13 +00:00
|
|
|
const dataTree: DataTree = yield select(getDataTree);
|
2022-12-07 10:28:29 +00:00
|
|
|
const validLogs = payload.filter(
|
|
|
|
|
(log) => log.source && log.source.propertyPath,
|
|
|
|
|
);
|
|
|
|
|
if (isEmpty(validLogs)) return;
|
|
|
|
|
|
|
|
|
|
const errorsPathsToDeleteFromConsole = new Set<string>();
|
|
|
|
|
|
|
|
|
|
for (const log of validLogs) {
|
|
|
|
|
const { source } = log;
|
|
|
|
|
if (!source || !source.propertyPath) continue;
|
|
|
|
|
const widget = dataTree[source.name];
|
|
|
|
|
// If property is not a trigger property we ignore
|
|
|
|
|
if (!isWidget(widget) || !(source.propertyPath in widget.triggerPaths))
|
|
|
|
|
return false;
|
|
|
|
|
// If the value of the property is empty(or set to 'No Action')
|
|
|
|
|
if (widget[source.propertyPath] === "") {
|
|
|
|
|
errorsPathsToDeleteFromConsole.add(`${source.id}-${source.propertyPath}`);
|
|
|
|
|
}
|
2021-09-15 05:11:13 +00:00
|
|
|
}
|
2022-12-07 10:28:29 +00:00
|
|
|
const errorIdsToDelete = Array.from(
|
|
|
|
|
errorsPathsToDeleteFromConsole,
|
|
|
|
|
).map((path) => ({ id: path }));
|
|
|
|
|
AppsmithConsole.deleteErrors(errorIdsToDelete);
|
2021-09-15 05:11:13 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
function* debuggerLogSaga(action: ReduxAction<Log[]>) {
|
|
|
|
|
const { payload: logs } = action;
|
|
|
|
|
// array of logs without LOG_TYPE and logs which are not handled in switch statement below.
|
|
|
|
|
let otherLogs: Log[] = [];
|
|
|
|
|
|
|
|
|
|
// Group logs by LOG_TYPE
|
|
|
|
|
const sortedLogs = logs.reduce(
|
|
|
|
|
(sortedLogs: Record<string, Log[]>, currentLog: Log) => {
|
|
|
|
|
if (currentLog.logType) {
|
|
|
|
|
return sortedLogs.hasOwnProperty(currentLog.logType)
|
|
|
|
|
? {
|
|
|
|
|
...sortedLogs,
|
|
|
|
|
[currentLog.logType]: [
|
|
|
|
|
...sortedLogs[currentLog.logType],
|
|
|
|
|
currentLog,
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
...sortedLogs,
|
|
|
|
|
[currentLog.logType]: [currentLog],
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
otherLogs.push(currentLog);
|
|
|
|
|
return sortedLogs;
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
2022-12-07 10:28:29 +00:00
|
|
|
},
|
|
|
|
|
{},
|
|
|
|
|
);
|
|
|
|
|
for (const item in sortedLogs) {
|
|
|
|
|
const logType = Number(item);
|
|
|
|
|
const payload = sortedLogs[item];
|
|
|
|
|
switch (logType) {
|
|
|
|
|
case LOG_TYPE.WIDGET_UPDATE:
|
|
|
|
|
yield put(debuggerLog(payload));
|
|
|
|
|
yield call(logDependentEntityProperties, payload);
|
|
|
|
|
yield call(onTriggerPropertyUpdates, payload);
|
|
|
|
|
return;
|
|
|
|
|
case LOG_TYPE.ACTION_UPDATE:
|
|
|
|
|
yield put(debuggerLog(payload));
|
|
|
|
|
yield call(logDependentEntityProperties, payload);
|
|
|
|
|
return;
|
|
|
|
|
case LOG_TYPE.JS_ACTION_UPDATE:
|
|
|
|
|
yield put(debuggerLog(payload));
|
|
|
|
|
return;
|
|
|
|
|
case LOG_TYPE.JS_PARSE_ERROR:
|
|
|
|
|
yield put(addErrorLogs(payload));
|
|
|
|
|
break;
|
|
|
|
|
case LOG_TYPE.JS_PARSE_SUCCESS: {
|
|
|
|
|
const errorIds = payload.map((log) => ({ id: log.source?.id ?? "" }));
|
|
|
|
|
AppsmithConsole.deleteErrors(errorIds);
|
|
|
|
|
break;
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
2022-12-07 10:28:29 +00:00
|
|
|
// @ts-expect-error: Types are not available
|
|
|
|
|
case LOG_TYPE.TRIGGER_EVAL_ERROR:
|
|
|
|
|
yield put(debuggerLog(payload));
|
|
|
|
|
case LOG_TYPE.EVAL_ERROR:
|
|
|
|
|
case LOG_TYPE.LINT_ERROR:
|
|
|
|
|
case LOG_TYPE.EVAL_WARNING:
|
|
|
|
|
case LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR: {
|
|
|
|
|
const filteredLogs = payload.filter(
|
|
|
|
|
(log) => log.source && log.source.propertyPath && log.text,
|
2021-04-23 13:50:55 +00:00
|
|
|
);
|
2022-12-07 10:28:29 +00:00
|
|
|
yield put(addErrorLogs(filteredLogs));
|
|
|
|
|
break;
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
2022-12-07 10:28:29 +00:00
|
|
|
|
|
|
|
|
case LOG_TYPE.ACTION_EXECUTION_ERROR:
|
|
|
|
|
{
|
|
|
|
|
const allFormatedLogs: Log[] = [];
|
|
|
|
|
|
|
|
|
|
for (const log of payload) {
|
|
|
|
|
const formattedLog: Log = yield call(
|
|
|
|
|
formatActionRequestSaga,
|
|
|
|
|
log,
|
|
|
|
|
"state",
|
|
|
|
|
);
|
|
|
|
|
allFormatedLogs.push(formattedLog);
|
|
|
|
|
}
|
|
|
|
|
yield put(addErrorLogs(allFormatedLogs));
|
|
|
|
|
yield put(debuggerLog(allFormatedLogs));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case LOG_TYPE.ACTION_EXECUTION_SUCCESS:
|
|
|
|
|
{
|
|
|
|
|
const allFormatedLogs: Log[] = [];
|
|
|
|
|
|
|
|
|
|
for (const log of payload) {
|
|
|
|
|
const formattedLog: Log = yield call(
|
|
|
|
|
formatActionRequestSaga,
|
|
|
|
|
log,
|
|
|
|
|
"state.request",
|
|
|
|
|
);
|
|
|
|
|
allFormatedLogs.push(formattedLog);
|
|
|
|
|
}
|
|
|
|
|
const payloadIds = payload.map((log) => ({
|
|
|
|
|
id: log.source?.id ?? "",
|
|
|
|
|
}));
|
|
|
|
|
AppsmithConsole.deleteErrors(payloadIds);
|
|
|
|
|
|
|
|
|
|
yield put(debuggerLog(allFormatedLogs));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case LOG_TYPE.ENTITY_DELETED:
|
|
|
|
|
yield fork(onEntityDeleteSaga, payload);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
otherLogs = otherLogs.concat(payload);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!isEmpty(otherLogs)) {
|
|
|
|
|
yield put(debuggerLog(otherLogs));
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 05:31:08 +00:00
|
|
|
// This saga is intended for analytics only
|
|
|
|
|
function* logDebuggerErrorAnalyticsSaga(
|
|
|
|
|
action: ReduxAction<LogDebuggerErrorAnalyticsPayload>,
|
|
|
|
|
) {
|
2021-07-08 12:44:31 +00:00
|
|
|
try {
|
|
|
|
|
const { payload } = action;
|
2022-06-21 13:57:34 +00:00
|
|
|
const currentPageId: string | undefined = yield select(getCurrentPageId);
|
2021-07-08 05:31:08 +00:00
|
|
|
|
2021-07-08 12:44:31 +00:00
|
|
|
if (payload.entityType === ENTITY_TYPE.WIDGET) {
|
2021-08-16 11:03:27 +00:00
|
|
|
const widget: WidgetProps | undefined = yield select(
|
|
|
|
|
getWidget,
|
|
|
|
|
payload.entityId,
|
|
|
|
|
);
|
|
|
|
|
const widgetType = widget?.type || payload?.analytics?.widgetType || "";
|
2021-07-08 12:44:31 +00:00
|
|
|
const propertyPath = `${widgetType}.${payload.propertyPath}`;
|
2021-07-08 05:31:08 +00:00
|
|
|
|
2021-07-08 12:44:31 +00:00
|
|
|
// Sending widget type for widgets
|
|
|
|
|
AnalyticsUtil.logEvent(payload.eventName, {
|
|
|
|
|
entityType: widgetType,
|
|
|
|
|
propertyPath,
|
|
|
|
|
errorMessages: payload.errorMessages,
|
|
|
|
|
pageId: currentPageId,
|
2021-08-16 11:03:27 +00:00
|
|
|
errorMessage: payload.errorMessage,
|
|
|
|
|
errorType: payload.errorType,
|
2021-07-08 12:44:31 +00:00
|
|
|
});
|
|
|
|
|
} else if (payload.entityType === ENTITY_TYPE.ACTION) {
|
2021-08-16 11:03:27 +00:00
|
|
|
const action: Action | undefined = yield select(
|
|
|
|
|
getAction,
|
|
|
|
|
payload.entityId,
|
|
|
|
|
);
|
|
|
|
|
const pluginId = action?.pluginId || payload?.analytics?.pluginId || "";
|
|
|
|
|
const plugin: Plugin = yield select(getPlugin, pluginId);
|
2021-07-08 12:44:31 +00:00
|
|
|
const pluginName = plugin.name.replace(/ /g, "");
|
|
|
|
|
let propertyPath = `${pluginName}`;
|
2021-07-08 05:31:08 +00:00
|
|
|
|
2021-07-08 12:44:31 +00:00
|
|
|
if (payload.propertyPath) {
|
|
|
|
|
propertyPath += `.${payload.propertyPath}`;
|
|
|
|
|
}
|
2021-07-08 05:31:08 +00:00
|
|
|
|
2021-07-08 12:44:31 +00:00
|
|
|
// Sending plugin name for actions
|
|
|
|
|
AnalyticsUtil.logEvent(payload.eventName, {
|
|
|
|
|
entityType: pluginName,
|
|
|
|
|
propertyPath,
|
|
|
|
|
errorMessages: payload.errorMessages,
|
|
|
|
|
pageId: currentPageId,
|
2021-08-16 11:03:27 +00:00
|
|
|
errorMessage: payload.errorMessage,
|
|
|
|
|
errorType: payload.errorType,
|
2021-08-23 13:47:17 +00:00
|
|
|
errorSubType: payload.errorSubType,
|
2021-07-08 12:44:31 +00:00
|
|
|
});
|
2021-09-08 17:32:22 +00:00
|
|
|
} else if (payload.entityType === ENTITY_TYPE.JSACTION) {
|
|
|
|
|
const action: JSCollection = yield select(
|
|
|
|
|
getJSCollection,
|
|
|
|
|
payload.entityId,
|
|
|
|
|
);
|
2022-06-30 07:21:20 +00:00
|
|
|
if (!action) return;
|
2021-09-08 17:32:22 +00:00
|
|
|
const plugin: Plugin = yield select(getPlugin, action.pluginId);
|
2022-05-05 13:18:56 +00:00
|
|
|
const pluginName = plugin?.name?.replace(/ /g, "");
|
2021-09-08 17:32:22 +00:00
|
|
|
|
|
|
|
|
// Sending plugin name for actions
|
|
|
|
|
AnalyticsUtil.logEvent(payload.eventName, {
|
|
|
|
|
entityType: pluginName,
|
2021-09-21 06:02:45 +00:00
|
|
|
propertyPath: payload.propertyPath,
|
2021-09-08 17:32:22 +00:00
|
|
|
errorMessages: payload.errorMessages,
|
|
|
|
|
pageId: currentPageId,
|
|
|
|
|
});
|
2021-07-08 12:44:31 +00:00
|
|
|
}
|
|
|
|
|
} catch (e) {
|
2021-11-05 05:49:19 +00:00
|
|
|
log.error(e);
|
2021-07-08 05:31:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
function* addDebuggerErrorLogsSaga(action: ReduxAction<Log[]>) {
|
|
|
|
|
const errorLogs = action.payload;
|
|
|
|
|
const currentDebuggerErrors: Record<string, Log> = yield select(
|
|
|
|
|
getDebuggerErrors,
|
|
|
|
|
);
|
|
|
|
|
yield put(debuggerLogInit(errorLogs));
|
|
|
|
|
const validErrorLogs = errorLogs.filter((log) => log.source && log.id);
|
|
|
|
|
if (isEmpty(validErrorLogs)) return;
|
|
|
|
|
|
|
|
|
|
for (const errorLog of validErrorLogs) {
|
|
|
|
|
const { id, messages, source } = errorLog;
|
|
|
|
|
if (!source || !id) continue;
|
|
|
|
|
const analyticsPayload = {
|
|
|
|
|
entityName: source.name,
|
|
|
|
|
entityType: source.type,
|
|
|
|
|
entityId: source.id,
|
|
|
|
|
propertyPath: source.propertyPath ?? "",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// If this is a new error
|
|
|
|
|
if (!currentDebuggerErrors.hasOwnProperty(id)) {
|
|
|
|
|
const errorMessages = errorLog.messages ?? [];
|
|
|
|
|
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
|
|
|
payload: {
|
|
|
|
|
...analyticsPayload,
|
|
|
|
|
eventName: "DEBUGGER_NEW_ERROR",
|
|
|
|
|
errorMessages,
|
|
|
|
|
},
|
|
|
|
|
});
|
2021-08-16 11:03:27 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
// Log analytics for new error messages
|
|
|
|
|
if (errorMessages.length && errorLog) {
|
|
|
|
|
yield all(
|
|
|
|
|
errorMessages.map((errorMessage) =>
|
|
|
|
|
put({
|
|
|
|
|
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
|
|
|
payload: {
|
|
|
|
|
...analyticsPayload,
|
|
|
|
|
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
|
|
|
|
errorMessage: errorMessage.message,
|
|
|
|
|
errorType: errorMessage.type,
|
|
|
|
|
errorSubType: errorMessage.subType,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const updatedErrorMessages = messages ?? [];
|
|
|
|
|
const existingErrorMessages = currentDebuggerErrors[id].messages ?? [];
|
|
|
|
|
// Log new error messages
|
|
|
|
|
yield all(
|
|
|
|
|
updatedErrorMessages.map((updatedErrorMessage) => {
|
|
|
|
|
const exists = findIndex(
|
|
|
|
|
existingErrorMessages,
|
|
|
|
|
(existingErrorMessage) => {
|
|
|
|
|
return isMatch(existingErrorMessage, updatedErrorMessage);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (exists < 0) {
|
|
|
|
|
return put({
|
|
|
|
|
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
|
|
|
payload: {
|
|
|
|
|
...analyticsPayload,
|
|
|
|
|
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
|
|
|
|
errorMessage: updatedErrorMessage.message,
|
|
|
|
|
errorType: updatedErrorMessage.type,
|
|
|
|
|
errorSubType: updatedErrorMessage.subType,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
// Log resolved error messages
|
|
|
|
|
yield all(
|
|
|
|
|
existingErrorMessages.map((existingErrorMessage) => {
|
|
|
|
|
const exists = findIndex(
|
|
|
|
|
updatedErrorMessages,
|
|
|
|
|
(updatedErrorMessage) => {
|
|
|
|
|
return isMatch(updatedErrorMessage, existingErrorMessage);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (exists < 0) {
|
|
|
|
|
return put({
|
|
|
|
|
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
|
|
|
payload: {
|
|
|
|
|
...analyticsPayload,
|
|
|
|
|
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
|
|
|
|
errorMessage: existingErrorMessage.message,
|
|
|
|
|
errorType: existingErrorMessage.type,
|
|
|
|
|
errorSubType: existingErrorMessage.subType,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function* deleteDebuggerErrorLogsSaga(
|
|
|
|
|
action: ReduxAction<{ id: string; analytics: Log["analytics"] }[]>,
|
|
|
|
|
) {
|
|
|
|
|
const { payload } = action;
|
|
|
|
|
const currentDebuggerErrors: Record<string, Log> = yield select(
|
|
|
|
|
getDebuggerErrors,
|
|
|
|
|
);
|
|
|
|
|
const existingErrorPayloads = payload.filter((item) =>
|
|
|
|
|
currentDebuggerErrors.hasOwnProperty(item.id),
|
|
|
|
|
);
|
|
|
|
|
if (isEmpty(existingErrorPayloads)) return;
|
2021-08-16 11:03:27 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
const validErrorPayloadsToDelete = existingErrorPayloads.filter((payload) => {
|
|
|
|
|
const existingError = currentDebuggerErrors[payload.id];
|
|
|
|
|
return existingError && existingError.source;
|
|
|
|
|
});
|
2021-08-25 04:34:42 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
if (isEmpty(validErrorPayloadsToDelete)) return;
|
2021-08-16 11:03:27 +00:00
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
for (const validErrorPayload of validErrorPayloadsToDelete) {
|
|
|
|
|
const error = currentDebuggerErrors[validErrorPayload.id];
|
|
|
|
|
if (!error || !error.source) continue;
|
|
|
|
|
const analyticsPayload = {
|
|
|
|
|
entityName: error.source.name,
|
|
|
|
|
entityType: error.source.type,
|
|
|
|
|
entityId: error.source.id,
|
|
|
|
|
propertyPath: error.source.propertyPath ?? "",
|
|
|
|
|
analytics: validErrorPayload.analytics,
|
|
|
|
|
};
|
|
|
|
|
const errorMessages = error.messages;
|
2021-08-16 11:03:27 +00:00
|
|
|
|
|
|
|
|
yield put({
|
|
|
|
|
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
|
|
|
payload: {
|
|
|
|
|
...analyticsPayload,
|
2022-12-07 10:28:29 +00:00
|
|
|
eventName: "DEBUGGER_RESOLVED_ERROR",
|
|
|
|
|
errorMessages,
|
2021-08-16 11:03:27 +00:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2022-12-07 10:28:29 +00:00
|
|
|
if (errorMessages) {
|
2021-08-16 11:03:27 +00:00
|
|
|
yield all(
|
2022-12-07 10:28:29 +00:00
|
|
|
errorMessages.map((errorMessage) => {
|
|
|
|
|
return put({
|
2021-08-16 11:03:27 +00:00
|
|
|
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
|
|
|
payload: {
|
|
|
|
|
...analyticsPayload,
|
2022-12-07 10:28:29 +00:00
|
|
|
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
2021-08-16 11:03:27 +00:00
|
|
|
errorMessage: errorMessage.message,
|
|
|
|
|
errorType: errorMessage.type,
|
2021-08-23 13:47:17 +00:00
|
|
|
errorSubType: errorMessage.subType,
|
2021-08-16 11:03:27 +00:00
|
|
|
},
|
2022-12-07 10:28:29 +00:00
|
|
|
});
|
|
|
|
|
}),
|
2021-08-16 11:03:27 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-07 10:28:29 +00:00
|
|
|
const validErrorIds = validErrorPayloadsToDelete.map((payload) => payload.id);
|
|
|
|
|
yield put(deleteErrorLog(validErrorIds));
|
2021-08-16 11:03:27 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-04 11:58:05 +00:00
|
|
|
// takes a log object array and stores it in the redux store
|
2023-02-11 18:33:20 +00:00
|
|
|
export function* storeLogs(logs: LogObject[]) {
|
2022-09-16 19:18:54 +00:00
|
|
|
AppsmithConsole.addLogs(
|
2023-01-10 04:53:08 +00:00
|
|
|
logs.map((log: LogObject) => {
|
|
|
|
|
return {
|
2022-09-04 11:58:05 +00:00
|
|
|
text: createLogTitleString(log.data),
|
|
|
|
|
logData: log.data,
|
2023-02-11 18:33:20 +00:00
|
|
|
source: log.source,
|
2022-09-16 19:18:54 +00:00
|
|
|
severity: log.severity,
|
|
|
|
|
timestamp: log.timestamp,
|
|
|
|
|
category: LOG_CATEGORY.USER_GENERATED,
|
2023-01-10 04:53:08 +00:00
|
|
|
};
|
|
|
|
|
}),
|
2022-09-16 19:18:54 +00:00
|
|
|
);
|
2022-09-04 11:58:05 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-07 06:23:47 +00:00
|
|
|
export function* updateTriggerMeta(
|
2022-09-04 11:58:05 +00:00
|
|
|
triggerMeta: TriggerMeta,
|
|
|
|
|
dynamicTrigger: string,
|
|
|
|
|
) {
|
|
|
|
|
let name = "";
|
|
|
|
|
|
2022-09-07 06:23:47 +00:00
|
|
|
if (!!triggerMeta.source && triggerMeta.source.hasOwnProperty("name")) {
|
2022-09-04 11:58:05 +00:00
|
|
|
name = triggerMeta.source.name;
|
2022-09-07 06:23:47 +00:00
|
|
|
} else if (!!triggerMeta.triggerPropertyName) {
|
|
|
|
|
name = triggerMeta.triggerPropertyName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
name.length === 0 &&
|
|
|
|
|
!!dynamicTrigger &&
|
|
|
|
|
!(dynamicTrigger.includes("{") || dynamicTrigger.includes("}"))
|
2022-09-04 11:58:05 +00:00
|
|
|
) {
|
|
|
|
|
// We use the dynamic trigger as the name if it is not a binding
|
|
|
|
|
name = dynamicTrigger.replace("()", "");
|
2022-09-07 06:23:47 +00:00
|
|
|
triggerMeta["triggerPropertyName"] = name;
|
2022-09-04 11:58:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 13:50:55 +00:00
|
|
|
export default function* debuggerSagasListeners() {
|
2021-07-08 05:31:08 +00:00
|
|
|
yield all([
|
|
|
|
|
takeEvery(ReduxActionTypes.DEBUGGER_LOG_INIT, debuggerLogSaga),
|
2021-09-09 15:10:22 +00:00
|
|
|
|
2021-07-08 05:31:08 +00:00
|
|
|
takeEvery(
|
|
|
|
|
ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
|
|
|
logDebuggerErrorAnalyticsSaga,
|
|
|
|
|
),
|
2021-08-16 11:03:27 +00:00
|
|
|
takeEvery(
|
|
|
|
|
ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG_INIT,
|
2022-12-07 10:28:29 +00:00
|
|
|
addDebuggerErrorLogsSaga,
|
2021-08-16 11:03:27 +00:00
|
|
|
),
|
|
|
|
|
takeEvery(
|
|
|
|
|
ReduxActionTypes.DEBUGGER_DELETE_ERROR_LOG_INIT,
|
2022-12-07 10:28:29 +00:00
|
|
|
deleteDebuggerErrorLogsSaga,
|
2021-08-16 11:03:27 +00:00
|
|
|
),
|
2021-07-08 05:31:08 +00:00
|
|
|
]);
|
2021-04-23 13:50:55 +00:00
|
|
|
}
|