From d24a0bf4b90a8d6b843290d021249a2381e8e493 Mon Sep 17 00:00:00 2001 From: Rudraprasad Das Date: Fri, 3 Nov 2023 22:43:36 +0530 Subject: [PATCH] feat: branch protection (#28526) - Adds server endpoints for getting and setting protected branches - Adds protected canvas view for branch protection - Adds default branch and protected branch in git modal settings Fixes #28434, #28056 Protected View - image Branch Protection Settings - image - New feature (non-breaking change which adds functionality) > > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > > Add Testsmith test cases links that relate to this PR > > > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Nayan --- app/client/src/actions/gitSyncActions.ts | 35 +++++ app/client/src/ce/constants/messages.ts | 5 +- app/client/src/ce/entities/FeatureFlag.ts | 3 - app/client/src/pages/Editor/EditorHeader.tsx | 1 + .../WidgetsEditor/MainContainerWrapper.tsx | 5 +- .../Editor/gitSync/QuickGitActions/index.tsx | 2 +- .../Editor/gitSync/Tabs/ConnectionSuccess.tsx | 129 +++++------------- .../Tabs/GitSettings/GitDefaultBranch.tsx | 43 +++--- .../Tabs/GitSettings/GitProtectedBranches.tsx | 45 ++---- app/client/src/sagas/GitSyncSagas.ts | 61 ++------- .../server/services/ce/GitServiceCEImpl.java | 5 +- .../server/services/ce/GitServiceCETest.java | 31 +---- 12 files changed, 125 insertions(+), 240 deletions(-) diff --git a/app/client/src/actions/gitSyncActions.ts b/app/client/src/actions/gitSyncActions.ts index c08068a911..9ea9d84675 100644 --- a/app/client/src/actions/gitSyncActions.ts +++ b/app/client/src/actions/gitSyncActions.ts @@ -474,6 +474,25 @@ export const fetchGitProtectedBranchesInit = () => { }; }; +export const fetchGitProtectedBranchesSuccess = ( + protectedBranches: string[], +) => { + return { + type: ReduxActionTypes.GIT_FETCH_PROTECTED_BRANCHES_SUCCESS, + payload: { protectedBranches }, + }; +}; + +export const fetchGitProtectedBranchesError = ( + error: any, + show: boolean = true, +) => { + return { + type: ReduxActionTypes.GIT_FETCH_PROTECTED_BRANCHES_ERROR, + payload: { error, show }, + }; +}; + export const updateGitProtectedBranchesInit = (payload: { protectedBranches: string[]; }) => { @@ -482,3 +501,19 @@ export const updateGitProtectedBranchesInit = (payload: { payload, }; }; + +export const updateGitProtectedBranchesSuccess = () => { + return { + type: ReduxActionTypes.GIT_UPDATE_PROTECTED_BRANCHES_SUCCESS, + }; +}; + +export const updateGitProtectedBranchesError = ( + error: any, + show: boolean = true, +) => { + return { + type: ReduxActionTypes.GIT_UPDATE_PROTECTED_BRANCHES_ERROR, + payload: { error, show }, + }; +}; diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 958fa34d65..6f77ffceee 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -508,7 +508,6 @@ export const PAGE_SERVER_UNAVAILABLE_ERROR_MESSAGES = ( export const POST = () => "Post"; export const CANCEL = () => "Cancel"; export const REMOVE = () => "Remove"; -export const CREATE = () => "Create"; // Showcase Carousel export const NEXT = () => "NEXT"; @@ -1038,7 +1037,7 @@ export const PREVIOUS_STEP = () => "Previous step"; export const GIT_CONNECT_SUCCESS_TITLE = () => "Successfully connected to your Git remote repository"; export const GIT_CONNECT_SUCCESS_MESSAGE = () => - "Now you can start collaborating with your team members by committing, merging and deploying your app"; + "Right now, {branch} is set as the default branch and it is protected."; export const START_USING_GIT = () => "Start using Git"; export const OPEN_GIT_SETTINGS = () => "Open Git settings"; export const GIT_AUTHOR = () => "Git author"; @@ -1087,8 +1086,6 @@ export const GO_TO_SETTINGS = () => "Go to settings"; export const NOW_PROTECT_BRANCH = () => "You can now protect your default branch."; export const APPSMITH_ENTERPRISE = () => "Appsmith Enterprise"; -export const PROTECT_BRANCH_SUCCESS = () => "Changed protected branches"; -export const UPDATE_DEFAULT_BRANCH_SUCCESS = () => "Updated default branch"; // Git Branch Protection end export const NAV_DESCRIPTION = () => diff --git a/app/client/src/ce/entities/FeatureFlag.ts b/app/client/src/ce/entities/FeatureFlag.ts index 06cbf116fc..9e8b3a55b7 100644 --- a/app/client/src/ce/entities/FeatureFlag.ts +++ b/app/client/src/ce/entities/FeatureFlag.ts @@ -30,8 +30,6 @@ export const FEATURE_FLAG = { release_app_sidebar_enabled: "release_app_sidebar_enabled", release_git_branch_protection_enabled: "release_git_branch_protection_enabled", - license_git_branch_protection_enabled: - "license_git_branch_protection_enabled", license_widget_rtl_support_enabled: "license_widget_rtl_support_enabled", } as const; @@ -62,7 +60,6 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = { ab_show_templates_instead_of_blank_canvas_enabled: false, release_app_sidebar_enabled: false, release_git_branch_protection_enabled: false, - license_git_branch_protection_enabled: false, license_widget_rtl_support_enabled: false, }; diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index 554e196081..06b66af263 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -115,6 +115,7 @@ export function EditorHeader() { ); const isPreviewingApp = isPreviewMode || isAppSettingsPaneWithNavigationTabOpen; + const isProtectedMode = useSelector(protectedModeSelector); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [showModal, setShowModal] = useState(false); diff --git a/app/client/src/pages/Editor/WidgetsEditor/MainContainerWrapper.tsx b/app/client/src/pages/Editor/WidgetsEditor/MainContainerWrapper.tsx index 4a424ea098..57d96ec9cf 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/MainContainerWrapper.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/MainContainerWrapper.tsx @@ -136,7 +136,7 @@ function MainContainerWrapper(props: MainCanvasWrapperProps) { const selectedTheme = useSelector(getSelectedAppTheme); const params = useParams<{ applicationId: string; pageId: string }>(); const shouldHaveTopMargin = - !(isPreviewMode || isProtectedMode) || + (!isPreviewMode && !isProtectedMode) || !isAppSettingsPaneWithNavigationTabOpen || pages.length > 1; const isAppThemeChanging = useSelector(getAppThemeIsChanging); @@ -185,7 +185,7 @@ function MainContainerWrapper(props: MainCanvasWrapperProps) { } const isPreviewingNavigation = - isPreviewMode || isProtectedMode || isAppSettingsPaneWithNavigationTabOpen; + isPreviewMode || isAppSettingsPaneWithNavigationTabOpen; /** * calculating exact height to not allow scroll at this component, @@ -231,6 +231,7 @@ function MainContainerWrapper(props: MainCanvasWrapperProps) { shouldHaveTopMargin && !showCanvasTopSection && !isPreviewingNavigation && + !isProtectedMode && !showAnonymousDataPopup, "mt-24": shouldShowSnapShotBanner, })} diff --git a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx index a41259d97c..0c0bce361e 100644 --- a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx +++ b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx @@ -179,7 +179,7 @@ const getQuickActionButtons = ({ { className: "t--bottom-bar-commit", disabled: isProtectedMode, - count: isProtectedMode ? undefined : changesToCommit, + count: changesToCommit, icon: "plus", loading: isFetchingGitStatus, onClick: commit, diff --git a/app/client/src/pages/Editor/gitSync/Tabs/ConnectionSuccess.tsx b/app/client/src/pages/Editor/gitSync/Tabs/ConnectionSuccess.tsx index 83f86190bf..cf9ebc777b 100644 --- a/app/client/src/pages/Editor/gitSync/Tabs/ConnectionSuccess.tsx +++ b/app/client/src/pages/Editor/gitSync/Tabs/ConnectionSuccess.tsx @@ -8,7 +8,6 @@ import { BRANCH_PROTECTION_RULE_1, BRANCH_PROTECTION_RULE_2, BRANCH_PROTECTION_RULE_3, - GIT_CONNECT_SUCCESS_MESSAGE, GIT_CONNECT_SUCCESS_TITLE, OPEN_GIT_SETTINGS, START_USING_GIT, @@ -21,10 +20,7 @@ import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components"; import { getCurrentAppGitMetaData } from "@appsmith/selectors/applicationSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; -import { - getDefaultGitBranchName, - getIsGitProtectedFeatureEnabled, -} from "selectors/gitSyncSelectors"; +import { getDefaultGitBranchName } from "selectors/gitSyncSelectors"; const Container = styled.div``; @@ -57,18 +53,6 @@ const FeatureIcon = styled(Icon)` margin-right: 4px; `; -const BranchTag = styled(Tag)` - display: inline-flex; -`; - -const DefaultBranchMessage = styled(Text)` - margin-bottom: 16px; -`; - -const ProtectionRulesTitle = styled(Text)` - margin-bottom: 8px; -`; - const features = [ createMessage(BRANCH_PROTECTION_RULE_1), createMessage(BRANCH_PROTECTION_RULE_2), @@ -78,9 +62,6 @@ const features = [ function ConnectionSuccess() { const gitMetadata = useSelector(getCurrentAppGitMetaData); const defaultBranchName = useSelector(getDefaultGitBranchName); - const isGitProtectedFeatureEnabled = useSelector( - getIsGitProtectedFeatureEnabled, - ); const dispatch = useDispatch(); useEffect(() => { @@ -110,55 +91,42 @@ function ConnectionSuccess() { }); }; - const preBranchProtectionContent = () => { - return ( - {createMessage(GIT_CONNECT_SUCCESS_MESSAGE)} - ); - }; - - const postBranchProtectionContent = () => { - return ( - <> - - Right now,{" "} - {defaultBranchName} is set - as the default branch and it is protected. - - - {createMessage(BRANCH_PROTECTION_RULES_AS_FOLLOWS)} - - - {features.map((feature) => ( - - - {feature} - - ))} - - {createMessage(BRANCH_PROTECTION_CHANGE_RULE)} - - ); - }; - - const preBranchProtectionActions = () => { - return ( - - ); - }; - - const postBranchProtectionActions = () => { - return ( - <> + return ( + <> + + + + + + {createMessage(GIT_CONNECT_SUCCESS_TITLE)} + + + + Right now,{" "} + + {defaultBranchName} + {" "} + is set as the default branch and it is protected. + + + {createMessage(BRANCH_PROTECTION_RULES_AS_FOLLOWS)} + + + {features.map((feature) => ( + + + {feature} + + ))} + + {createMessage(BRANCH_PROTECTION_CHANGE_RULE)} + + + - - ); - }; - - return ( - <> - - - - - - {createMessage(GIT_CONNECT_SUCCESS_TITLE)} - - - {isGitProtectedFeatureEnabled - ? postBranchProtectionContent() - : preBranchProtectionContent()} - - - - {isGitProtectedFeatureEnabled - ? postBranchProtectionActions() - : preBranchProtectionActions()} ); diff --git a/app/client/src/pages/Editor/gitSync/Tabs/GitSettings/GitDefaultBranch.tsx b/app/client/src/pages/Editor/gitSync/Tabs/GitSettings/GitDefaultBranch.tsx index 8c7691351e..a84833fb76 100644 --- a/app/client/src/pages/Editor/gitSync/Tabs/GitSettings/GitDefaultBranch.tsx +++ b/app/client/src/pages/Editor/gitSync/Tabs/GitSettings/GitDefaultBranch.tsx @@ -6,14 +6,12 @@ import { createMessage, } from "@appsmith/constants/messages"; import { updateGitDefaultBranch } from "actions/gitSyncActions"; +import { isCEMode } from "@appsmith/utils"; import { Button, Link, Option, Select, Text } from "design-system"; import React, { useEffect, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { getGitBranches } from "selectors/gitSyncSelectors"; import styled from "styled-components"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag"; -import { useAppsmithEnterpriseLink } from "./hooks"; const Container = styled.div` padding-top: 16px; @@ -43,10 +41,9 @@ const StyledSelect = styled(Select)` `; function GitDefaultBranch() { + const isCE = isCEMode(); + const dispatch = useDispatch(); - const isGitProtectedFeatureLicensed = useFeatureFlag( - FEATURE_FLAG.license_git_branch_protection_enabled, - ); const unfilteredBranches = useSelector(getGitBranches); const [selectedValue, setSelectedValue] = useState(); @@ -55,10 +52,6 @@ function GitDefaultBranch() { return defaultBranch?.branchName; }, [unfilteredBranches]); - const enterprisePricingLink = useAppsmithEnterpriseLink( - "git_branch_protection", - ); - useEffect(() => { const defaultBranch = unfilteredBranches.find((b) => b.default); setSelectedValue(defaultBranch?.branchName); @@ -75,7 +68,7 @@ function GitDefaultBranch() { }; const updateIsDisabled = - !selectedValue || selectedValue === currentDefaultBranch; + isCE && (!selectedValue || selectedValue === currentDefaultBranch); return ( @@ -86,24 +79,21 @@ function GitDefaultBranch() { {createMessage(DEFAULT_BRANCH_DESC)} - {!isGitProtectedFeatureLicensed && ( - - To change your default branch, try{" "} - - {createMessage(APPSMITH_ENTERPRISE)} - - - )} + + To change your default branch, try{" "} + + {createMessage(APPSMITH_ENTERPRISE)} + + setSelectedValue(v)} value={selectedValue} > @@ -114,7 +104,6 @@ function GitDefaultBranch() { ))}