Skip to content

Commit

Permalink
feat: adding branch persistence (#36622)
Browse files Browse the repository at this point in the history
## Description
Persisting latest branch. Re-opening the branch opens it in the latest
branch

Fixes #30321

## Automation

/ok-to-test tags="@tag.Git"

### 🔍 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/11325191494>
> Commit: 570c6fb
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11325191494&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Git`
> Spec:
> <hr>Mon, 14 Oct 2024 10:27:07 UTC
<!-- 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

- **New Features**
- Enhanced Git branch management functionality in the ApplicationCard
component.
- New utility functions for storing and retrieving the latest Git
branch.
	- Improved error handling and response management for Git operations.
	- Added user-specific tracking for Git branches in the AppEditorEngine.
	- Introduced a new feature flag for Git branch persistence.
- New Cypress test suite to verify Git branch persistence after
switching branches.
- Added methods for asserting branch visibility and URL consistency in
the GitSync class.

- **Bug Fixes**
- Refined error handling for various sagas, ensuring accurate error
dispatching and user feedback.

- **Documentation**
- Updated function signatures and error handling descriptions in the
initialization and Git synchronization processes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
brayn003 authored Oct 14, 2024
1 parent 97f2560 commit f572d6d
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags";
import {
agHelper,
gitSync,
homePage,
} from "../../../../support/Objects/ObjectsCore";

let wsName: string;
let appName: string;
let repoName: string;

describe(
"Git Persist Branch",
{
tags: ["@tag.Git", "@tag.GitPersistBranch"],
},
function () {
before(() => {
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
wsName = "GitPB-" + uid;
appName = "GitPB1-" + uid;
homePage.CreateNewWorkspace(wsName, true);
homePage.CreateAppInWorkspace(wsName, appName);
gitSync.CreateNConnectToGit("test-git-perssit-branch", true, true);
cy.get("@gitRepoName").then((resRepoName) => {
repoName = resRepoName.toString();
homePage.NavigateToHome();
});
});
});
it("Check if branch persist after changing branch and exiting the app", function () {
featureFlagIntercept({ release_git_persist_branch_enabled: true }, true);
homePage.EditAppFromAppHover(appName);
gitSync.CreateGitBranch("b1", false);
cy.get("@gitbranchName").then((resBranchName) => {
const branchName = resBranchName.toString();
homePage.NavigateToHome();
homePage.EditAppFromAppHover(appName);
gitSync.AssertBranchName(branchName);
gitSync.AssertBranchNameInUrl(branchName);
});
});
},
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { featureFlagIntercept } from "../../../../../support/Objects/FeatureFlags";
import * as _ from "../../../../../support/Objects/ObjectsCore";
import EditorNavigation, {
EntityType,
Expand Down
12 changes: 12 additions & 0 deletions app/client/cypress/support/Pages/GitSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,4 +453,16 @@ export class GitSync {
}
this.CloseGitSyncModal();
}

public AssertBranchName(branch: string) {
this.agHelper.AssertElementVisibility(this._branchButton);
this.agHelper.AssertContains(branch);
}

public AssertBranchNameInUrl(branch: string) {
cy.location("search")
.then((searchParams) => new URLSearchParams(searchParams))
.invoke("get", "branch")
.should("equal", branch);
}
}
2 changes: 2 additions & 0 deletions app/client/src/ce/entities/FeatureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const FEATURE_FLAG = {
rollout_side_by_side_enabled: "rollout_side_by_side_enabled",
release_layout_conversion_enabled: "release_layout_conversion_enabled",
release_anvil_toggle_enabled: "release_anvil_toggle_enabled",
release_git_persist_branch_enabled: "release_git_persist_branch_enabled",
release_ide_animations_enabled: "release_ide_animations_enabled",
} as const;

Expand Down Expand Up @@ -71,6 +72,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
rollout_side_by_side_enabled: false,
release_layout_conversion_enabled: false,
release_anvil_toggle_enabled: false,
release_git_persist_branch_enabled: false,
release_ide_animations_enabled: false,
};

Expand Down
31 changes: 29 additions & 2 deletions app/client/src/entities/Engine/AppEditorEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ import {
reportSWStatus,
waitForWidgetConfigBuild,
} from "sagas/InitSagas";
import { getCurrentGitBranch } from "selectors/gitSyncSelectors";
import {
getCurrentGitBranch,
isGitPersistBranchEnabledSelector,
} from "selectors/gitSyncSelectors";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import history from "utils/history";
import type { AppEnginePayload } from ".";
Expand All @@ -50,7 +53,7 @@ import {
} from "ee/sagas/userSagas";
import { getFirstTimeUserOnboardingComplete } from "selectors/onboardingSelectors";
import { isAirgapped } from "ee/utils/airgapHelpers";
import { getAIPromptTriggered } from "utils/storage";
import { getAIPromptTriggered, setLatestGitBranchInLocal } from "utils/storage";
import { trackOpenEditorTabs } from "../../utils/editor/browserTabsTracking";
import { EditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
import { waitForFetchEnvironments } from "ee/sagas/EnvironmentSagas";
Expand All @@ -68,6 +71,9 @@ import {
import { getCurrentApplication } from "ee/selectors/applicationSelectors";
import type { Span } from "@opentelemetry/api";
import { endSpan, startNestedSpan } from "UITelemetry/generateTraces";
import { getCurrentUser } from "selectors/usersSelectors";
import type { User } from "constants/userConstants";
import log from "loglevel";

export default class AppEditorEngine extends AppEngine {
constructor(mode: APP_MODE) {
Expand Down Expand Up @@ -269,6 +275,27 @@ export default class AppEditorEngine extends AppEngine {
getCurrentApplication,
);

const isGitPersistBranchEnabled: boolean = yield select(
isGitPersistBranchEnabledSelector,
);

if (isGitPersistBranchEnabled) {
const currentUser: User = yield select(getCurrentUser);
const currentBranch: string = yield select(getCurrentGitBranch);

if (currentUser?.email && currentApplication?.baseId && currentBranch) {
yield setLatestGitBranchInLocal(
currentUser.email,
currentApplication.baseId,
currentBranch,
);
} else {
log.error(
`There was an error setting the latest git branch in local - userEmail: ${!!currentUser?.email}, applicationId: ${currentApplication?.baseId}, branch: ${currentBranch}`,
);
}
}

const [isAnotherEditorTabOpen, currentTabs] = yield call(
trackOpenEditorTabs,
currentApplication.id,
Expand Down
59 changes: 44 additions & 15 deletions app/client/src/pages/Applications/ApplicationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ import { getCurrentUser } from "actions/authActions";
import Card, { ContextMenuTrigger } from "components/common/Card";
import { generateEditedByText } from "./helpers";
import { noop } from "lodash";
import { getLatestGitBranchFromLocal } from "utils/storage";
import { getCurrentUser as getCurrentUserSelector } from "selectors/usersSelectors";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";

interface ApplicationCardProps {
application: ApplicationPayload;
Expand Down Expand Up @@ -98,6 +102,7 @@ export function ApplicationCard(props: ApplicationCardProps) {
const theme = useContext(ThemeContext);
const isSavingName = useSelector(getIsSavingAppName);
const isErroredSavingName = useSelector(getIsErroredSavingAppName);
const currentUser = useSelector(getCurrentUserSelector);
const initialsAndColorCode = getInitialsAndColorCode(
props.application.name,
theme.colors.appCardColors,
Expand All @@ -116,7 +121,40 @@ export function ApplicationCard(props: ApplicationCardProps) {
const dispatch = useDispatch();

const applicationId = props.application?.id;
const baseApplicationId = props.application?.baseId;
const showGitBadge = props.application?.gitApplicationMetadata?.branchName;
const [editorParams, setEditorParams] = useState({});
const isGitPersistBranchEnabled = useFeatureFlag(
FEATURE_FLAG.release_git_persist_branch_enabled,
);

useEffect(() => {
(async () => {
const storedLatestBranch = await getLatestGitBranchFromLocal(
currentUser?.email ?? "",
baseApplicationId,
);

if (isGitPersistBranchEnabled && storedLatestBranch) {
setEditorParams({ branch: storedLatestBranch });
} else if (showGitBadge) {
setEditorParams({ branch: showGitBadge });
}
})();
}, [
baseApplicationId,
currentUser?.email,
showGitBadge,
isGitPersistBranchEnabled,
]);

const viewerParams = useMemo(() => {
if (showGitBadge) {
return { branch: showGitBadge };
} else {
return {};
}
}, [showGitBadge]);

useEffect(() => {
let colorCode;
Expand Down Expand Up @@ -273,15 +311,6 @@ export function ApplicationCard(props: ApplicationCardProps) {
initials += props.application.name[1].toUpperCase() || "";
}

// should show correct branch of application when edit mode
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const params: any = {};

if (showGitBadge) {
params.branch = showGitBadge;
}

const handleMenuOnClose = (open: boolean) => {
if (!open && !isDeleting) {
setIsMenuOpen(false);
Expand Down Expand Up @@ -437,16 +466,16 @@ export function ApplicationCard(props: ApplicationCardProps) {

if (!basePageId) return "";

return builderURL({ basePageId, params });
}, [props.application.defaultBasePageId, params]);
return builderURL({ basePageId, params: editorParams });
}, [props.application.defaultBasePageId, editorParams]);

const viewModeURL = useMemo(() => {
const basePageId = props.application.defaultBasePageId;

if (!basePageId) return "";

return viewerURL({ basePageId, params });
}, [props.application.defaultBasePageId, params]);
return viewerURL({ basePageId, params: viewerParams });
}, [props.application.defaultBasePageId, viewerParams]);

const launchApp = useCallback(() => {
setURLParams();
Expand All @@ -463,11 +492,11 @@ export function ApplicationCard(props: ApplicationCardProps) {
history.push(
viewerURL({
basePageId: props.application.defaultBasePageId,
params,
params: viewerParams,
}),
);
dispatch(getCurrentUser());
}, [props.application.defaultPageId]);
}, [dispatch, props.application.defaultBasePageId, viewerParams]);

return (
<Card
Expand Down
1 change: 0 additions & 1 deletion app/client/src/sagas/InitSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ export function* getInitResponses({
}: {
applicationId?: string;
basePageId?: string;
branch?: string;
mode?: APP_MODE;
shouldInitialiseUserDetails?: boolean;
// TODO: Fix this the next time the file is edited
Expand Down
6 changes: 6 additions & 0 deletions app/client/src/selectors/gitSyncSelectors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getCurrentApplication,
} from "ee/selectors/applicationSelectors";
import type { Branch } from "entities/GitSync";
import { selectFeatureFlags } from "ee/selectors/featureFlagsSelectors";

export const getGitSyncState = (state: AppState): GitSyncReducerState =>
state.ui.gitSync;
Expand Down Expand Up @@ -280,3 +281,8 @@ export const isGitSettingsModalOpenSelector = (state: AppState) =>

export const activeGitSettingsModalTabSelector = (state: AppState) =>
state.ui.gitSync.activeGitSettingsModalTab;

export const isGitPersistBranchEnabledSelector = createSelector(
selectFeatureFlags,
(featureFlags) => featureFlags.release_git_persist_branch_enabled ?? false,
);
50 changes: 50 additions & 0 deletions app/client/src/utils/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const STORAGE_KEYS: {
CODE_WIDGET_NAVIGATION_USED: "CODE_WIDGET_NAVIGATION_USED",
OVERRIDDEN_FEATURE_FLAGS: "OVERRIDDEN_FEATURE_FLAGS",
ACTION_TEST_PAYLOAD: "ACTION_TEST_PAYLOAD",
LATEST_GIT_BRANCH: "LATEST_GIT_BRANCH",
};

const store = localforage.createInstance({
Expand Down Expand Up @@ -1087,3 +1088,52 @@ export const storeActionTestPayload = async (payload: {
return false;
}
};

export const setLatestGitBranchInLocal = async (
userEmail: string,
baseApplicationId: string,
branch: string,
) => {
try {
const storedBranches: Record<
string,
Record<string, string>
> = (await store.getItem(STORAGE_KEYS.LATEST_GIT_BRANCH)) ?? {};
const userBranches = storedBranches?.[userEmail] ?? {};
const newBranches = {
...(storedBranches ?? {}),
[userEmail]: {
...userBranches,
[baseApplicationId]: branch,
},
};

await store.setItem(STORAGE_KEYS.LATEST_GIT_BRANCH, newBranches);

return true;
} catch (error) {
log.error("An error occurred while setting LATEST_GIT_BRANCH");
log.error(error);

return false;
}
};

export const getLatestGitBranchFromLocal = async (
userEmail: string,
baseApplicationId: string,
) => {
try {
const storedBranches: Record<string, Record<string, string>> | null =
await store.getItem(STORAGE_KEYS.LATEST_GIT_BRANCH);
const userBranches = storedBranches?.[userEmail] ?? {};
const branch = userBranches?.[baseApplicationId] ?? null;

return branch;
} catch (error) {
log.error("An error occurred while fetching LATEST_GIT_BRANCH");
log.error(error);

return null;
}
};

0 comments on commit f572d6d

Please sign in to comment.