fix: Preserve query execution data on name refactor (#28973)

## Description
Code changes to preserve execution data when a Query/JS Object is
renamed.
>
#### PR fixes following issue(s)
Fixes #28985
>
>
#### 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
- Bug fix (non-breaking change which fixes an issue)
>
>
>
## Testing
>
#### How Has This Been Tested?
- [x] Manual
- [x] 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
- [x] My code follows the style guidelines of this project
- [x] 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
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] 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:
arunvjn 2023-11-22 14:04:48 +05:30 committed by GitHub
parent 1249330f91
commit dee0a54227
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 190 additions and 60 deletions

View File

@ -0,0 +1,88 @@
import * as _ from "../../../../support/Objects/ObjectsCore";
describe("Api execution results test cases", () => {
it("1. Check to see if API execution results are preserved after it is renamed", () => {
const {
agHelper,
apiPage,
dataManager,
entityExplorer,
jsEditor,
propPane,
} = _;
// Drag and drop a button widget
entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.BUTTON, 200, 200);
entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.BUTTON, 400, 400);
entityExplorer.NavigateToSwitcher("Explorer");
// Create a new API
apiPage.CreateAndFillApi(
dataManager.dsValues[dataManager.defaultEnviorment].mockApiUrl,
);
apiPage.RunAPI();
jsEditor.CreateJSObject(
`export default {
async func() {
return Api1.data
}
}`,
{
paste: true,
completeReplace: true,
toRun: true,
shouldCreateNewJSObj: true,
prettify: true,
},
);
entityExplorer.SelectEntityByName("Button1");
// Set the label to the button
propPane.TypeTextIntoField(
"label",
"{{Api1.data ? 'Success 1' : 'Failed 1'}}",
);
propPane.ToggleJSMode("onClick", true);
propPane.TypeTextIntoField("onClick", "{{showAlert('Successful')}}");
agHelper.ClickButton("Success 1");
agHelper.ValidateToastMessage("Successful");
entityExplorer.RenameEntityFromExplorer("Api1", "Api123");
entityExplorer.SelectEntityByName("Button1");
agHelper.ClickButton("Success 1");
agHelper.ValidateToastMessage("Successful");
entityExplorer.SelectEntityByName("Button2");
// Set the label to the button
propPane.TypeTextIntoField(
"label",
"{{JSObject1.func.data ? 'Success 2' : 'Failed 2'}}",
);
propPane.ToggleJSMode("onClick", true);
propPane.TypeTextIntoField("onClick", "{{showAlert('Successful')}}");
agHelper.ClickButton("Success 2");
agHelper.ValidateToastMessage("Successful");
entityExplorer.RenameEntityFromExplorer("JSObject1", "JSObject123");
entityExplorer.SelectEntityByName("Button2");
agHelper.ClickButton("Success 2");
agHelper.ValidateToastMessage("Successful");
});
});

View File

@ -340,22 +340,17 @@ export const bindDataOnCanvas = (payload: {
}; };
}; };
export const updateActionData = ({ export const updateActionData = (
data, payload: {
dataPath, entityName: string;
entityName, dataPath: string;
}: { data: unknown;
entityName: string; dataPathRef?: string;
dataPath: string; }[],
data: unknown; ) => {
}) => {
return { return {
type: ReduxActionTypes.UPDATE_ACTION_DATA, type: ReduxActionTypes.UPDATE_ACTION_DATA,
payload: { payload,
entityName,
dataPath,
data,
},
}; };
}; };

View File

@ -82,11 +82,13 @@ export function* executeActionTriggers(
); );
if (action) { if (action) {
yield put( yield put(
updateActionData({ updateActionData([
entityName: action.name, {
dataPath: "data", entityName: action.name,
data: undefined, dataPath: "data",
}), data: undefined,
},
]),
); );
} }
break; break;

View File

@ -7,7 +7,10 @@ import {
ReduxActionTypes, ReduxActionTypes,
} from "@appsmith/constants/ReduxActionConstants"; } from "@appsmith/constants/ReduxActionConstants";
import { put, select, call } from "redux-saga/effects"; import { put, select, call } from "redux-saga/effects";
import type { FetchActionsPayload } from "actions/pluginActionActions"; import {
updateActionData,
type FetchActionsPayload,
} from "actions/pluginActionActions";
import type { JSAction, JSCollection } from "entities/JSCollection"; import type { JSAction, JSCollection } from "entities/JSCollection";
import { import {
copyJSCollectionError, copyJSCollectionError,
@ -370,6 +373,20 @@ export function* refactorJSObjectName(
actionId: id, actionId: id,
}, },
}); });
const jsObject: JSCollection = yield select((state) =>
getJSCollection(state, id),
);
const functions = jsObject.actions;
yield put(
updateActionData(
functions.map((f) => ({
entityName: newName,
data: undefined,
dataPath: `${f.name}.data`,
dataPathRef: `${oldName}.${f.name}.data`,
})),
),
);
if (currentPageId === pageId) { if (currentPageId === pageId) {
// @ts-expect-error: refactorResponse.data is of type unknown // @ts-expect-error: refactorResponse.data is of type unknown
yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId); yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId);

View File

@ -1175,11 +1175,13 @@ function* executePageLoadAction(pageAction: PageAction) {
}), }),
); );
yield put( yield put(
updateActionData({ updateActionData([
entityName: action.name, {
dataPath: "data", entityName: action.name,
data: payload.body, dataPath: "data",
}), data: payload.body,
},
]),
); );
PerformanceTracker.stopAsyncTracking( PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.EXECUTE_ACTION, PerformanceTransactionName.EXECUTE_ACTION,
@ -1230,11 +1232,13 @@ function* executePageLoadAction(pageAction: PageAction) {
pageAction.id, pageAction.id,
); );
yield put( yield put(
updateActionData({ updateActionData([
entityName: action.name, {
dataPath: "data", entityName: action.name,
data: payload.body, dataPath: "data",
}), data: payload.body,
},
]),
); );
yield take(ReduxActionTypes.SET_EVALUATED_TREE); yield take(ReduxActionTypes.SET_EVALUATED_TREE);
} }
@ -1392,11 +1396,13 @@ function* executePluginActionSaga(
); );
yield put( yield put(
updateActionData({ updateActionData([
entityName: pluginAction.name, {
dataPath: "data", entityName: pluginAction.name,
data: isError ? undefined : payload.body, dataPath: "data",
}), data: isError ? undefined : payload.body,
},
]),
); );
// TODO: Plugins are not always fetched before on page load actions are executed. // TODO: Plugins are not always fetched before on page load actions are executed.
try { try {
@ -1451,11 +1457,13 @@ function* executePluginActionSaga(
}), }),
); );
yield put( yield put(
updateActionData({ updateActionData([
entityName: pluginAction.name, {
dataPath: "data", entityName: pluginAction.name,
data: EMPTY_RESPONSE.body, dataPath: "data",
}), data: EMPTY_RESPONSE.body,
},
]),
); );
if (e instanceof UserCancelledActionExecutionError) { if (e instanceof UserCancelledActionExecutionError) {
// Case: user cancelled the request of file upload // Case: user cancelled the request of file upload
@ -1526,11 +1534,13 @@ function* clearTriggerActionResponse() {
if (action.data && !action.config.executeOnLoad) { if (action.data && !action.config.executeOnLoad) {
yield put(clearActionResponse(action.config.id)); yield put(clearActionResponse(action.config.id));
yield put( yield put(
updateActionData({ updateActionData([
entityName: action.config.name, {
dataPath: "data", entityName: action.config.name,
data: undefined, dataPath: "data",
}), data: undefined,
},
]),
); );
} }
} }
@ -1587,16 +1597,14 @@ function* handleUpdateActionData(
entityName: string; entityName: string;
dataPath: string; dataPath: string;
data: unknown; data: unknown;
dataPathRef?: string;
}>, }>,
) { ) {
const { data, dataPath, entityName } = action.payload; yield call(
yield call(evalWorker.request, EVAL_WORKER_ACTIONS.UPDATE_ACTION_DATA, [ evalWorker.request,
{ EVAL_WORKER_ACTIONS.UPDATE_ACTION_DATA,
entityName, action.payload,
dataPath, );
data,
},
]);
} }
export function* watchPluginActionExecutionSagas() { export function* watchPluginActionExecutionSagas() {

View File

@ -38,6 +38,7 @@ import {
moveActionError, moveActionError,
moveActionSuccess, moveActionSuccess,
updateAction, updateAction,
updateActionData,
updateActionProperty, updateActionProperty,
updateActionSuccess, updateActionSuccess,
} from "actions/pluginActionActions"; } from "actions/pluginActionActions";
@ -747,6 +748,16 @@ export function* refactorActionName(
actionId: id, actionId: id,
}, },
}); });
yield put(
updateActionData([
{
entityName: newName,
dataPath: "data",
data: undefined,
dataPathRef: `${oldName}.data`,
},
]),
);
if (currentPageId === pageId) { if (currentPageId === pageId) {
// @ts-expect-error: refactorResponse is of type unknown // @ts-expect-error: refactorResponse is of type unknown
yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId); yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId);

View File

@ -219,11 +219,13 @@ function* setUpTourAppSaga() {
const query: ActionData | undefined = yield select(getQueryAction); const query: ActionData | undefined = yield select(getQueryAction);
yield put(clearActionResponse(query?.config.id ?? "")); yield put(clearActionResponse(query?.config.id ?? ""));
yield put( yield put(
updateActionData({ updateActionData([
entityName: query?.config.name || "", {
dataPath: "data", entityName: query?.config.name || "",
data: undefined, dataPath: "data",
}), data: undefined,
},
]),
); );
const applicationId: string = yield select(getCurrentApplicationId); const applicationId: string = yield select(getCurrentApplicationId);
yield put( yield put(

View File

@ -1,6 +1,6 @@
import { dataTreeEvaluator } from "./evalTree"; import { dataTreeEvaluator } from "./evalTree";
import type { EvalWorkerSyncRequest } from "../types"; import type { EvalWorkerSyncRequest } from "../types";
import { set } from "lodash"; import set from "lodash/set";
import { evalTreeWithChanges } from "../evalTreeWithChanges"; import { evalTreeWithChanges } from "../evalTreeWithChanges";
import DataStore from "../dataStore"; import DataStore from "../dataStore";
@ -8,6 +8,7 @@ export interface UpdateActionProps {
entityName: string; entityName: string;
dataPath: string; dataPath: string;
data: unknown; data: unknown;
dataPathRef?: string;
} }
export default function (request: EvalWorkerSyncRequest) { export default function (request: EvalWorkerSyncRequest) {
const actionsDataToUpdate: UpdateActionProps[] = request.data; const actionsDataToUpdate: UpdateActionProps[] = request.data;
@ -21,7 +22,13 @@ export function handleActionsDataUpdate(actionsToUpdate: UpdateActionProps[]) {
const evalTree = dataTreeEvaluator.getEvalTree(); const evalTree = dataTreeEvaluator.getEvalTree();
for (const actionToUpdate of actionsToUpdate) { for (const actionToUpdate of actionsToUpdate) {
const { data, dataPath, entityName } = actionToUpdate; const { dataPath, dataPathRef, entityName } = actionToUpdate;
let { data } = actionToUpdate;
if (dataPathRef) {
data = DataStore.getActionData(dataPathRef);
DataStore.deleteActionData(dataPathRef);
}
// update the evaltree // update the evaltree
set(evalTree, `${entityName}.[${dataPath}]`, data); set(evalTree, `${entityName}.[${dataPath}]`, data);
// Update context // Update context