chore: Refactor widget rename saga to extend for packages (#40243)

## Description
This PR implements a flexible extension system for widget name updates
by:
- Creating a singleton WidgetNameUpdateExtension class for pluggable
widget renaming handlers
- Adding default implementation with handleWidgetNameUpdateDefault
function
- Refactoring UpdateWidgetNameRequest interface to support optional
fields (pageId, moduleId, contextType)
- Extracting widget name update logic to dedicated functions for better
maintainability
- Adding packageMiddleware to the Redux store configuration
These changes enable customized widget naming behavior in different
contexts without modifying core code, improving extensibility while
maintaining backward compatibility.

PR for https://github.com/appsmithorg/appsmith-ee/pull/7122

## Automation

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

### 🔍 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/14463310871>
> Commit: 4b4433fa68479ada6426ee39d353abc3973a2864
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=14463310871&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Tue, 15 Apr 2025 15:51:36 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

- **Refactor**
- Modularized the widget name update process, allowing for extension or
override of update behavior.
- Improved the structure of widget name update logic for better
maintainability.

- **Chores**
- Updated internal interfaces to support additional parameters and
optional properties.
  - Added a new middleware to the Redux store setup.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ashit Rath 2025-04-18 09:53:33 +05:30 committed by GitHub
parent c8a132f88d
commit b5d22b8ba0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 103 additions and 34 deletions

View File

@ -135,10 +135,12 @@ export interface ClonePageRequest {
}
export interface UpdateWidgetNameRequest {
pageId: string;
pageId?: string;
layoutId: string;
newName: string;
oldName: string;
moduleId?: string;
contextType?: "MODULE" | "PAGE";
}
export interface GenerateTemplatePageRequest {

View File

@ -153,6 +153,11 @@ import {
} from "selectors/gitModSelectors";
import captureException from "instrumentation/sendFaroErrors";
export interface HandleWidgetNameUpdatePayload {
newName: string;
widgetName: string;
}
export const checkIfMigrationIsNeeded = (
fetchPageResponse?: FetchPageResponse,
) => {
@ -954,6 +959,96 @@ export function* clonePageSaga(
}
}
export class WidgetNameUpdateExtension {
// Singleton instance
private static instance = new WidgetNameUpdateExtension();
// The extension function storage
private extensionFunction:
| ((params: HandleWidgetNameUpdatePayload) => Generator)
| null = null;
// Private constructor
private constructor() {}
// Get the instance
static getInstance() {
return this.instance;
}
// Set the extension function
setExtension(fn: (params: HandleWidgetNameUpdatePayload) => Generator) {
this.extensionFunction = fn;
}
// Get the extension function
getExtension() {
return this.extensionFunction;
}
}
export function* updateWidgetNameAPISaga(
requestParams: UpdateWidgetNameRequest,
) {
const response: UpdateWidgetNameResponse = yield call(
PageApi.updateWidgetName,
requestParams,
);
const isValidResponse: boolean = yield validateResponse(response);
return { response, isValidResponse };
}
export function* handleWidgetNameUpdateDefault(
params: HandleWidgetNameUpdatePayload,
) {
const { newName, widgetName } = params;
const layoutId: string | undefined = yield select(getCurrentLayoutId);
const pageId: string | undefined = yield select(getCurrentPageId);
const request: UpdateWidgetNameRequest = {
newName: newName,
oldName: widgetName,
pageId,
// @ts-expect-error: layoutId can be undefined
layoutId,
};
const { isValidResponse, response } = yield call(
updateWidgetNameAPISaga,
request,
);
if (isValidResponse) {
// @ts-expect-error: pageId can be undefined
yield updateCanvasWithDSL(response.data, pageId, layoutId);
yield put(updateWidgetNameSuccess());
// Add this to the page DSLs for entity explorer
yield put({
type: ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS,
payload: {
pageId: pageId,
dsl: response.data.dsl,
layoutId,
},
});
checkAndLogErrorsIfCyclicDependency(
(response.data as PageLayout).layoutOnLoadActionErrors,
);
}
}
export function* handleWidgetNameUpdate(params: HandleWidgetNameUpdatePayload) {
const extension = WidgetNameUpdateExtension.getInstance().getExtension();
if (extension) {
yield call(extension, params);
} else {
yield call(handleWidgetNameUpdateDefault, params);
}
}
/**
* this saga do two things
*
@ -967,8 +1062,6 @@ export function* updateWidgetNameSaga(
) {
try {
const { widgetName } = yield select(getWidgetName, action.payload.id);
const layoutId: string | undefined = yield select(getCurrentLayoutId);
const pageId: string | undefined = yield select(getCurrentPageId);
const getUsedNames: Record<string, true> = yield select(
getUsedActionNames,
"",
@ -1058,37 +1151,10 @@ export function* updateWidgetNameSaga(
// check if name is not conflicting with any
// existing entity/api/queries/reserved words
if (isNameValid(action.payload.newName, getUsedNames)) {
const request: UpdateWidgetNameRequest = {
yield call(handleWidgetNameUpdate, {
newName: action.payload.newName,
oldName: widgetName,
// @ts-expect-error: pageId can be undefined
pageId,
// @ts-expect-error: layoutId can be undefined
layoutId,
};
const response: UpdateWidgetNameResponse = yield call(
PageApi.updateWidgetName,
request,
);
const isValidResponse: boolean = yield validateResponse(response);
if (isValidResponse) {
// @ts-expect-error: pageId can be undefined
yield updateCanvasWithDSL(response.data, pageId, layoutId);
yield put(updateWidgetNameSuccess());
// Add this to the page DSLs for entity explorer
yield put({
type: ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS,
payload: {
pageId: pageId,
dsl: response.data.dsl,
layoutId,
},
});
checkAndLogErrorsIfCyclicDependency(
(response.data as PageLayout).layoutOnLoadActionErrors,
);
}
widgetName,
});
} else {
yield put({
type: ReduxActionErrorTypes.UPDATE_WIDGET_NAME_ERROR,

View File

@ -8,6 +8,7 @@ import { composeWithDevTools } from "redux-devtools-extension/logOnlyInProductio
import * as Sentry from "@sentry/react";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import routeParamsMiddleware from "ee/middlewares/RouteParamsMiddleware";
import packageMiddleware from "ee/middlewares/PackageMiddleware";
const sagaMiddleware = createSagaMiddleware();
const ignoredSentryActionTypes = [
@ -30,7 +31,7 @@ export default createStore(
appReducer,
composeWithDevTools(
reduxBatch,
applyMiddleware(sagaMiddleware, routeParamsMiddleware),
applyMiddleware(packageMiddleware, sagaMiddleware, routeParamsMiddleware),
reduxBatch,
sentryReduxEnhancer,
),