diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/DocumentViewer/DocumentViewer_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/DocumentViewer/DocumentViewer_spec.ts index 490bc3c766c4..f0ea56595bed 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/DocumentViewer/DocumentViewer_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/DocumentViewer/DocumentViewer_spec.ts @@ -15,13 +15,12 @@ import EditorNavigation, { } from "../../../../../support/Pages/EditorNavigation"; const ppt = - "https://ssz.sgp1.digitaloceanspaces.com/3ZEO2582C29EA0KKK2/ppt-on-population-pptxafa26c44-208f-46a3-89cc-8a5c020b6863.pptx"; + "http://host.docker.internal:4200/ppt-on-population-pptxafa26c44-208f-46a3-89cc-8a5c020b6863.pptx"; const pngImage = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRb8umIiCU_K6ac-xS-ni4y6SP7XAd8C7Ms3Q&usqp=CAU"; const jpgImage = "https://community.appsmith.com/sites/default/files/styles/small_thumbnail/public/2024-03/aws-logo.jpg?itok=yG4bpfFs"; -const pdf = - "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf"; +const pdf = "http://host.docker.internal:4200/sample-pdf-file.pdf"; describe( "DocumentViewer Widget Functionality", diff --git a/app/client/cypress/e2e/Regression/ServerSide/JsFunctionExecution/Fetch_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/JsFunctionExecution/Fetch_Spec.ts index b59b5fb9dd2b..feff456776c0 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/JsFunctionExecution/Fetch_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/JsFunctionExecution/Fetch_Spec.ts @@ -1,5 +1,7 @@ import { agHelper, + apiPage, + dataManager, entityExplorer, jsEditor, propPane, @@ -76,18 +78,25 @@ describe("Tests fetch calls", { tags: ["@tag.JS"] }, () => { }); it("3. Tests if fetch works with store value", function () { + apiPage.CreateAndFillApi( + dataManager.dsValues[dataManager.defaultEnviorment].mockGenderAge + + `{{this.params.person}}`, + "Gender_Age", + ); + apiPage.RunAPI(); entityExplorer.DragDropWidgetNVerify("buttonwidget", 500, 200); EditorNavigation.SelectEntityByName("Button1", EntityType.Widget); - propPane.TypeTextIntoField("Label", "getUserID"); + propPane.TypeTextIntoField("Label", "getUserName"); propPane.EnterJSContext( "onClick", - `{{fetch('https://jsonplaceholder.typicode.com/todos/1') - .then(res => res.json()) - .then(json => storeValue('userId', json.userId)) - .then(() => showAlert("UserId: " + appsmith.store.userId))}}`, + `{{(async function(){ + const gender = await Gender_Age.run({ person: "sagar" }); + storeValue("Gender", gender); + showAlert("Your name is " + appsmith.store.Gender.name); + })()}}`, ); - agHelper.Sleep(2000); - agHelper.ClickButton("getUserID"); - agHelper.AssertContains("UserId: 1", "exist"); + + agHelper.ClickButton("getUserName"); + agHelper.AssertContains("Your name is sagar", "exist"); }); }); diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 22cfe873e309..51a10762d293 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -68,23 +68,25 @@ export const fetchPageAction = ( export interface FetchPublishedPageActionPayload { pageId: string; bustCache?: boolean; + firstLoad?: boolean; pageWithMigratedDsl?: FetchPageResponse; } export interface FetchPublishedPageResourcesPayload { pageId: string; - basePageId: string; } export const fetchPublishedPageAction = ( pageId: string, bustCache = false, + firstLoad = false, pageWithMigratedDsl?: FetchPageResponse, ): ReduxAction => ({ type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT, payload: { pageId, bustCache, + firstLoad, pageWithMigratedDsl, }, }); @@ -297,14 +299,12 @@ export const clonePageSuccess = ({ // Fetches resources required for published page, currently only used for fetching actions // In future we can reuse this for fetching other page level resources in published mode -export const fetchPublishedPageResources = ({ - basePageId, - pageId, -}: FetchPublishedPageResourcesPayload): ReduxAction => ({ +export const fetchPublishedPageResourcesAction = ( + pageId: string, +): ReduxAction => ({ type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_INIT, payload: { pageId, - basePageId, }, }); @@ -675,18 +675,21 @@ export const setupPageAction = ( export interface SetupPublishedPageActionPayload { pageId: string; bustCache: boolean; + firstLoad: boolean; pageWithMigratedDsl?: FetchPageResponse; } export const setupPublishedPage = ( pageId: string, bustCache = false, + firstLoad = false, pageWithMigratedDsl?: FetchPageResponse, ): ReduxAction => ({ type: ReduxActionTypes.SETUP_PUBLISHED_PAGE_INIT, payload: { pageId, bustCache, + firstLoad, pageWithMigratedDsl, }, }); diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 3d344202b8a4..5d9a68d41c5f 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -985,8 +985,6 @@ const AppViewActionTypes = { SET_APP_VIEWER_HEADER_HEIGHT: "SET_APP_VIEWER_HEADER_HEIGHT", SET_APP_SIDEBAR_PINNED: "SET_APP_SIDEBAR_PINNED", FETCH_PUBLISHED_PAGE_RESOURCES_INIT: "FETCH_PUBLISHED_PAGE_RESOURCES_INIT", - FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS: - "FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS", }; const AppViewActionErrorTypes = { diff --git a/app/client/src/ce/sagas/PageSagas.tsx b/app/client/src/ce/sagas/PageSagas.tsx index aeee2a7b60c4..12e52ba930c5 100644 --- a/app/client/src/ce/sagas/PageSagas.tsx +++ b/app/client/src/ce/sagas/PageSagas.tsx @@ -325,47 +325,12 @@ export function* fetchPageSaga(action: ReduxAction) { } } -export function* updateCanvasLayout(response: FetchPageResponse) { - // Wait for widget config to load before we can get the canvas payload - yield call(waitForWidgetConfigBuild); - // Get Canvas payload - const canvasWidgetsPayload = getCanvasWidgetsPayload(response); - - // resize main canvas - resizePublishedMainCanvasToLowestWidget(canvasWidgetsPayload.widgets); - // Update the canvas - yield put(initCanvasLayout(canvasWidgetsPayload)); - - // Since new page has new layout, we need to generate a data structure - // to compute dynamic height based on the new layout. - yield put(generateAutoHeightLayoutTreeAction(true, true)); -} - -export function* postFetchedPublishedPage( - response: FetchPageResponse, - pageId: string, -) { - // set current page - yield put( - updateCurrentPage( - pageId, - response.data.slug, - response.data.userPermissions, - ), - ); - // Clear any existing caches - yield call(clearEvalCache); - // Set url params - yield call(setDataUrl); - - yield call(updateCanvasLayout, response); -} - export function* fetchPublishedPageSaga( action: ReduxAction, ) { try { - const { bustCache, pageId, pageWithMigratedDsl } = action.payload; + const { bustCache, firstLoad, pageId, pageWithMigratedDsl } = + action.payload; const params = { pageId, bustCache }; const response: FetchPageResponse = yield call( @@ -377,9 +342,41 @@ export function* fetchPublishedPageSaga( const isValidResponse: boolean = yield validateResponse(response); if (isValidResponse) { - yield call(postFetchedPublishedPage, response, pageId); + // Clear any existing caches + yield call(clearEvalCache); + // Set url params + yield call(setDataUrl); + // Wait for widget config to load before we can get the canvas payload + yield call(waitForWidgetConfigBuild); + // Get Canvas payload + const canvasWidgetsPayload = getCanvasWidgetsPayload(response); + + // resize main canvas + resizePublishedMainCanvasToLowestWidget(canvasWidgetsPayload.widgets); + // Update the canvas + yield put(initCanvasLayout(canvasWidgetsPayload)); + // set current page + yield put( + updateCurrentPage( + pageId, + response.data.slug, + response.data.userPermissions, + ), + ); + // dispatch fetch page success yield put(fetchPublishedPageSuccess()); + + // Since new page has new layout, we need to generate a data structure + // to compute dynamic height based on the new layout. + yield put(generateAutoHeightLayoutTreeAction(true, true)); + + /* Currently, All Actions are fetched in initSagas and on pageSwitch we only fetch page + */ + // Hence, if is not isFirstLoad then trigger evaluation with execute pageLoad action + if (!firstLoad) { + yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); + } } } catch (error) { yield put({ @@ -395,9 +392,9 @@ export function* fetchPublishedPageResourcesSaga( action: ReduxAction, ) { try { - const { basePageId, pageId } = action.payload; + const { pageId } = action.payload; - const params = { defaultPageId: basePageId }; + const params = { defaultPageId: pageId }; const initConsolidatedApiResponse: ApiResponse = yield ConsolidatedPageLoadApi.getConsolidatedPageLoadDataView(params); @@ -413,18 +410,10 @@ export function* fetchPublishedPageResourcesSaga( // In future, we can reuse this saga to fetch other resources of the page like actionCollections etc const { publishedActions } = response; - yield call( - postFetchedPublishedPage, - response.pageWithMigratedDsl, - pageId, - ); - - // NOTE: fetchActionsForView is used here to update publishedActions in redux store and not to fetch actions again + // Sending applicationId as empty as we have publishedActions present, + // it won't call the actions view api with applicationId yield put(fetchActionsForView({ applicationId: "", publishedActions })); yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); - yield put({ - type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS, - }); } } catch (error) { yield put({ @@ -1436,13 +1425,21 @@ export function* setupPublishedPageSaga( action: ReduxAction, ) { try { - const { bustCache, pageId, pageWithMigratedDsl } = action.payload; + const { bustCache, firstLoad, pageId, pageWithMigratedDsl } = + action.payload; /* Added the first line for isPageSwitching redux state to be true when page is being fetched to fix scroll position issue. Added the second line for sync call instead of async (due to first line) as it was leading to issue with on page load actions trigger. */ - yield put(fetchPublishedPageAction(pageId, bustCache, pageWithMigratedDsl)); + yield put( + fetchPublishedPageAction( + pageId, + bustCache, + firstLoad, + pageWithMigratedDsl, + ), + ); yield take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS); yield put({ diff --git a/app/client/src/ce/sagas/__tests__/PageSaga.test.ts b/app/client/src/ce/sagas/__tests__/PageSaga.test.ts index 4fb55f74d31b..96dc534cc274 100644 --- a/app/client/src/ce/sagas/__tests__/PageSaga.test.ts +++ b/app/client/src/ce/sagas/__tests__/PageSaga.test.ts @@ -47,6 +47,7 @@ describe("ce/PageSaga", () => { pageWithMigratedDsl: mockResponse.data .pageWithMigratedDsl as FetchPageResponse, bustCache: false, + firstLoad: true, }, }; @@ -56,6 +57,7 @@ describe("ce/PageSaga", () => { fetchPublishedPageAction( action.payload.pageId, action.payload.bustCache, + action.payload.firstLoad, action.payload.pageWithMigratedDsl, ), ) diff --git a/app/client/src/entities/Engine/AppViewerEngine.ts b/app/client/src/entities/Engine/AppViewerEngine.ts index e4cca1b7db6e..d93d7b6cb1a5 100644 --- a/app/client/src/entities/Engine/AppViewerEngine.ts +++ b/app/client/src/entities/Engine/AppViewerEngine.ts @@ -105,7 +105,7 @@ export default class AppViewerEngine extends AppEngine { }), fetchSelectedAppThemeAction(applicationId, currentTheme), fetchAppThemesAction(applicationId, themes), - setupPublishedPage(toLoadPageId, true, pageWithMigratedDsl), + setupPublishedPage(toLoadPageId, true, true, pageWithMigratedDsl), ]; const successActionEffects = [ diff --git a/app/client/src/pages/AppViewer/index.tsx b/app/client/src/pages/AppViewer/index.tsx index abe1ebbf3884..bde4699342c4 100644 --- a/app/client/src/pages/AppViewer/index.tsx +++ b/app/client/src/pages/AppViewer/index.tsx @@ -28,7 +28,10 @@ import { useSelector } from "react-redux"; import BrandingBadge from "./BrandingBadge"; import { setAppViewHeaderHeight } from "actions/appViewActions"; import { CANVAS_SELECTOR } from "constants/WidgetConstants"; -import { fetchPublishedPageResources } from "actions/pageActions"; +import { + setupPublishedPage, + fetchPublishedPageResourcesAction, +} from "actions/pageActions"; import usePrevious from "utils/hooks/usePrevious"; import { getIsBranchUpdated } from "../utils"; import { APP_MODE } from "entities/App"; @@ -162,12 +165,10 @@ function AppViewer(props: Props) { )?.pageId; if (pageId) { - dispatch( - fetchPublishedPageResources({ - basePageId, - pageId, - }), - ); + dispatch(setupPublishedPage(pageId, true)); + + // Used for fetching page resources + dispatch(fetchPublishedPageResourcesAction(basePageId)); } } } diff --git a/app/client/src/reducers/uiReducers/appViewReducer.tsx b/app/client/src/reducers/uiReducers/appViewReducer.tsx index 05ffcdbfcad3..1d3671392f24 100644 --- a/app/client/src/reducers/uiReducers/appViewReducer.tsx +++ b/app/client/src/reducers/uiReducers/appViewReducer.tsx @@ -26,11 +26,6 @@ const appViewReducer = createReducer(initialState, { [ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT]: (state: AppViewReduxState) => { return { ...state, isFetchingPage: true }; }, - [ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_INIT]: ( - state: AppViewReduxState, - ) => { - return { ...state, isFetchingPage: true }; - }, [ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR]: ( state: AppViewReduxState, ) => { @@ -49,14 +44,6 @@ const appViewReducer = createReducer(initialState, { isFetchingPage: false, }; }, - [ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS]: ( - state: AppViewReduxState, - ) => { - return { - ...state, - isFetchingPage: false, - }; - }, [ReduxActionTypes.SET_APP_VIEWER_HEADER_HEIGHT]: ( state: AppViewReduxState, action: ReduxAction, diff --git a/app/server/appsmith-plugins/mysqlPlugin/src/test/java/com/external/plugins/MySqlPluginTest.java b/app/server/appsmith-plugins/mysqlPlugin/src/test/java/com/external/plugins/MySqlPluginTest.java index cd29212ec041..5455415ec627 100755 --- a/app/server/appsmith-plugins/mysqlPlugin/src/test/java/com/external/plugins/MySqlPluginTest.java +++ b/app/server/appsmith-plugins/mysqlPlugin/src/test/java/com/external/plugins/MySqlPluginTest.java @@ -29,6 +29,7 @@ import io.r2dbc.spi.ConnectionFactories; import io.r2dbc.spi.ConnectionFactoryOptions; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -77,6 +78,15 @@ public class MySqlPluginTest { static MySqlPlugin.MySqlPluginExecutor pluginExecutor = new MySqlPlugin.MySqlPluginExecutor(); + ConnectionContext instanceConnectionContext; + + @AfterEach + public void cleanup() { + if (instanceConnectionContext != null && instanceConnectionContext.getConnection() != null) { + instanceConnectionContext.getConnection().close(); + } + } + @SuppressWarnings("rawtypes") // The type parameter for the container type is just itself and is // pseudo-optional. @Container @@ -202,7 +212,12 @@ private static DatasourceConfiguration createDatasourceConfiguration() { @Test public void testConnectMySQLContainer() { - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); StepVerifier.create(connectionContextMono) .assertNext(connectionContext -> { @@ -222,8 +237,12 @@ public void testMySqlNoPasswordExceptionMessage() { Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); - Mono datasourceTestResultMono = - connectionContextMono.flatMap(connectionPool -> pluginExecutor.testDatasource(connectionPool)); + Mono datasourceTestResultMono = connectionContextMono + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }) + .flatMap(connectionPool -> pluginExecutor.testDatasource(connectionPool)); String gateway = mySQLContainer.getContainerInfo().getNetworkSettings().getGateway(); String expectedErrorMessage = new StringBuilder("Access denied for user 'mysql'@'") @@ -244,7 +263,12 @@ public void testConnectMySQLContainerWithInvalidTimezone() { final DatasourceConfiguration dsConfig = createDatasourceConfigForContainerWithInvalidTZ(); dsConfig.setProperties(List.of(new Property("serverTimezone", "UTC"))); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); StepVerifier.create(connectionContextMono) .assertNext(Assertions::assertNotNull) @@ -330,7 +354,12 @@ public void testDatasourceWithNullPassword() { Set output = pluginExecutor.validateDatasource(dsConfig); assertTrue(output.isEmpty()); // test connect - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); StepVerifier.create(connectionContextMono) .assertNext(Assertions::assertNotNull) @@ -356,7 +385,12 @@ public void testDatasourceWithRootUserAndNullPassword() { Set output = pluginExecutor.validateDatasource(dsConfig); assertTrue(output.isEmpty()); // test connect - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); StepVerifier.create(connectionContextMono) .assertNext(Assertions::assertNotNull) @@ -371,7 +405,12 @@ public void testDatasourceWithRootUserAndNullPassword() { @Test public void testExecute() { - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("show databases"); @@ -391,7 +430,12 @@ public void testExecute() { @Test public void testExecuteWithFormattingWithShowCmd() { dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("show\n\tdatabases"); @@ -413,7 +457,12 @@ public void testExecuteWithFormattingWithShowCmd() { @Test public void testExecuteWithFormattingWithSelectCmd() { dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("select\n\t*\nfrom\nusers where id=1"); @@ -452,7 +501,12 @@ public void testExecuteWithFormattingWithSelectCmd() { @Test public void testExecuteWithLongRunningQuery() { - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT SLEEP(20);"); @@ -475,6 +529,7 @@ public void testStaleConnectionCheck() { actionConfiguration.setBody("show databases"); ConnectionContext connectionContext = pluginExecutor.datasourceCreate(dsConfig).block(); + instanceConnectionContext = connectionContext; Flux resultFlux = Mono.from((connectionContext.getConnection()).disposeLater()) .thenMany(pluginExecutor.executeParameterized( connectionContext, new ExecuteActionDTO(), dsConfig, actionConfiguration)); @@ -487,7 +542,12 @@ public void testStaleConnectionCheck() { @Test public void testAliasColumnNames() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT id as user_id FROM users WHERE id = 1"); @@ -513,7 +573,12 @@ public void testAliasColumnNames() { @Test public void testPreparedStatementErrorWithIsKeyword() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); /** @@ -563,7 +628,12 @@ public void testPreparedStatementWithRealTypes() { .blockLast(); // wait until completion of all the queries DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); /** @@ -639,7 +709,12 @@ public void testPreparedStatementWithBooleanType() { .blockLast(); // wait until completion of all the queries DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT id FROM test_boolean_type WHERE c_boolean={{binding1}};"); @@ -679,7 +754,12 @@ public void testPreparedStatementWithBooleanType() { @Test public void testExecuteWithPreparedStatement() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT id FROM users WHERE id = {{binding1}} limit 1 offset {{binding2}};"); @@ -760,7 +840,12 @@ public void testExecuteWithPreparedStatement() { @Test public void testExecuteDataTypes() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT * FROM users WHERE id = 1"); @@ -905,7 +990,12 @@ public void testExecuteDataTypesExtensive() throws AppsmithPluginException { } private void testExecute(String query, String expectedResult) { - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody(query); Mono executeMono = connectionContextMono.flatMap(conn -> @@ -928,7 +1018,10 @@ public void testStructure() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); Mono structureMono = pluginExecutor .datasourceCreate(dsConfig) - .flatMap(connection -> pluginExecutor.getStructure(connection, dsConfig)); + .flatMap(connection -> { + instanceConnectionContext = connection; + return pluginExecutor.getStructure(connection, dsConfig); + }); StepVerifier.create(structureMono) .assertNext(structure -> { @@ -1053,8 +1146,12 @@ public void testSslDisabled() { DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration(); datasourceConfiguration.getConnection().getSsl().setAuthType(SSLDetails.AuthType.DISABLED); - Mono> connectionContextMono = - pluginExecutor.datasourceCreate(datasourceConfiguration); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(datasourceConfiguration) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); Mono executeMono = connectionContextMono.flatMap(conn -> pluginExecutor.executeParameterized(conn, new ExecuteActionDTO(), dsConfig, actionConfiguration)); StepVerifier.create(executeMono) @@ -1101,8 +1198,12 @@ public void testSslDefault() { DatasourceConfiguration datasourceConfiguration = createDatasourceConfiguration(); datasourceConfiguration.getConnection().getSsl().setAuthType(SSLDetails.AuthType.DEFAULT); - Mono> connectionContextMono = - pluginExecutor.datasourceCreate(datasourceConfiguration); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); Mono executeMono = connectionContextMono.flatMap(conn -> pluginExecutor.executeParameterized(conn, new ExecuteActionDTO(), dsConfig, actionConfiguration)); StepVerifier.create(executeMono) @@ -1120,7 +1221,12 @@ public void testSslDefault() { @Test public void testDuplicateColumnNames() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT id, username as id, password, email as password FROM users WHERE id = 1"); @@ -1157,7 +1263,12 @@ public void testDuplicateColumnNames() { @Test public void testExecuteDescribeTableCmd() { dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("describe users"); @@ -1180,7 +1291,12 @@ public void testExecuteDescribeTableCmd() { @Test public void testExecuteDescTableCmd() { dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("desc users"); @@ -1205,7 +1321,12 @@ public void testNullObjectWithPreparedStatement() { pluginExecutor = spy(new MySqlPlugin.MySqlPluginExecutor()); doReturn(false).when(pluginExecutor).isIsOperatorUsed(any()); DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT * from (\n" + "\tselect 'Appsmith' as company_name, true as open_source\n" @@ -1252,7 +1373,12 @@ public void testNullObjectWithPreparedStatement() { @Test public void testNullAsStringWithPreparedStatement() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT * from (\n" + "\tselect 'Appsmith' as company_name, true as open_source\n" @@ -1300,7 +1426,12 @@ public void testNullAsStringWithPreparedStatement() { @Test public void testNumericValuesHavingLeadingZeroWithPreparedStatement() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT {{binding1}} as numeric_string;"); @@ -1342,7 +1473,12 @@ public void testNumericValuesHavingLeadingZeroWithPreparedStatement() { @Test public void testLongValueWithPreparedStatement() { DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMono = pluginExecutor.datasourceCreate(dsConfig); + Mono> connectionContextMono = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }); ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("select id from users LIMIT {{binding1}}"); @@ -1383,8 +1519,13 @@ public void testLongValueWithPreparedStatement() { @Test public void testDatasourceDestroy() { dsConfig = createDatasourceConfiguration(); - Mono> connectionContextMonoCache = - pluginExecutor.datasourceCreate(dsConfig).cache(); + Mono> connectionContextMonoCache = pluginExecutor + .datasourceCreate(dsConfig) + .map(connectionPool -> { + instanceConnectionContext = connectionPool; + return connectionPool; + }) + .cache(); Mono testConnResultMono = connectionContextMonoCache.flatMap(conn -> pluginExecutor.testDatasource(conn)); Mono, DatasourceTestResult>> zipMono = @@ -1417,8 +1558,10 @@ public void testExecuteCommon_queryWithComments_callValidationCallsAfterRemoving MySqlPlugin.MySqlPluginExecutor spyPlugin = spy(pluginExecutor); DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - ConnectionContext connectionContextMono = + ConnectionContext connectionContext = pluginExecutor.datasourceCreate(dsConfig).block(); + instanceConnectionContext = connectionContext; + ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setBody("SELECT id FROM users WHERE -- IS operator\nid = 1 limit 1;"); @@ -1428,7 +1571,7 @@ public void testExecuteCommon_queryWithComments_callValidationCallsAfterRemoving HashMap requestData = new HashMap<>(); Mono resultMono = - spyPlugin.executeCommon(connectionContextMono, actionConfiguration, TRUE, null, null, requestData); + spyPlugin.executeCommon(connectionContext, actionConfiguration, TRUE, null, null, requestData); StepVerifier.create(resultMono) .assertNext(result -> { diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/RedisConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/RedisConfig.java index 8b255dea33f5..6a8a6cc6c7b2 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/RedisConfig.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/RedisConfig.java @@ -215,7 +215,7 @@ public byte[] serialize(Object t) { boolean allValuesAreClientDTOs = true; for (final LoginSource loginSource : LoginSource.oauthSources) { final Object value = data.get(loginSource.name().toLowerCase()); - if (value != null && !(value instanceof OAuth2AuthorizedClientDTO)) { + if (value != null && !(value instanceof OAuth2AuthorizedClient)) { allValuesAreClientDTOs = false; break; } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration063CacheBustSpringBoot3_3.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration063CacheBustSpringBoot3_3.java new file mode 100644 index 000000000000..61936af057f5 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration063CacheBustSpringBoot3_3.java @@ -0,0 +1,38 @@ +package com.appsmith.server.migrations.db.ce; + +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.ReactiveRedisOperations; +import org.springframework.data.redis.core.script.RedisScript; +import reactor.core.publisher.Flux; + +@RequiredArgsConstructor +@Slf4j +@ChangeUnit(order = "063", id = "reset_session_oauth2_spring_3_3") +public class Migration063CacheBustSpringBoot3_3 { + + private final ReactiveRedisOperations reactiveRedisOperations; + + @RollbackExecution + public void rollbackExecution() {} + + @Execution + public void execute() { + doClearRedisOAuth2AuthClientKeys(reactiveRedisOperations); + } + + public static void doClearRedisOAuth2AuthClientKeys( + ReactiveRedisOperations reactiveRedisOperations) { + final String authorizedClientsKey = + "sessionAttr:org.springframework.security.oauth2.client.web.server.WebSessionServerOAuth2AuthorizedClientRepository.AUTHORIZED_CLIENTS"; + final String script = + "for _,k in ipairs(redis.call('keys','spring:session:sessions:*')) do local fieldExists = redis.call('hexists', k, '" + + authorizedClientsKey + "'); if fieldExists == 1 then redis.call('del', k) end end"; + final Flux flushdb = reactiveRedisOperations.execute(RedisScript.of(script)); + + flushdb.blockLast(); + } +} diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/connectionpoolconfig/configurations/ConnectionPoolConfigCETest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/connectionpoolconfig/configurations/ConnectionPoolConfigCETest.java index e2a7bed2a02f..6f9cbd498a99 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/connectionpoolconfig/configurations/ConnectionPoolConfigCETest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/connectionpoolconfig/configurations/ConnectionPoolConfigCETest.java @@ -18,7 +18,7 @@ public class ConnectionPoolConfigCETest { @Test public void verifyGetMaxConnectionPoolSizeProvidesDefaultValue() { // this is same as default - Integer connectionPoolMaxSize = 20; + Integer connectionPoolMaxSize = 5; Mono connectionPoolMaxSizeMono = connectionPoolConfig.getMaxConnectionPoolSize(); StepVerifier.create(connectionPoolMaxSizeMono).assertNext(poolSize -> {