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:
parent
1249330f91
commit
dee0a54227
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
|
|
@ -340,22 +340,17 @@ export const bindDataOnCanvas = (payload: {
|
|||
};
|
||||
};
|
||||
|
||||
export const updateActionData = ({
|
||||
data,
|
||||
dataPath,
|
||||
entityName,
|
||||
}: {
|
||||
entityName: string;
|
||||
dataPath: string;
|
||||
data: unknown;
|
||||
}) => {
|
||||
export const updateActionData = (
|
||||
payload: {
|
||||
entityName: string;
|
||||
dataPath: string;
|
||||
data: unknown;
|
||||
dataPathRef?: string;
|
||||
}[],
|
||||
) => {
|
||||
return {
|
||||
type: ReduxActionTypes.UPDATE_ACTION_DATA,
|
||||
payload: {
|
||||
entityName,
|
||||
dataPath,
|
||||
data,
|
||||
},
|
||||
payload,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -82,11 +82,13 @@ export function* executeActionTriggers(
|
|||
);
|
||||
if (action) {
|
||||
yield put(
|
||||
updateActionData({
|
||||
entityName: action.name,
|
||||
dataPath: "data",
|
||||
data: undefined,
|
||||
}),
|
||||
updateActionData([
|
||||
{
|
||||
entityName: action.name,
|
||||
dataPath: "data",
|
||||
data: undefined,
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import {
|
|||
ReduxActionTypes,
|
||||
} from "@appsmith/constants/ReduxActionConstants";
|
||||
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 {
|
||||
copyJSCollectionError,
|
||||
|
|
@ -370,6 +373,20 @@ export function* refactorJSObjectName(
|
|||
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) {
|
||||
// @ts-expect-error: refactorResponse.data is of type unknown
|
||||
yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId);
|
||||
|
|
|
|||
|
|
@ -1175,11 +1175,13 @@ function* executePageLoadAction(pageAction: PageAction) {
|
|||
}),
|
||||
);
|
||||
yield put(
|
||||
updateActionData({
|
||||
entityName: action.name,
|
||||
dataPath: "data",
|
||||
data: payload.body,
|
||||
}),
|
||||
updateActionData([
|
||||
{
|
||||
entityName: action.name,
|
||||
dataPath: "data",
|
||||
data: payload.body,
|
||||
},
|
||||
]),
|
||||
);
|
||||
PerformanceTracker.stopAsyncTracking(
|
||||
PerformanceTransactionName.EXECUTE_ACTION,
|
||||
|
|
@ -1230,11 +1232,13 @@ function* executePageLoadAction(pageAction: PageAction) {
|
|||
pageAction.id,
|
||||
);
|
||||
yield put(
|
||||
updateActionData({
|
||||
entityName: action.name,
|
||||
dataPath: "data",
|
||||
data: payload.body,
|
||||
}),
|
||||
updateActionData([
|
||||
{
|
||||
entityName: action.name,
|
||||
dataPath: "data",
|
||||
data: payload.body,
|
||||
},
|
||||
]),
|
||||
);
|
||||
yield take(ReduxActionTypes.SET_EVALUATED_TREE);
|
||||
}
|
||||
|
|
@ -1392,11 +1396,13 @@ function* executePluginActionSaga(
|
|||
);
|
||||
|
||||
yield put(
|
||||
updateActionData({
|
||||
entityName: pluginAction.name,
|
||||
dataPath: "data",
|
||||
data: isError ? undefined : payload.body,
|
||||
}),
|
||||
updateActionData([
|
||||
{
|
||||
entityName: pluginAction.name,
|
||||
dataPath: "data",
|
||||
data: isError ? undefined : payload.body,
|
||||
},
|
||||
]),
|
||||
);
|
||||
// TODO: Plugins are not always fetched before on page load actions are executed.
|
||||
try {
|
||||
|
|
@ -1451,11 +1457,13 @@ function* executePluginActionSaga(
|
|||
}),
|
||||
);
|
||||
yield put(
|
||||
updateActionData({
|
||||
entityName: pluginAction.name,
|
||||
dataPath: "data",
|
||||
data: EMPTY_RESPONSE.body,
|
||||
}),
|
||||
updateActionData([
|
||||
{
|
||||
entityName: pluginAction.name,
|
||||
dataPath: "data",
|
||||
data: EMPTY_RESPONSE.body,
|
||||
},
|
||||
]),
|
||||
);
|
||||
if (e instanceof UserCancelledActionExecutionError) {
|
||||
// Case: user cancelled the request of file upload
|
||||
|
|
@ -1526,11 +1534,13 @@ function* clearTriggerActionResponse() {
|
|||
if (action.data && !action.config.executeOnLoad) {
|
||||
yield put(clearActionResponse(action.config.id));
|
||||
yield put(
|
||||
updateActionData({
|
||||
entityName: action.config.name,
|
||||
dataPath: "data",
|
||||
data: undefined,
|
||||
}),
|
||||
updateActionData([
|
||||
{
|
||||
entityName: action.config.name,
|
||||
dataPath: "data",
|
||||
data: undefined,
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1587,16 +1597,14 @@ function* handleUpdateActionData(
|
|||
entityName: string;
|
||||
dataPath: string;
|
||||
data: unknown;
|
||||
dataPathRef?: string;
|
||||
}>,
|
||||
) {
|
||||
const { data, dataPath, entityName } = action.payload;
|
||||
yield call(evalWorker.request, EVAL_WORKER_ACTIONS.UPDATE_ACTION_DATA, [
|
||||
{
|
||||
entityName,
|
||||
dataPath,
|
||||
data,
|
||||
},
|
||||
]);
|
||||
yield call(
|
||||
evalWorker.request,
|
||||
EVAL_WORKER_ACTIONS.UPDATE_ACTION_DATA,
|
||||
action.payload,
|
||||
);
|
||||
}
|
||||
|
||||
export function* watchPluginActionExecutionSagas() {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import {
|
|||
moveActionError,
|
||||
moveActionSuccess,
|
||||
updateAction,
|
||||
updateActionData,
|
||||
updateActionProperty,
|
||||
updateActionSuccess,
|
||||
} from "actions/pluginActionActions";
|
||||
|
|
@ -747,6 +748,16 @@ export function* refactorActionName(
|
|||
actionId: id,
|
||||
},
|
||||
});
|
||||
yield put(
|
||||
updateActionData([
|
||||
{
|
||||
entityName: newName,
|
||||
dataPath: "data",
|
||||
data: undefined,
|
||||
dataPathRef: `${oldName}.data`,
|
||||
},
|
||||
]),
|
||||
);
|
||||
if (currentPageId === pageId) {
|
||||
// @ts-expect-error: refactorResponse is of type unknown
|
||||
yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId);
|
||||
|
|
|
|||
|
|
@ -219,11 +219,13 @@ function* setUpTourAppSaga() {
|
|||
const query: ActionData | undefined = yield select(getQueryAction);
|
||||
yield put(clearActionResponse(query?.config.id ?? ""));
|
||||
yield put(
|
||||
updateActionData({
|
||||
entityName: query?.config.name || "",
|
||||
dataPath: "data",
|
||||
data: undefined,
|
||||
}),
|
||||
updateActionData([
|
||||
{
|
||||
entityName: query?.config.name || "",
|
||||
dataPath: "data",
|
||||
data: undefined,
|
||||
},
|
||||
]),
|
||||
);
|
||||
const applicationId: string = yield select(getCurrentApplicationId);
|
||||
yield put(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { dataTreeEvaluator } from "./evalTree";
|
||||
import type { EvalWorkerSyncRequest } from "../types";
|
||||
import { set } from "lodash";
|
||||
import set from "lodash/set";
|
||||
import { evalTreeWithChanges } from "../evalTreeWithChanges";
|
||||
import DataStore from "../dataStore";
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ export interface UpdateActionProps {
|
|||
entityName: string;
|
||||
dataPath: string;
|
||||
data: unknown;
|
||||
dataPathRef?: string;
|
||||
}
|
||||
export default function (request: EvalWorkerSyncRequest) {
|
||||
const actionsDataToUpdate: UpdateActionProps[] = request.data;
|
||||
|
|
@ -21,7 +22,13 @@ export function handleActionsDataUpdate(actionsToUpdate: UpdateActionProps[]) {
|
|||
const evalTree = dataTreeEvaluator.getEvalTree();
|
||||
|
||||
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
|
||||
set(evalTree, `${entityName}.[${dataPath}]`, data);
|
||||
// Update context
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user