fix: code editor changes (#40239)

## Description
We've encountered a bug in the REST API plugin where rapidly changing
the URL input causes the save status to get stuck in the loading state.
This happens because the evaluation is debounced, and by the time it's
ready to run, the inputs may have changed in a way that prevents the
evaluation from being triggered. However, we still initiate a saga that
tracks the terminal state and controls the loading status. Since the
evaluation never actually occurs, the terminal state is never reached,
causing the loading status to remain stuck.

Fixes #`Issue Number`  
_or_  
Fixes `Issue URL`
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.JS"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/15493848736>
> Commit: f993e21dd7a3bc0414d5c2cb35e96f3831b625bc
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=15493848736&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.JS`
> Spec:
> <hr>Fri, 06 Jun 2025 16:05:30 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **Refactor**
- Streamlined change handling in the code editor for more consistent
updates using a standardized debounce timer.
- **Bug Fixes**
- Improved auto-save reliability by adding a delay before save status
checks to prevent premature assertions.
- **New Features**
- Introduced a configurable save delay constant to unify save operation
timing across the editor.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: “sneha122” <“sneha@appsmith.com”>
This commit is contained in:
Vemparala Surya Vamsi 2025-06-09 11:21:27 +05:30 committed by GitHub
parent fde8d013aa
commit 99db0a1fae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 17 additions and 27 deletions

View File

@ -8,6 +8,9 @@ import EditorNavigator from "./EditorNavigation";
import { EntityType } from "./EditorNavigation";
import ClickOptions = Cypress.ClickOptions;
import { DEBOUNCE_WAIT_TIME_ON_INPUT_CHANGE } from "../../../src/constants/WidgetConstants";
const {
SAVE_TRIGGER_DELAY_MS,
} = require("../../../src/components/editorComponents/CodeEditor/debounceConstants");
type ElementType = string | JQuery<HTMLElement>;
@ -296,6 +299,11 @@ export class AggregateHelper {
}
public AssertAutoSave() {
// After fix made in https://github.com/appsmithorg/appsmith/pull/40239 to make save state and nw call in sync
// We will need to wait for the debounced time before we make any assertions on save state, otherwise
// we might run into situation where absence of save is asserted but save actually hasn't happened
// Additional 100ms added to avoid flaky issues that might be caused by race condition.
this.Sleep(SAVE_TRIGGER_DELAY_MS + 100);
let saveStatus = this.CheckForPageSaveError();
// wait for save query to trigger & n/w call to finish occuring
if (!saveStatus)

View File

@ -0,0 +1 @@
export const SAVE_TRIGGER_DELAY_MS = 600;

View File

@ -161,6 +161,7 @@ import { getEachEntityInformation } from "ee/utils/autocomplete/EntityDefinition
import { getCurrentPageId } from "selectors/editorSelectors";
import { executeCommandAction } from "actions/pluginActionActions";
import { PEEK_OVERLAY_DELAY } from "./PeekOverlayPopup/constants";
import { SAVE_TRIGGER_DELAY_MS } from "./debounceConstants";
type ReduxStateProps = ReturnType<typeof mapStateToProps>;
type ReduxDispatchProps = ReturnType<typeof mapDispatchToProps>;
@ -269,8 +270,6 @@ interface State {
isOpened: boolean;
autoCompleteVisible: boolean;
hinterOpen: boolean;
// Flag for determining whether the entity change has been started or not so that even if the initial and final value remains the same, the status should be changed to not loading
changeStarted: boolean;
ctrlPressed: boolean;
peekOverlayProps:
| (PeekOverlayStateProps & {
@ -314,7 +313,6 @@ class CodeEditor extends Component<Props, State> {
isOpened: false,
autoCompleteVisible: false,
hinterOpen: false,
changeStarted: false,
ctrlPressed: false,
peekOverlayProps: undefined,
showAIWindow: false,
@ -1309,17 +1307,17 @@ class CodeEditor extends Component<Props, State> {
instance?: CodeMirror.Editor,
changeObj?: CodeMirror.EditorChangeLinkedList,
) => {
const value = this.editor?.getValue() || "";
const value = this.editor.getValue() || "";
const inputValue = this.props.input.value || "";
if (
this.props.input.onChange &&
((value !== inputValue && this.state.isFocused) ||
this.state.changeStarted)
value !== inputValue &&
this.state.isFocused
) {
this.setState({
changeStarted: false,
});
/* This action updates the status of the savingEntity to true so that any
shortcut commands do not execute before updating the entity in the store */
this.props.startingEntityUpdate();
this.props.input.onChange(value);
}
@ -1364,29 +1362,12 @@ class CodeEditor extends Component<Props, State> {
}
};
handleDebouncedChange = _.debounce(this.handleChange, 600);
handleDebouncedChange = _.debounce(this.handleChange, SAVE_TRIGGER_DELAY_MS);
startChange = (
instance: CodeMirror.Editor,
changeObj: CodeMirror.EditorChangeLinkedList,
) => {
/* This action updates the status of the savingEntity to true so that any
shortcut commands do not execute before updating the entity in the store */
const value = this.editor.getValue() || "";
const inputValue = this.props.input.value || "";
if (
this.props.input.onChange &&
value !== inputValue &&
this.state.isFocused &&
!this.state.changeStarted
) {
this.setState({
changeStarted: true,
});
this.props.startingEntityUpdate();
}
this.hidePeekOverlay();
this.handleDebouncedChange(instance, changeObj);
};