Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/client/src/actions/pageActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type { ApiResponse } from "api/ApiResponses";
import type { EvaluationReduxAction } from "./EvaluationReduxActionTypes";
import { appsmithTelemetry } from "instrumentation";
import type { NavigateToAnotherPagePayload } from "sagas/ActionExecution/NavigateActionSaga/types";
import type { Path } from "history";

export interface FetchPageListPayload {
applicationId: string;
Expand Down Expand Up @@ -699,7 +700,7 @@ export const setupPublishedPage = (
});

export const navigateToAnotherPage = (
payload: NavigateToAnotherPagePayload,
payload: NavigateToAnotherPagePayload | Path,
) => ({
type: ReduxActionTypes.NAVIGATE_TO_ANOTHER_PAGE,
payload,
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/pages/Applications/CreateNewAppsOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ const CreateNewAppsOption = ({
})),
);

urlBuilder.setCurrentBasePageId(application.pages[0].baseId);

startWithData();
}
}, [application]);
Expand Down
15 changes: 10 additions & 5 deletions app/client/src/pages/AppIDE/components/PageList/PageEntity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ import {
import { PERMISSION_TYPE, isPermitted } from "ee/utils/permissionHelpers";
import { getCurrentApplication } from "ee/selectors/applicationSelectors";
import type { DefaultRootState } from "react-redux";
import { updatePageAction } from "actions/pageActions";
import { navigateToAnotherPage, updatePageAction } from "actions/pageActions";
import { useGetPageFocusUrl } from "./hooks/useGetPageFocusUrl";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions";
import history, { NavigationMethod } from "utils/history";
import { EntityItem } from "@appsmith/ads";
import { useNameEditorState } from "IDE/hooks/useNameEditorState";
import { useValidateEntityName } from "IDE";
import { noop } from "lodash";
import { NavigationMethod } from "utils/history";

export const PageEntity = ({
onClick,
Expand Down Expand Up @@ -90,9 +90,14 @@ export const PageEntity = ({
toUrl: navigateToUrl,
});
dispatch(toggleInOnboardingWidgetSelection(true));
history.push(navigateToUrl, {
invokedBy: NavigationMethod.EntityExplorer,
});
dispatch(
navigateToAnotherPage({
pageURL: navigateToUrl,
state: {
invokedBy: NavigationMethod.EntityExplorer,
},
}),
);

if (onClick) {
onClick();
Expand Down
24 changes: 24 additions & 0 deletions app/client/src/sagas/ActionExecution/NavigateActionSaga/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,24 @@ export function* navigateToAnyPageInApplication(
yield call(pushToHistory, action.payload);
}

/**
* Pushes navigation state to browser history after executing page unload actions.
*
* This function handles three different variants of history.push() calls to maintain
* backward compatibility with existing code patterns:
*
* 1. String payload: Direct path navigation without query or state
* - Uses: history.push(path)
*
* 2. Payload with state but no query: Navigation with state object
* - Uses: history.push(pageURL, state)
*
* 3. Payload with query and/or state: Full navigation with all parameters
* - Uses: history.push({ pathname, search, state })
*
* These variants exist to conform to how the code was working previously and
* ensure consistent behavior across different navigation scenarios.
*/
export function* pushToHistory(payload: NavigateToAnotherPagePayload | Path) {
yield put({
type: ReduxActionTypes.EXECUTE_PAGE_UNLOAD_ACTIONS,
Expand All @@ -130,6 +148,12 @@ export function* pushToHistory(payload: NavigateToAnotherPagePayload | Path) {
return;
}

if (!payload.query && payload.state) {
history.push(payload.pageURL, payload.state);

return;
}

const historyState: LocationDescriptor<AppsmithLocationState> = {
pathname: payload.pageURL,
search: payload.query,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import type { AppsmithLocationState } from "utils/history";

export interface NavigateToAnotherPagePayload {
pageURL: string;
query: string;
state: AppsmithLocationState;
query?: string;
state?: AppsmithLocationState;
}
10 changes: 7 additions & 3 deletions app/client/src/sagas/ActionExecution/PluginActionSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1702,14 +1702,18 @@ export function* executePageUnloadActionsSaga() {
const span = startRootSpan("executePageUnloadActionsSaga");

try {
const pageActions: Action[] = yield select(getLayoutOnUnloadActions);
const actionCount = pageActions.length;
const pageOnUnloadActions: Action[] = yield select(
getLayoutOnUnloadActions,
);
const actionCount = pageOnUnloadActions.length;

setAttributesToSpan(span, { numActions: actionCount });

// Execute unload actions in parallel batches
yield all(
pageActions.map((action) => call(executeOnPageUnloadJSAction, action)),
pageOnUnloadActions.map((action) =>
call(executeOnPageUnloadJSAction, action),
),
);

// Publish success event after all actions are executed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { DefaultRootState } from "react-redux";
import { getLayoutOnUnloadActions } from "selectors/editorSelectors";

describe("getLayoutOnUnloadActions", () => {
it("should filter actions by current page ID", () => {
const state = {
entities: {
pageList: { currentPageId: "page1" },
jsActions: [
{
isLoading: false,
config: {
id: "collection1",
pageId: "page1",
actions: [
{
id: "action1",
pageId: "page1",
runBehaviour: "ON_PAGE_UNLOAD",
name: "myFun1",
fullyQualifiedName: "JSObject1.myFun1",
collectionId: "collection1",
},
{
id: "action3",
pageId: "page1",
runBehaviour: "ON_PAGE_LOAD",
name: "myFun2",
fullyQualifiedName: "JSObject1.myFun2",
collectionId: "collection1",
},
],
},
},
{
isLoading: false,
config: {
id: "collection2",
pageId: "page2",
actions: [
{
id: "action2",
pageId: "page2",
runBehaviour: "ON_PAGE_UNLOAD",
name: "myFun1",
fullyQualifiedName: "JSObject2.myFun1",
collectionId: "collection2",
},
],
},
},
],
},
};

const result = getLayoutOnUnloadActions(
state as unknown as DefaultRootState,
);

expect(result).toEqual([
{
id: "action1",
pageId: "page1",
runBehaviour: "ON_PAGE_UNLOAD",
name: "myFun1",
fullyQualifiedName: "JSObject1.myFun1",
collectionId: "collection1",
},
]);
});
});
18 changes: 10 additions & 8 deletions app/client/src/selectors/editorSelectors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,21 @@ export const getPageSavingError = (state: DefaultRootState) => {
return state.ui.editor.loadingStates.savingError;
};

export const getCurrentPageId = (state: DefaultRootState) =>
state.entities.pageList.currentPageId;

export const getLayoutOnLoadActions = (state: DefaultRootState) =>
state.ui.editor.pageActions || [];

export const getLayoutOnUnloadActions = createSelector(
getCurrentPageId,
getAllJSCollectionActions,
(jsActions) => {
return jsActions.filter((action) => {
return action.runBehaviour === ActionRunBehaviour.ON_PAGE_UNLOAD;
});
},
(currentPageId, jsActions) =>
jsActions.filter(
(action) =>
action.runBehaviour === ActionRunBehaviour.ON_PAGE_UNLOAD &&
action.pageId === currentPageId,
),
);

export const getLayoutOnLoadIssues = (state: DefaultRootState) => {
Expand Down Expand Up @@ -167,9 +172,6 @@ export const getPageByBaseId = (basePageId: string) =>
pages.find((page) => page.basePageId === basePageId),
);

export const getCurrentPageId = (state: DefaultRootState) =>
state.entities.pageList.currentPageId;

export const getCurrentBasePageId = (state: DefaultRootState) =>
state.entities.pageList.currentBasePageId;

Expand Down
29 changes: 24 additions & 5 deletions deploy/docker/fs/opt/appsmith/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -359,14 +359,17 @@ setup-custom-ca-certificates() (
local stacks_ca_certs_path="$stacks_path/ca-certs"
local store="$TMP/cacerts"
local opts_file="$TMP/java-cacerts-opts"
local temp_cert_dir="$TMP/ca-certs-temp"

rm -f "$store" "$opts_file"
rm -rf "$temp_cert_dir"
mkdir -p "$temp_cert_dir"

if [[ -n "$(ls "$stacks_ca_certs_path"/*.pem 2>/dev/null)" ]]; then
tlog "Looks like you have some '.pem' files in your 'ca-certs' folder. Please rename them to '.crt' to be picked up automatically.".
fi

if ! [[ -d "$stacks_ca_certs_path" && "$(find "$stacks_ca_certs_path" -maxdepth 1 -type f -name '*.crt' | wc -l)" -gt 0 ]]; then
if ! [[ -d "$stacks_ca_certs_path" && "$(find "$stacks_ca_certs_path" -maxdepth 1 \( -type f -name '*.crt' -o -type l -name '*.crt' \) | wc -l)" -gt 0 ]]; then
tlog "No custom CA certificates found."
return
fi
Expand All @@ -378,15 +381,31 @@ setup-custom-ca-certificates() (
-srcstorepass changeit \
-deststorepass changeit

# Add the custom CA certificates to the store.
find -L "$stacks_ca_certs_path" -maxdepth 1 -type f -name '*.crt' \
-print \
-exec keytool -import -alias '{}' -noprompt -keystore "$store" -file '{}' -storepass changeit ';'
# Split every .crt file (bundle or single) into individual certs
cert_index=0
while read -r cert_file; do
awk -v prefix="$temp_cert_dir/cert" -v ext=".crt" -v idx="$cert_index" '
BEGIN {n=0}
/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/ {
print > (prefix idx "_" n ext)
if (/-----END CERTIFICATE-----/) n++
}
' "$cert_file"
cert_index=$((cert_index + 1))
done < <(find -L "$stacks_ca_certs_path" -maxdepth 1 -type f -o -type l -name '*.crt')

# Import all certificates from the temp directory
find "$temp_cert_dir" -type f -name '*.crt' | while read -r cert_file; do
keytool -import -alias "$(basename "$cert_file")" -noprompt -keystore "$store" -file "$cert_file" -storepass changeit
done

{
echo "-Djavax.net.ssl.trustStore=$store"
echo "-Djavax.net.ssl.trustStorePassword=changeit"
} > "$opts_file"

# Cleanup
rm -rf "$temp_cert_dir"
)

configure_supervisord() {
Expand Down
Loading