chore: Split JS collection update api call to override for modules (#34009)

## Description
The API call is moved to a saga called `updateJSCollectionAPICall` whose
role is to identify whom this JS collection belongs to `Page` / `Module`
/ `Workflow` and then call the correct API.

In the context of CE, it just calls the existing `updateJSCollection`
but in EE this saga (`updateJSCollectionAPICall`) is extended and does
the checking mentioned above.

For more info on extension check the [EE
PR](https://github.com/appsmithorg/appsmith-ee/pull/4356)

Fixes https://github.com/appsmithorg/appsmith/issues/34008

## 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/9465185887>
> Commit: 5e0214b97de20cf1b9042717fdb578f872c2c7d3
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9465185887&attempt=1"
target="_blank">Click here!</a>

<!-- 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

- **Tests**
  - Added test cases for the `updateJSCollectionAPICall` function.
- Updated `pageId` constant in `pasteSagas.test.ts` for improved
readability and consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com>
This commit is contained in:
ashit-rath 2024-06-11 19:25:34 +05:30 committed by GitHub
parent a114518cc0
commit cdee74e93b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 152 additions and 4 deletions

View File

@ -0,0 +1,124 @@
import { runSaga } from "redux-saga";
import type { Saga } from "redux-saga";
import ActionAPI from "api/ActionAPI";
import { PostgresFactory } from "test/factories/Actions/Postgres";
import type { ApiResponse } from "api/ApiResponses";
import type { Action } from "entities/Action";
import { JSObjectFactory } from "test/factories/Actions/JSObject";
// Since this is a ce test, importing from @appsmith might lead to unexpected results
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import JSActionAPI from "ce/api/JSActionAPI";
import {
updateActionAPICall,
updateJSCollectionAPICall,
} from "./ApiCallerSagas";
jest.mock("ce/api/JSActionAPI");
jest.mock("api/ActionAPI");
const successResponse = <T = any>(data: T) => {
return {
responseMeta: {
status: 200,
success: true,
},
data,
errorDisplay: "",
} as ApiResponse<T>;
};
describe("updateActionAPICall", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("should call ActionAPI.updateAction and return the response", async () => {
const dispatchedActions: any[] = [];
const action = PostgresFactory.build();
const response: ApiResponse<Action> = successResponse(action);
(ActionAPI.updateAction as jest.Mock).mockResolvedValue(response);
const result = await runSaga(
{
dispatch: (action) => dispatchedActions.push(action),
},
updateActionAPICall as Saga,
action,
).toPromise();
expect(ActionAPI.updateAction).toHaveBeenCalledWith(action);
expect(result).toEqual(response);
});
it("should throw an error when ActionAPI.updateAction fails", async () => {
const dispatchedActions: any[] = [];
const action = PostgresFactory.build();
const error = new Error("Some error");
(ActionAPI.updateAction as jest.Mock).mockRejectedValue(error);
try {
await runSaga(
{
dispatch: (action) => dispatchedActions.push(action),
},
updateActionAPICall as Saga,
action,
).toPromise();
} catch (e) {
expect(e).toEqual(error);
}
expect(ActionAPI.updateAction).toHaveBeenCalledWith(action);
});
});
describe("updateJSCollectionAPICall", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("should call JSActionAPI.updateJSCollection and return the response", async () => {
const dispatchedActions: any[] = [];
const jsCollection = JSObjectFactory.build();
const response = successResponse(jsCollection);
(JSActionAPI.updateJSCollection as jest.Mock).mockResolvedValue(response);
const result = await runSaga(
{
dispatch: (action) => dispatchedActions.push(action),
},
updateJSCollectionAPICall as Saga,
jsCollection,
).toPromise();
expect(JSActionAPI.updateJSCollection).toHaveBeenCalledWith(jsCollection);
expect(result).toEqual(response);
});
it("should throw an error when JSActionAPI.updateJSCollection fails", async () => {
const dispatchedActions: any[] = [];
const jsCollection = JSObjectFactory.build();
const error = new Error("Some error");
(JSActionAPI.updateJSCollection as jest.Mock).mockRejectedValue(error);
try {
await runSaga(
{
dispatch: (action) => dispatchedActions.push(action),
},
updateJSCollectionAPICall as Saga,
jsCollection,
).toPromise();
} catch (e) {
expect(e).toEqual(error);
}
expect(JSActionAPI.updateJSCollection).toHaveBeenCalledWith(jsCollection);
});
});

View File

@ -1,6 +1,8 @@
import JSActionAPI from "@appsmith/api/JSActionAPI";
import ActionAPI from "api/ActionAPI";
import type { ApiResponse } from "api/ApiResponses";
import type { Action } from "entities/Action";
import type { JSCollection } from "entities/JSCollection";
/**
* DO NOT ADD any additional code/functionality in this saga. This function is overridden in EE to
@ -18,3 +20,21 @@ export function* updateActionAPICall(action: Action) {
throw e;
}
}
/**
* DO NOT ADD any additional code/functionality in this saga. This function is overridden in EE to
* use a different API for update jsCollection under the package editor.
* The purpose of this saga is only to call the appropriate API and return the result
* @param action Action
* @returns Action
*/
export function* updateJSCollectionAPICall(jsCollection: JSCollection) {
try {
const response: ApiResponse<JSCollection> =
yield JSActionAPI.updateJSCollection(jsCollection);
return response;
} catch (e) {
throw e;
}
}

View File

@ -45,7 +45,7 @@ jest.mock("selectors/layoutSystemSelectors", () => ({
getLayoutSystemType: jest.fn(),
}));
describe("pasteSagas", () => {
const pageId = "pageId";
const pageId = "0123456789abcdef00000000";
beforeAll(() => {
registerLayoutComponents();

View File

@ -100,6 +100,7 @@ import { getFocusablePropertyPaneField } from "selectors/propertyPaneSelectors";
import { getIsSideBySideEnabled } from "selectors/ideSelectors";
import { setIdeEditorViewMode } from "actions/ideActions";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";
import { updateJSCollectionAPICall } from "@appsmith/sagas/ApiCallerSagas";
export interface GenerateDefaultJSObjectProps {
name: string;
@ -325,8 +326,10 @@ function* updateJSCollection(data: {
try {
const { deletedActions, jsCollection, newActions } = data;
if (jsCollection) {
const response: JSCollectionCreateUpdateResponse =
yield JSActionAPI.updateJSCollection(jsCollection);
const response: JSCollectionCreateUpdateResponse = yield call(
updateJSCollectionAPICall,
jsCollection,
);
const isValidResponse: boolean = yield validateResponse(response);
if (isValidResponse) {
if (newActions && newActions.length) {
@ -724,7 +727,8 @@ function* handleUpdateJSFunctionPropertySaga(
});
collection.actions = updatedActions;
const response: ApiResponse<JSCollectionCreateUpdateResponse> =
yield JSActionAPI.updateJSCollection(collection);
yield call(updateJSCollectionAPICall, collection);
const isValidResponse: boolean = yield validateResponse(response);
if (isValidResponse) {
yield put({