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 = ({
|
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,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user