fix: [ritik/code-1773] Added Repository Setting for Public/Private Access (#2023)

pull/3519/head
Ritik Kapoor 2024-05-13 18:01:12 +00:00 committed by Harness
parent 55f340ab7e
commit ea205ff7ba
14 changed files with 131 additions and 75 deletions

View File

@ -48,7 +48,9 @@ const App: React.FC<AppProps> = React.memo(function App({
hooks,
customComponents,
currentUserProfileURL = '',
defaultSettingsURL = ''
defaultSettingsURL = '',
isPublicAccessEnabledOnResources = false,
isCurrentSessionPublic = false
}: AppProps) {
const [strings, setStrings] = useState<LanguageRecord>()
const getRequestOptions = useCallback(
@ -90,7 +92,9 @@ const App: React.FC<AppProps> = React.memo(function App({
currentUser: defaultCurrentUser,
customComponents,
currentUserProfileURL,
defaultSettingsURL
defaultSettingsURL,
isPublicAccessEnabledOnResources,
isCurrentSessionPublic
}}>
<IconoirProvider
iconProps={{

View File

@ -53,7 +53,9 @@ const AppContext = React.createContext<AppContextProps>({
},
currentUserProfileURL: '',
routingId: '',
defaultSettingsURL: ''
defaultSettingsURL: '',
isPublicAccessEnabledOnResources: false,
isCurrentSessionPublic: false
})
export const AppContextProvider: React.FC<{ value: AppProps }> = React.memo(function AppContextProvider({

View File

@ -78,4 +78,6 @@ export interface AppProps {
currentUserProfileURL: string
defaultSettingsURL: string
isPublicAccessEnabledOnResources: boolean
isCurrentSessionPublic: boolean
}

View File

@ -50,6 +50,8 @@ ReactDOM.render(
currentUserProfileURL=""
routingId=""
defaultSettingsURL=""
isPublicAccessEnabledOnResources
isCurrentSessionPublic={false}
/>,
document.getElementById('react-root')
)

View File

@ -15,11 +15,13 @@
*/
import React, { useState } from 'react'
import { Render } from 'react-jsx-match'
import cx from 'classnames'
import { Button, ButtonVariation, Container, FlexExpander, Layout, Text } from '@harnessio/uicore'
import { Color, FontVariation } from '@harnessio/design-system'
import { Classes } from '@blueprintjs/core'
import { Icon } from '@harnessio/icons'
import { useAppContext } from 'AppContext'
import { useStrings } from 'framework/strings'
import { CopyButton } from 'components/CopyButton/CopyButton'
import { CodeIcon } from 'utils/GitUtils'
@ -33,7 +35,7 @@ interface CloneButtonTooltipProps {
export function CloneButtonTooltip({ httpsURL }: CloneButtonTooltipProps) {
const { getString } = useStrings()
const [flag, setFlag] = useState(false)
const { isCurrentSessionPublic } = useAppContext()
return (
<Container className={css.container} padding="xlarge">
<Layout.Vertical spacing="small">
@ -58,24 +60,27 @@ export function CloneButtonTooltip({ httpsURL }: CloneButtonTooltipProps) {
<CopyButton content={httpsURL} id={css.cloneCopyButton} icon={CodeIcon.Copy} iconProps={{ size: 14 }} />
</Layout.Horizontal>
</Container>
<Button
width={300}
onClick={() => {
setFlag(true)
}}
variation={ButtonVariation.SECONDARY}>
{getString('generateCloneCred')}
</Button>
<Text
padding={{ top: 'small' }}
width={300}
icon={'code-info'}
className={css.codeText}
iconProps={{ size: 16 }}
color={Color.GREY_700}
font={{ variation: FontVariation.BODY2_SEMI, size: 'xsmall' }}>
{getString('generateCloneText')}
</Text>
<Render when={!isCurrentSessionPublic}>
<Button
width={300}
onClick={() => {
setFlag(true)
}}
variation={ButtonVariation.SECONDARY}>
{getString('generateCloneCred')}
</Button>
<Text
padding={{ top: 'small' }}
width={300}
icon={'code-info'}
className={css.codeText}
iconProps={{ size: 16 }}
color={Color.GREY_700}
font={{ variation: FontVariation.BODY2_SEMI, size: 'xsmall' }}>
{getString('generateCloneText')}
</Text>
</Render>
</Layout.Vertical>
<CloneCredentialDialog flag={flag} setFlag={setFlag} />
</Container>

View File

@ -42,7 +42,7 @@ const KEYWORD_REGEX = /((?:(?:-{0,1})(?:repo|lang|file|case|count)):\S*|(?: or|a
const CodeSearchBar: FC<CodeSearchBarProps> = ({ value, onChange, onSearch, onKeyDown, searchMode, setSearchMode }) => {
const { getString } = useStrings()
const { hooks, routingId, defaultSettingsURL } = useAppContext()
const { hooks, routingId, defaultSettingsURL, isCurrentSessionPublic } = useAppContext()
const { SEMANTIC_SEARCH_ENABLED: isSemanticSearchFFEnabled } = hooks?.useFeatureFlags()
const { orgIdentifier, projectIdentifier } = useParams<Identifier>()
const { data: aidaSettingResponse, loading: isAidaSettingLoading } = hooks?.useGetSettingValue({
@ -51,7 +51,9 @@ const CodeSearchBar: FC<CodeSearchBarProps> = ({ value, onChange, onSearch, onKe
})
const [enableSemanticSearch, setEnableSemanticSearch] = useState<boolean>(false)
useEffect(() => {
setEnableSemanticSearch(isSemanticSearchFFEnabled && aidaSettingResponse?.data?.value == 'true')
setEnableSemanticSearch(
isSemanticSearchFFEnabled && aidaSettingResponse?.data?.value == 'true' && !isCurrentSessionPublic
)
}, [isAidaSettingLoading, isSemanticSearchFFEnabled])
const isSemanticMode = enableSemanticSearch && searchMode === SEARCH_MODE.SEMANTIC
return (

View File

@ -26,3 +26,7 @@
.breadcrumb {
align-items: center;
}
.hideBreadcrumbs {
display: none !important;
}

View File

@ -18,3 +18,4 @@
// This is an auto-generated file
export declare const breadcrumb: string
export declare const header: string
export declare const hideBreadcrumbs: string

View File

@ -15,6 +15,7 @@
*/
import React, { Fragment } from 'react'
import cx from 'classnames'
import { Container, Layout, Text, PageHeader, PageHeaderProps } from '@harnessio/uicore'
import { Icon } from '@harnessio/icons'
import { Color, FontVariation } from '@harnessio/design-system'
@ -50,7 +51,7 @@ export function RepositoryPageHeader({
const { gitRef } = useParams<CODEProps>()
const { getString } = useStrings()
const space = useGetSpaceParam()
const { routes } = useAppContext()
const { routes, isCurrentSessionPublic } = useAppContext()
return (
<PageHeader
@ -59,7 +60,9 @@ export function RepositoryPageHeader({
title=""
breadcrumbs={
<Container className={css.header}>
<Layout.Horizontal spacing="small" className={css.breadcrumb}>
<Layout.Horizontal
spacing="small"
className={cx(css.breadcrumb, { [css.hideBreadcrumbs]: isCurrentSessionPublic })}>
<Link to={routes.toCODERepositories({ space })}>{getString('repositories')}</Link>
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
<Link to={routes.toCODERepository({ repoPath: (repoMetadata?.path as string) || '', gitRef })}>

View File

@ -179,6 +179,7 @@ export interface StringsMap {
confirmNewPassword: string
confirmPassRequired: string
confirmPassword: string
confirmRepoVisButton: string
confirmation: string
content: string
contents: string
@ -206,6 +207,7 @@ export interface StringsMap {
'createRepoModal.branchLabel': string
'createRepoModal.privateLabel': string
'createRepoModal.publicLabel': string
'createRepoModal.publicWarning': string
createRepoPerms: string
createSpace: string
createTag: string

View File

@ -122,6 +122,7 @@ createRepoModal:
branch: ' branch.'
publicLabel: Anyone with access to the Gitness environment can clone this repo.
privateLabel: You choose who can see and commit to this repository.
publicWarning: Please note that anyone with access to the Gitness environment can clone this repo.
validation:
repoNamePatternIsNotValid: "Name can only contain alphanumerics, '-', '_', '.', and '$'"
gitBranchNameInvalid: Branch name is invalid.
@ -893,7 +894,8 @@ enterGitlabPlaceholder: https://gitlab.com/
enterGithubPlaceholder: https://api.github.com/
enterBitbucketPlaceholder: https://bitbucket.org/
changeRepoVis: Change repository visibility
changeRepoVisContent: Are you sure you want to make this repository {repoVis}? {repoText}
changeRepoVisContent: Are you sure you want to make this repository {repoVis}?
confirmRepoVisButton: Yes, make the Repository {repoVis}
repoVisibility: Repository visibility
visibility: Visibility
attachText: Attach images & videos by dragging & dropping, selecting or pasting them.

View File

@ -43,7 +43,7 @@ export function ContentHeader({
resourceContent
}: Pick<GitInfoProps, 'repoMetadata' | 'gitRef' | 'resourcePath' | 'resourceContent'>) {
const { getString } = useStrings()
const { routes, standalone, hooks } = useAppContext()
const { routes, standalone, hooks, isCurrentSessionPublic } = useAppContext()
const history = useHistory()
const _isDir = isDir(resourceContent)
const space = useGetSpaceParam()
@ -164,7 +164,9 @@ export function ContentHeader({
</>
)}
</Layout.Horizontal>
<div className={css.searchBoxCtn}>{!standalone ? <CodeSearch repoMetadata={repoMetadata} /> : null}</div>
<div className={css.searchBoxCtn}>
{!standalone && !isCurrentSessionPublic ? <CodeSearch repoMetadata={repoMetadata} /> : null}
</div>
</Container>
)
}

View File

@ -65,8 +65,7 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
const { showError, showSuccess } = useToaster()
const space = useGetSpaceParam()
const { standalone } = useAppContext()
const { hooks } = useAppContext()
const { standalone, hooks, isPublicAccessEnabledOnResources } = useAppContext()
const { getString } = useStrings()
const currRepoVisibility = repoMetadata?.is_public === true ? RepoVisibility.PUBLIC : RepoVisibility.PRIVATE
@ -77,6 +76,11 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
path: `/api/v1/repos/${repoMetadata?.path}/+/`
})
const { mutate: changeVisibility } = useMutate({
verb: 'POST',
path: `/api/v1/repos/${repoMetadata?.path}/+/public-access`
})
const permEditResult = hooks?.usePermissionTranslate?.(
{
resource: {
@ -109,50 +113,71 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
return (
<Dialog
className={css.dialogContainer}
style={{ width: 610, maxHeight: '95vh', overflow: 'auto' }}
title={getString('changeRepoVis')}
style={{ width: 585, maxHeight: '95vh', overflow: 'auto' }}
title={<Text font={{ variation: FontVariation.H4 }}>{getString('changeRepoVis')}</Text>}
isOpen
onClose={hideModal}>
<Text>
<StringSubstitute
str={getString('changeRepoVisContent')}
vars={{
repoVis: <span className={css.text}>{repoVis}</span>,
repoText:
repoVis === RepoVisibility.PUBLIC
? getString('createRepoModal.publicLabel')
: getString('createRepoModal.privateLabel')
<Layout.Vertical spacing="xlarge">
<Text>
<StringSubstitute
str={getString('changeRepoVisContent')}
vars={{
repoVis: <span className={css.text}>{repoVis}</span>
}}
/>
</Text>
<Container
intent="warning"
background="yellow100"
border={{
color: 'orange500'
}}
/>
</Text>
<hr className={css.dividerContainer} />
<Layout.Horizontal className={css.buttonContainer}>
<Button
margin={{ right: 'medium' }}
type="submit"
text={getString('confirm')}
variation={ButtonVariation.PRIMARY}
onClick={() => {
mutate({ is_public: repoVis === RepoVisibility.PUBLIC ? true : false })
.then(() => {
showSuccess(getString('repoUpdate'))
hideModal()
refetch()
})
.catch(err => {
showError(getErrorMessage(err))
})
refetch()
}}
/>
<Button
text={getString('cancel')}
variation={ButtonVariation.TERTIARY}
onClick={() => {
hideModal()
}}
/>
</Layout.Horizontal>
margin={{ top: 'medium', bottom: 'medium' }}>
<Text
icon="warning-outline"
iconProps={{ size: 16, margin: { right: 'small' } }}
padding={{ left: 'large', right: 'large', top: 'small', bottom: 'small' }}
color={Color.WARNING}>
{repoVis === RepoVisibility.PUBLIC
? getString('createRepoModal.publicWarning')
: getString('createRepoModal.privateLabel')}
</Text>
</Container>
<Layout.Horizontal className={css.buttonContainer}>
<Button
margin={{ right: 'medium' }}
type="submit"
text={
<StringSubstitute
str={getString('confirmRepoVisButton')}
vars={{
repoVis: <span className={css.text}>{repoVis}</span>
}}
/>
}
variation={ButtonVariation.PRIMARY}
onClick={() => {
changeVisibility({ is_public: repoVis === RepoVisibility.PUBLIC ? true : false })
.then(() => {
showSuccess(getString('repoUpdate'))
hideModal()
refetch()
})
.catch(err => {
showError(getErrorMessage(err))
})
refetch()
}}
/>
<Button
text={getString('cancel')}
variation={ButtonVariation.TERTIARY}
onClick={() => {
hideModal()
}}
/>
</Layout.Horizontal>
</Layout.Vertical>
</Dialog>
)
}
@ -315,7 +340,7 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
</Container>
</Layout.Horizontal>
</Container>
<Render when={enablePublicRepo}>
<Render when={enablePublicRepo && isPublicAccessEnabledOnResources}>
<Container padding="large" margin={{ bottom: 'medium' }} className={css.generalContainer}>
<Layout.Horizontal padding={{ bottom: 'medium' }}>
<Container className={css.label}>
@ -330,6 +355,7 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
onChange={evt => {
setRepoVis((evt.target as HTMLInputElement).value as RepoVisibility)
}}
{...permissionProps(permEditResult, standalone)}
className={css.radioContainer}
items={[
{
@ -391,6 +417,7 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
setRepoVis(formik.values.isPublic)
openModal()
}}
{...permissionProps(permEditResult, standalone)}
/>
) : null}
</Layout.Horizontal>

View File

@ -122,8 +122,6 @@
}
.dialogContainer {
padding-bottom: 27px !important;
:global(.bp3-dialog-header) {
margin-bottom: var(--spacing-medium) !important;
}