diff --git a/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_Bugs_Spec.js b/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_Bugs_Spec.js index 419d4c56d2..02ac326a51 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_Bugs_Spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/ApiTests/API_Bugs_Spec.js @@ -192,6 +192,20 @@ describe("Rest Bugs tests", function () { }); }); + // this test applies to other fields as well - params and body formdata + it("Bug 25817: Assert that header fields are correctly updated.", function () { + apiPage.CreateAndFillApi("https://postman-echo.com/gzip", "HeaderTest"); + apiPage.EnterHeader("hello", "world", 0); + apiPage.EnterHeader("", "", 1); + agHelper.GetNClick(apiPage._addMoreHeaderFieldButton); + apiPage.EnterHeader("hey", "there", 2); + + agHelper.RefreshPage(); + + apiPage.ValidateHeaderParams({ key: "hello", value: "world" }, 0); + apiPage.ValidateHeaderParams({ key: "hey", value: "there" }, 1); + }); + afterEach(() => { // put your clean up code if any }); diff --git a/app/client/cypress/support/Pages/ApiPage.ts b/app/client/cypress/support/Pages/ApiPage.ts index dc156e0c35..8ea38936b6 100644 --- a/app/client/cypress/support/Pages/ApiPage.ts +++ b/app/client/cypress/support/Pages/ApiPage.ts @@ -76,6 +76,7 @@ export class ApiPage { `.t--auto-generated-${key}-info`; private _createQuery = ".t--create-query"; public _editorDS = ".t--datasource-editor"; + public _addMoreHeaderFieldButton = ".t--addApiHeader"; CreateApi( apiName = "", @@ -291,10 +292,13 @@ export class ApiPage { this.agHelper.ValidateCodeEditorContent(this._paramValue(0), param.value); } - ValidateHeaderParams(header: { key: string; value: string }) { + ValidateHeaderParams(header: { key: string; value: string }, index = 0) { this.SelectPaneTab("Headers"); - this.agHelper.ValidateCodeEditorContent(this._headerKey(0), header.key); - this.agHelper.ValidateCodeEditorContent(this._headerValue(0), header.value); + this.agHelper.ValidateCodeEditorContent(this._headerKey(index), header.key); + this.agHelper.ValidateCodeEditorContent( + this._headerValue(index), + header.value, + ); } ValidateImportedHeaderParams( diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index efac9788b3..6e12dd3f1b 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -50,6 +50,7 @@ import AnalyticsUtil from "utils/AnalyticsUtil"; import type { Action, ActionViewMode, + ApiAction, ApiActionConfig, SlashCommandPayload, } from "entities/Action"; @@ -89,7 +90,7 @@ import { ERROR_ACTION_MOVE_FAIL, ERROR_ACTION_RENAME_FAIL, } from "@appsmith/constants/messages"; -import { get, merge } from "lodash"; +import { get, isEmpty, merge } from "lodash"; import { fixActionPayloadForMongoQuery, getConfigInitialValues, @@ -404,19 +405,39 @@ export function* fetchActionsForPageSaga( } } -export function* updateActionSaga( - actionPayload: ReduxAction<{ id: string; action?: Action }>, -) { +export function* updateActionSaga(actionPayload: ReduxAction<{ id: string }>) { try { PerformanceTracker.startAsyncTracking( PerformanceTransactionName.UPDATE_ACTION_API, { actionid: actionPayload.payload.id }, ); - let action = actionPayload.payload.action; - if (!action) action = yield select(getAction, actionPayload.payload.id); + + let action: Action = yield select(getAction, actionPayload.payload.id); if (!action) throw new Error("Could not find action to update"); if (isAPIAction(action)) { + // get api action object from redux form + const reduxFormApiAction: ApiAction = yield select( + getFormValues(API_EDITOR_FORM_NAME), + ); + + // run transformation on redux form action's headers, bodyformData and queryParameters. + // the reason we do this is because the transformation should only be done on the raw action data from the redux form. + // However sometimes when we attempt to save an API as a datasource, we update the Apiaction with the datasource information and the redux form data will not be available i.e. reduxFormApiAction = undefined + // In this scenario we can just default to the action object - (skip the if block below). + if (!isEmpty(reduxFormApiAction)) { + action = { + ...action, + actionConfiguration: { + ...action.actionConfiguration, + headers: reduxFormApiAction.actionConfiguration.headers, + bodyFormData: reduxFormApiAction.actionConfiguration.bodyFormData, + queryParameters: + reduxFormApiAction.actionConfiguration.queryParameters, + }, + }; + } + action = transformRestAction(action); } @@ -426,10 +447,7 @@ export function* updateActionSaga( // @ts-expect-error: Types are not available action = fixActionPayloadForMongoQuery(action); } - const response: ApiResponse = yield ActionAPI.updateAction( - // @ts-expect-error: Types are not available - action, - ); + const response: ApiResponse = yield ActionAPI.updateAction(action); const isValidResponse: boolean = yield validateResponse(response); if (isValidResponse) {