chore: Refactor debugger analytics for active fields (#28036)
## Description This PR fires debugger analytics for active fields only after the editor onblur event is triggered. #### PR fixes following issue(s) Fixes #27679 > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
This commit is contained in:
parent
b04548b915
commit
3d1640e0ae
9
app/client/src/actions/activeFieldActions.ts
Normal file
9
app/client/src/actions/activeFieldActions.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||||
|
|
||||||
|
export const setActiveEditorField = (field: string) => ({
|
||||||
|
type: ReduxActionTypes.SET_ACTIVE_EDITOR_FIELD,
|
||||||
|
payload: { field },
|
||||||
|
});
|
||||||
|
export const resetActiveEditorField = () => ({
|
||||||
|
type: ReduxActionTypes.RESET_ACTIVE_EDITOR_FIELD,
|
||||||
|
});
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||||
import type { ENTITY_TYPE, Log, Message } from "entities/AppsmithConsole";
|
import type {
|
||||||
|
ENTITY_TYPE,
|
||||||
|
Log,
|
||||||
|
Message,
|
||||||
|
SourceEntity,
|
||||||
|
} from "entities/AppsmithConsole";
|
||||||
import type { DebuggerContext } from "reducers/uiReducers/debuggerReducer";
|
import type { DebuggerContext } from "reducers/uiReducers/debuggerReducer";
|
||||||
import type { EventName } from "@appsmith/utils/analyticsUtilTypes";
|
import type { EventName } from "@appsmith/utils/analyticsUtilTypes";
|
||||||
import type { APP_MODE } from "entities/App";
|
import type { APP_MODE } from "entities/App";
|
||||||
|
|
@ -17,6 +22,10 @@ export interface LogDebuggerErrorAnalyticsPayload {
|
||||||
errorSubType?: Message["subType"];
|
errorSubType?: Message["subType"];
|
||||||
analytics?: Log["analytics"];
|
analytics?: Log["analytics"];
|
||||||
appMode: APP_MODE;
|
appMode: APP_MODE;
|
||||||
|
source: SourceEntity;
|
||||||
|
logId: string;
|
||||||
|
environmentId?: string;
|
||||||
|
environmentName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const debuggerLogInit = (payload: Log[]) => ({
|
export const debuggerLogInit = (payload: Log[]) => ({
|
||||||
|
|
@ -61,14 +70,6 @@ export const deleteErrorLog = (ids: string[]) => ({
|
||||||
payload: ids,
|
payload: ids,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only used for analytics
|
|
||||||
export const logDebuggerErrorAnalytics = (
|
|
||||||
payload: LogDebuggerErrorAnalyticsPayload,
|
|
||||||
) => ({
|
|
||||||
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
|
||||||
payload,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const hideDebuggerErrors = (payload: boolean) => ({
|
export const hideDebuggerErrors = (payload: boolean) => ({
|
||||||
type: ReduxActionTypes.HIDE_DEBUGGER_ERRORS,
|
type: ReduxActionTypes.HIDE_DEBUGGER_ERRORS,
|
||||||
payload,
|
payload,
|
||||||
|
|
|
||||||
|
|
@ -866,6 +866,8 @@ const ActionTypes = {
|
||||||
DELETE_MULTIPLE_APPLICATION_CANCEL: "DELETE_MULTIPLE_APPLICATION_CANCEL",
|
DELETE_MULTIPLE_APPLICATION_CANCEL: "DELETE_MULTIPLE_APPLICATION_CANCEL",
|
||||||
TRIGGER_EVAL: "TRIGGER_EVAL",
|
TRIGGER_EVAL: "TRIGGER_EVAL",
|
||||||
UPDATE_ACTION_DATA: "UPDATE_ACTION_DATA",
|
UPDATE_ACTION_DATA: "UPDATE_ACTION_DATA",
|
||||||
|
SET_ACTIVE_EDITOR_FIELD: "SET_ACTIVE_EDITOR_FIELD",
|
||||||
|
RESET_ACTIVE_EDITOR_FIELD: "RESET_ACTIVE_EDITOR_FIELD",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReduxActionTypes = {
|
export const ReduxActionTypes = {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ import type { OneClickBindingState } from "reducers/uiReducers/oneClickBindingRe
|
||||||
/* Reducers which are integrated into the core system when registering a pluggable module
|
/* Reducers which are integrated into the core system when registering a pluggable module
|
||||||
or done so by a module that is designed to be eventually pluggable */
|
or done so by a module that is designed to be eventually pluggable */
|
||||||
import type { WidgetPositionsReduxState } from "layoutSystems/anvil/integrations/reducers/widgetPositionsReducer";
|
import type { WidgetPositionsReduxState } from "layoutSystems/anvil/integrations/reducers/widgetPositionsReducer";
|
||||||
|
import type { ActiveField } from "reducers/uiReducers/activeFieldEditorReducer";
|
||||||
|
|
||||||
export const reducerObject = {
|
export const reducerObject = {
|
||||||
entities: entityReducer,
|
entities: entityReducer,
|
||||||
|
|
@ -142,6 +143,7 @@ export interface AppState {
|
||||||
layoutConversion: layoutConversionReduxState;
|
layoutConversion: layoutConversionReduxState;
|
||||||
actionSelector: ActionSelectorReduxState;
|
actionSelector: ActionSelectorReduxState;
|
||||||
oneClickBinding: OneClickBindingState;
|
oneClickBinding: OneClickBindingState;
|
||||||
|
activeField: ActiveField;
|
||||||
};
|
};
|
||||||
entities: {
|
entities: {
|
||||||
canvasWidgetsStructure: CanvasWidgetStructure;
|
canvasWidgetsStructure: CanvasWidgetStructure;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import autoHeightUIReducer from "reducers/uiReducers/autoHeightReducer";
|
||||||
import analyticsReducer from "reducers/uiReducers/analyticsReducer";
|
import analyticsReducer from "reducers/uiReducers/analyticsReducer";
|
||||||
import layoutConversionReducer from "reducers/uiReducers/layoutConversionReducer";
|
import layoutConversionReducer from "reducers/uiReducers/layoutConversionReducer";
|
||||||
import oneClickBindingReducer from "reducers/uiReducers/oneClickBindingReducer";
|
import oneClickBindingReducer from "reducers/uiReducers/oneClickBindingReducer";
|
||||||
|
import activeFieldReducer from "reducers/uiReducers/activeFieldEditorReducer";
|
||||||
|
|
||||||
export const uiReducerObject = {
|
export const uiReducerObject = {
|
||||||
analytics: analyticsReducer,
|
analytics: analyticsReducer,
|
||||||
|
|
@ -100,4 +101,5 @@ export const uiReducerObject = {
|
||||||
layoutConversion: layoutConversionReducer,
|
layoutConversion: layoutConversionReducer,
|
||||||
actionSelector: actionSelectorReducer,
|
actionSelector: actionSelectorReducer,
|
||||||
oneClickBinding: oneClickBindingReducer,
|
oneClickBinding: oneClickBindingReducer,
|
||||||
|
activeField: activeFieldReducer,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,10 @@ import { CodeEditorSignPosting } from "@appsmith/components/editorComponents/Cod
|
||||||
import { getFocusablePropertyPaneField } from "selectors/propertyPaneSelectors";
|
import { getFocusablePropertyPaneField } from "selectors/propertyPaneSelectors";
|
||||||
import resizeObserver from "utils/resizeObserver";
|
import resizeObserver from "utils/resizeObserver";
|
||||||
import { EMPTY_BINDING } from "../ActionCreator/constants";
|
import { EMPTY_BINDING } from "../ActionCreator/constants";
|
||||||
|
import {
|
||||||
|
resetActiveEditorField,
|
||||||
|
setActiveEditorField,
|
||||||
|
} from "actions/activeFieldActions";
|
||||||
|
|
||||||
type ReduxStateProps = ReturnType<typeof mapStateToProps>;
|
type ReduxStateProps = ReturnType<typeof mapStateToProps>;
|
||||||
type ReduxDispatchProps = ReturnType<typeof mapDispatchToProps>;
|
type ReduxDispatchProps = ReturnType<typeof mapDispatchToProps>;
|
||||||
|
|
@ -1063,6 +1067,7 @@ class CodeEditor extends Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleEditorFocus = (cm: CodeMirror.Editor) => {
|
handleEditorFocus = (cm: CodeMirror.Editor) => {
|
||||||
|
this.props.setActiveField(this.props.dataTreePath || "");
|
||||||
this.setState({ isFocused: true });
|
this.setState({ isFocused: true });
|
||||||
const { sticky } = cm.getCursor();
|
const { sticky } = cm.getCursor();
|
||||||
const isUserFocus = sticky !== null;
|
const isUserFocus = sticky !== null;
|
||||||
|
|
@ -1118,6 +1123,7 @@ class CodeEditor extends Component<Props, State> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.props.resetActiveField();
|
||||||
this.handleChange();
|
this.handleChange();
|
||||||
this.setState({ isFocused: false });
|
this.setState({ isFocused: false });
|
||||||
this.editor.setOption("matchBrackets", false);
|
this.editor.setOption("matchBrackets", false);
|
||||||
|
|
@ -1724,6 +1730,8 @@ const mapDispatchToProps = (dispatch: any) => ({
|
||||||
startingEntityUpdate: () => dispatch(startingEntityUpdate()),
|
startingEntityUpdate: () => dispatch(startingEntityUpdate()),
|
||||||
setCodeEditorLastFocus: (payload: CodeEditorFocusState) =>
|
setCodeEditorLastFocus: (payload: CodeEditorFocusState) =>
|
||||||
dispatch(setEditorFieldFocusAction(payload)),
|
dispatch(setEditorFieldFocusAction(payload)),
|
||||||
|
setActiveField: (path: string) => dispatch(setActiveEditorField(path)),
|
||||||
|
resetActiveField: () => dispatch(resetActiveEditorField()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Sentry.withProfiler(
|
export default Sentry.withProfiler(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
|
||||||
|
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||||
|
import { createReducer } from "utils/ReducerUtils";
|
||||||
|
|
||||||
|
export type ActiveField = null | string;
|
||||||
|
const initialState: ActiveField = null;
|
||||||
|
|
||||||
|
const activeFieldReducer = createReducer(initialState, {
|
||||||
|
[ReduxActionTypes.SET_ACTIVE_EDITOR_FIELD]: (
|
||||||
|
state: ActiveField,
|
||||||
|
action: ReduxAction<{ field: string }>,
|
||||||
|
) => {
|
||||||
|
return action.payload.field;
|
||||||
|
},
|
||||||
|
[ReduxActionTypes.RESET_ACTIVE_EDITOR_FIELD]: () => {
|
||||||
|
return initialState;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default activeFieldReducer;
|
||||||
|
|
@ -64,6 +64,13 @@ import {
|
||||||
isWidget,
|
isWidget,
|
||||||
} from "@appsmith/workers/Evaluation/evaluationUtils";
|
} from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||||
import { getCurrentEnvironmentDetails } from "@appsmith/selectors/environmentSelectors";
|
import { getCurrentEnvironmentDetails } from "@appsmith/selectors/environmentSelectors";
|
||||||
|
import { getActiveEditorField } from "selectors/activeEditorFieldSelectors";
|
||||||
|
|
||||||
|
let blockedSource: string | null = null;
|
||||||
|
|
||||||
|
function generateErrorId(error: Log) {
|
||||||
|
return error.id + "_" + error.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
// Saga to format action request values to be shown in the debugger
|
// Saga to format action request values to be shown in the debugger
|
||||||
function* formatActionRequestSaga(
|
function* formatActionRequestSaga(
|
||||||
|
|
@ -376,12 +383,29 @@ function* debuggerLogSaga(action: ReduxAction<Log[]>) {
|
||||||
|
|
||||||
// This saga is intended for analytics only
|
// This saga is intended for analytics only
|
||||||
function* logDebuggerErrorAnalyticsSaga(
|
function* logDebuggerErrorAnalyticsSaga(
|
||||||
action: ReduxAction<LogDebuggerErrorAnalyticsPayload>,
|
analyticsPayload: LogDebuggerErrorAnalyticsPayload,
|
||||||
) {
|
currentDebuggerErrors: Record<string, Log>,
|
||||||
|
): unknown {
|
||||||
try {
|
try {
|
||||||
const { payload } = action;
|
const payload = analyticsPayload;
|
||||||
const currentPageId: string | undefined = yield select(getCurrentPageId);
|
const currentPageId: string | undefined = yield select(getCurrentPageId);
|
||||||
|
const { source } = payload;
|
||||||
|
const activeEditorField: ReturnType<typeof getActiveEditorField> =
|
||||||
|
yield select(getActiveEditorField);
|
||||||
|
const sourceFullPath = source.name + "." + source.propertyPath || "";
|
||||||
|
// To prevent redundant logs for active editor fields
|
||||||
|
// We dispatch log events only after the onBlur event of the editor field is fired
|
||||||
|
if (sourceFullPath === activeEditorField) {
|
||||||
|
if (!blockedSource) {
|
||||||
|
blockedSource = sourceFullPath;
|
||||||
|
yield fork(
|
||||||
|
activeFieldDebuggerErrorHandler,
|
||||||
|
analyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (payload.entityType === ENTITY_TYPE.WIDGET) {
|
if (payload.entityType === ENTITY_TYPE.WIDGET) {
|
||||||
const widget: WidgetProps | undefined = yield select(
|
const widget: WidgetProps | undefined = yield select(
|
||||||
getWidget,
|
getWidget,
|
||||||
|
|
@ -474,15 +498,18 @@ function* addDebuggerErrorLogsSaga(action: ReduxAction<Log[]>) {
|
||||||
if (!currentDebuggerErrors.hasOwnProperty(id)) {
|
if (!currentDebuggerErrors.hasOwnProperty(id)) {
|
||||||
const errorMessages = errorLog.messages ?? [];
|
const errorMessages = errorLog.messages ?? [];
|
||||||
|
|
||||||
yield put({
|
yield fork(
|
||||||
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
logDebuggerErrorAnalyticsSaga,
|
||||||
payload: {
|
{
|
||||||
...analyticsPayload,
|
...analyticsPayload,
|
||||||
eventName: "DEBUGGER_NEW_ERROR",
|
eventName: "DEBUGGER_NEW_ERROR",
|
||||||
errorMessages,
|
errorMessages,
|
||||||
appMode,
|
appMode,
|
||||||
},
|
source,
|
||||||
});
|
logId: id,
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
);
|
||||||
|
|
||||||
// Log analytics for new error messages
|
// Log analytics for new error messages
|
||||||
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
||||||
|
|
@ -492,20 +519,23 @@ function* addDebuggerErrorLogsSaga(action: ReduxAction<Log[]>) {
|
||||||
);
|
);
|
||||||
yield all(
|
yield all(
|
||||||
errorMessages.map((errorMessage) =>
|
errorMessages.map((errorMessage) =>
|
||||||
put({
|
fork(
|
||||||
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
logDebuggerErrorAnalyticsSaga,
|
||||||
payload: {
|
{
|
||||||
...analyticsPayload,
|
...analyticsPayload,
|
||||||
environmentId: currentEnvDetails.id,
|
environmentId: currentEnvDetails.id,
|
||||||
environmentName: currentEnvDetails.name,
|
environmentName: currentEnvDetails.name,
|
||||||
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
||||||
errorId: errorLog.id + "_" + errorLog.timestamp,
|
errorId: generateErrorId(errorLog),
|
||||||
errorMessage: errorMessage.message,
|
errorMessage: errorMessage.message,
|
||||||
errorType: errorMessage.type,
|
errorType: errorMessage.type,
|
||||||
errorSubType: errorMessage.subType,
|
errorSubType: errorMessage.subType,
|
||||||
appMode,
|
appMode,
|
||||||
},
|
source,
|
||||||
}),
|
logId: id,
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -527,20 +557,23 @@ function* addDebuggerErrorLogsSaga(action: ReduxAction<Log[]>) {
|
||||||
|
|
||||||
if (exists < 0) {
|
if (exists < 0) {
|
||||||
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
||||||
return put({
|
return fork(
|
||||||
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
logDebuggerErrorAnalyticsSaga,
|
||||||
payload: {
|
{
|
||||||
...analyticsPayload,
|
...analyticsPayload,
|
||||||
environmentId: currentEnvDetails.id,
|
environmentId: currentEnvDetails.id,
|
||||||
environmentName: currentEnvDetails.name,
|
environmentName: currentEnvDetails.name,
|
||||||
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
||||||
errorId: errorLog.id + "_" + errorLog.timestamp,
|
errorId: generateErrorId(errorLog),
|
||||||
errorMessage: updatedErrorMessage.message,
|
errorMessage: updatedErrorMessage.message,
|
||||||
errorType: updatedErrorMessage.type,
|
errorType: updatedErrorMessage.type,
|
||||||
errorSubType: updatedErrorMessage.subType,
|
errorSubType: updatedErrorMessage.subType,
|
||||||
appMode,
|
appMode,
|
||||||
},
|
source,
|
||||||
});
|
logId: id,
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -556,23 +589,23 @@ function* addDebuggerErrorLogsSaga(action: ReduxAction<Log[]>) {
|
||||||
|
|
||||||
if (exists < 0) {
|
if (exists < 0) {
|
||||||
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
||||||
return put({
|
return fork(
|
||||||
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
logDebuggerErrorAnalyticsSaga,
|
||||||
payload: {
|
{
|
||||||
...analyticsPayload,
|
...analyticsPayload,
|
||||||
environmentId: currentEnvDetails.id,
|
environmentId: currentEnvDetails.id,
|
||||||
environmentName: currentEnvDetails.name,
|
environmentName: currentEnvDetails.name,
|
||||||
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
||||||
errorId:
|
errorId: generateErrorId(currentDebuggerErrors[id]),
|
||||||
currentDebuggerErrors[id].id +
|
|
||||||
"_" +
|
|
||||||
currentDebuggerErrors[id].timestamp,
|
|
||||||
errorMessage: existingErrorMessage.message,
|
errorMessage: existingErrorMessage.message,
|
||||||
errorType: existingErrorMessage.type,
|
errorType: existingErrorMessage.type,
|
||||||
errorSubType: existingErrorMessage.subType,
|
errorSubType: existingErrorMessage.subType,
|
||||||
appMode,
|
appMode,
|
||||||
},
|
source,
|
||||||
});
|
logId: id,
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -611,15 +644,18 @@ function* deleteDebuggerErrorLogsSaga(
|
||||||
};
|
};
|
||||||
const errorMessages = error.messages;
|
const errorMessages = error.messages;
|
||||||
|
|
||||||
yield put({
|
yield fork(
|
||||||
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
logDebuggerErrorAnalyticsSaga,
|
||||||
payload: {
|
{
|
||||||
...analyticsPayload,
|
...analyticsPayload,
|
||||||
eventName: "DEBUGGER_RESOLVED_ERROR",
|
eventName: "DEBUGGER_RESOLVED_ERROR",
|
||||||
errorMessages,
|
errorMessages,
|
||||||
appMode,
|
appMode,
|
||||||
},
|
source: error.source,
|
||||||
});
|
logId: error.id,
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
);
|
||||||
|
|
||||||
if (errorMessages) {
|
if (errorMessages) {
|
||||||
const currentEnvDetails: { id: string; name: string } = yield select(
|
const currentEnvDetails: { id: string; name: string } = yield select(
|
||||||
|
|
@ -628,20 +664,23 @@ function* deleteDebuggerErrorLogsSaga(
|
||||||
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
//errorID has timestamp for 1:1 mapping with new and resolved errors
|
||||||
yield all(
|
yield all(
|
||||||
errorMessages.map((errorMessage) => {
|
errorMessages.map((errorMessage) => {
|
||||||
return put({
|
return fork(
|
||||||
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
logDebuggerErrorAnalyticsSaga,
|
||||||
payload: {
|
{
|
||||||
...analyticsPayload,
|
...analyticsPayload,
|
||||||
environmentId: currentEnvDetails.id,
|
environmentId: currentEnvDetails.id,
|
||||||
environmentName: currentEnvDetails.name,
|
environmentName: currentEnvDetails.name,
|
||||||
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
||||||
errorId: error.id + "_" + error.timestamp,
|
errorId: generateErrorId(error),
|
||||||
errorMessage: errorMessage.message,
|
errorMessage: errorMessage.message,
|
||||||
errorType: errorMessage.type,
|
errorType: errorMessage.type,
|
||||||
errorSubType: errorMessage.subType,
|
errorSubType: errorMessage.subType,
|
||||||
appMode,
|
appMode,
|
||||||
},
|
source: error.source,
|
||||||
});
|
logId: error.id,
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -689,15 +728,154 @@ export function* updateTriggerMeta(
|
||||||
triggerMeta["triggerPropertyName"] = name;
|
triggerMeta["triggerPropertyName"] = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// This function handles logging of debugger error events for active editor fields
|
||||||
|
// Error logs are fired only after the editor gets blur
|
||||||
|
function* activeFieldDebuggerErrorHandler(
|
||||||
|
analyticsPayload: LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors: Record<string, Log>,
|
||||||
|
) {
|
||||||
|
const { logId, source } = analyticsPayload;
|
||||||
|
const initialSourceDebuggerError: Log = currentDebuggerErrors[logId];
|
||||||
|
const sourceMetaData = {
|
||||||
|
entityName: source.name,
|
||||||
|
entityType: source.type,
|
||||||
|
entityId: source.id,
|
||||||
|
propertyPath: source.propertyPath ?? "",
|
||||||
|
source: source,
|
||||||
|
};
|
||||||
|
const appMode: ReturnType<typeof getAppMode> = yield select(getAppMode);
|
||||||
|
const currentEnvDetails: { id: string; name: string } = yield select(
|
||||||
|
getCurrentEnvironmentDetails,
|
||||||
|
);
|
||||||
|
const envMetaData = {
|
||||||
|
appMode,
|
||||||
|
environmentId: currentEnvDetails.id,
|
||||||
|
environmentName: currentEnvDetails.name,
|
||||||
|
};
|
||||||
|
yield take(ReduxActionTypes.RESET_ACTIVE_EDITOR_FIELD);
|
||||||
|
|
||||||
|
const latestDebuggerErrors: Record<string, Log> =
|
||||||
|
yield select(getDebuggerErrors);
|
||||||
|
const latestSourceDebuggerError: Log = latestDebuggerErrors[logId];
|
||||||
|
blockedSource = null;
|
||||||
|
|
||||||
|
if (!initialSourceDebuggerError && latestSourceDebuggerError) {
|
||||||
|
yield fork(
|
||||||
|
logDebuggerErrorAnalyticsSaga,
|
||||||
|
{
|
||||||
|
...sourceMetaData,
|
||||||
|
...envMetaData,
|
||||||
|
eventName: "DEBUGGER_NEW_ERROR",
|
||||||
|
errorMessages: latestSourceDebuggerError.messages,
|
||||||
|
errorId: generateErrorId(latestSourceDebuggerError),
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
latestDebuggerErrors,
|
||||||
|
);
|
||||||
|
|
||||||
|
yield all(
|
||||||
|
latestSourceDebuggerError.messages?.map((errorMessage) =>
|
||||||
|
fork(
|
||||||
|
logDebuggerErrorAnalyticsSaga,
|
||||||
|
{
|
||||||
|
...sourceMetaData,
|
||||||
|
...envMetaData,
|
||||||
|
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
||||||
|
errorId: generateErrorId(latestSourceDebuggerError),
|
||||||
|
errorMessage: errorMessage.message,
|
||||||
|
errorType: errorMessage.type,
|
||||||
|
errorSubType: errorMessage.subType,
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
),
|
||||||
|
) || [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latestSourceDebuggerError && initialSourceDebuggerError) {
|
||||||
|
yield fork(
|
||||||
|
logDebuggerErrorAnalyticsSaga,
|
||||||
|
{
|
||||||
|
...sourceMetaData,
|
||||||
|
...envMetaData,
|
||||||
|
eventName: "DEBUGGER_RESOLVED_ERROR",
|
||||||
|
errorMessages: initialSourceDebuggerError.messages,
|
||||||
|
errorId: generateErrorId(initialSourceDebuggerError),
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
latestDebuggerErrors,
|
||||||
|
);
|
||||||
|
|
||||||
|
yield all(
|
||||||
|
initialSourceDebuggerError.messages?.map((errorMessage) => {
|
||||||
|
return fork(
|
||||||
|
logDebuggerErrorAnalyticsSaga,
|
||||||
|
{
|
||||||
|
...sourceMetaData,
|
||||||
|
...envMetaData,
|
||||||
|
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
||||||
|
errorMessage: errorMessage.message,
|
||||||
|
errorId: generateErrorId(initialSourceDebuggerError),
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
latestDebuggerErrors,
|
||||||
|
);
|
||||||
|
}) || [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestSourceDebuggerError && initialSourceDebuggerError) {
|
||||||
|
const latestErrorMessages = latestSourceDebuggerError.messages || [];
|
||||||
|
const initialErrorMessages = initialSourceDebuggerError.messages || [];
|
||||||
|
yield all(
|
||||||
|
initialErrorMessages.map((initialErrorMessage) => {
|
||||||
|
const exists = findIndex(latestErrorMessages, (latestErrorMessage) => {
|
||||||
|
return isMatch(latestErrorMessage, initialErrorMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exists < 0) {
|
||||||
|
return put({
|
||||||
|
type: ReduxActionTypes.DEBUGGER_ERROR_ANALYTICS,
|
||||||
|
payload: {
|
||||||
|
...sourceMetaData,
|
||||||
|
...envMetaData,
|
||||||
|
eventName: "DEBUGGER_RESOLVED_ERROR_MESSAGE",
|
||||||
|
errorMessage: initialErrorMessage.message,
|
||||||
|
errorId: generateErrorId(initialSourceDebuggerError),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
yield all(
|
||||||
|
latestErrorMessages.map((latestErrorMessage) => {
|
||||||
|
const exists = findIndex(
|
||||||
|
initialErrorMessages,
|
||||||
|
(initialErrorMessage) => {
|
||||||
|
return isMatch(initialErrorMessage, latestErrorMessage);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exists < 0) {
|
||||||
|
return fork(
|
||||||
|
logDebuggerErrorAnalyticsSaga,
|
||||||
|
{
|
||||||
|
...sourceMetaData,
|
||||||
|
...envMetaData,
|
||||||
|
eventName: "DEBUGGER_NEW_ERROR_MESSAGE",
|
||||||
|
errorMessage: latestErrorMessage.message,
|
||||||
|
errorType: latestErrorMessage.type,
|
||||||
|
errorSubType: latestErrorMessage.subType,
|
||||||
|
errorId: generateErrorId(latestSourceDebuggerError),
|
||||||
|
} as LogDebuggerErrorAnalyticsPayload,
|
||||||
|
currentDebuggerErrors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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.DEBUGGER_ERROR_ANALYTICS,
|
|
||||||
logDebuggerErrorAnalyticsSaga,
|
|
||||||
),
|
|
||||||
takeEvery(
|
takeEvery(
|
||||||
ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG_INIT,
|
ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG_INIT,
|
||||||
addDebuggerErrorLogsSaga,
|
addDebuggerErrorLogsSaga,
|
||||||
|
|
|
||||||
|
|
@ -550,7 +550,7 @@ export function* handleJSFunctionExecutionErrorLog(
|
||||||
}),
|
}),
|
||||||
source: {
|
source: {
|
||||||
id: action.collectionId ? action.collectionId : action.id,
|
id: action.collectionId ? action.collectionId : action.id,
|
||||||
name: `${collectionName}.${action.name}`,
|
name: collectionName,
|
||||||
type: ENTITY_TYPE.JSACTION,
|
type: ENTITY_TYPE.JSACTION,
|
||||||
propertyPath: `${action.name}`,
|
propertyPath: `${action.name}`,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
5
app/client/src/selectors/activeEditorFieldSelectors.ts
Normal file
5
app/client/src/selectors/activeEditorFieldSelectors.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import type { AppState } from "@appsmith/reducers";
|
||||||
|
|
||||||
|
export function getActiveEditorField(state: AppState) {
|
||||||
|
return state.ui.activeField;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user