diff --git a/frontend/common/services/useGithub.ts b/frontend/common/services/useGithub.ts index 600b01bca300..630a8e50ec2b 100644 --- a/frontend/common/services/useGithub.ts +++ b/frontend/common/services/useGithub.ts @@ -29,6 +29,7 @@ export const githubService = service query.organisation_id }/github/repositories/?${Utils.toParam({ installation_id: query.installation_id, + is_github_installation: query.is_github_installation, })}`, }), }), diff --git a/frontend/common/types/requests.ts b/frontend/common/types/requests.ts index 242f0c8bfbfc..620348473066 100644 --- a/frontend/common/types/requests.ts +++ b/frontend/common/types/requests.ts @@ -317,6 +317,7 @@ export type Req = { updateGithubIntegration: { organisation_id: string github_integration_id: string + installation_id: string } deleteGithubIntegration: { organisation_id: string @@ -361,7 +362,11 @@ export type Req = { repo_name: string repo_owner: string } - getGithubRepos: { installation_id: string; organisation_id: string } + getGithubRepos: { + installation_id: string + organisation_id: string + is_github_installation: string + } getServersideEnvironmentKeys: { environmentId: string } deleteServersideEnvironmentKeys: { environmentId: string; id: string } createServersideEnvironmentKeys: { diff --git a/frontend/web/components/App.js b/frontend/web/components/App.js index d177591bce23..e846f442c067 100644 --- a/frontend/web/components/App.js +++ b/frontend/web/components/App.js @@ -18,6 +18,7 @@ import { getStore } from 'common/store' import { resolveAuthFlow } from '@datadog/ui-extensions-sdk' import ConfigProvider from 'common/providers/ConfigProvider' import { getOrganisationUsage } from 'common/services/useOrganisationUsage' +import { getGithubIntegration } from 'common/services/useGithubIntegration' import Button from './base/forms/Button' import Icon from './Icon' import AccountStore from 'common/stores/account-store' @@ -38,10 +39,12 @@ const App = class extends Component { state = { activeOrganisation: 0, asideIsVisible: !isMobile, + githubMessage: '', lastEnvironmentId: '', lastProjectId: '', pin: '', showAnnouncement: true, + showGithubMessage: false, } constructor(props, context) { @@ -53,6 +56,7 @@ const App = class extends Component { getBuildVersion() this.listenTo(ProjectStore, 'change', () => this.forceUpdate()) this.listenTo(AccountStore, 'change', this.getOrganisationUsage) + this.getGithubIntegration() this.getOrganisationUsage() window.addEventListener('scroll', this.handleScroll) AsyncStorage.getItem('lastEnv').then((res) => { @@ -74,6 +78,9 @@ const App = class extends Component { getOrganisationUsage(getStore(), { organisationId: AccountStore.getOrganisation()?.id, }).then((res) => { + if (AccountStore.getOrganisation().id) { + this.getGithubIntegration() + } this.setState({ activeOrganisation: AccountStore.getOrganisation().id, }) @@ -81,6 +88,21 @@ const App = class extends Component { } } + getGithubIntegration = () => { + if (this.state.activeOrganisation) { + getGithubIntegration(getStore(), { + organisation_id: this.state.activeOrganisation, + }).then((res) => { + if (res.isError && res.error.data.status === 410) { + this.setState({ + githubMessage: res.error.data.detail, + showGithubMessage: true, + }) + } + }) + } + } + toggleDarkMode = () => { const newValue = !Utils.getFlagsmithHasFeature('dark_mode') flagsmith.setTrait('dark_mode', newValue) @@ -474,6 +496,28 @@ const App = class extends Component { id={AccountStore.getOrganisation()?.id} /> )} + {user && this.state.showGithubMessage && ( + + + this.closeAnnouncement(announcementValue.id) + } + buttonText={'Go to Integrations'} + goToIntegrations={() => { + this.context.router.history.replace( + `/project/${projectId}/integrations/`, + ) + }} + > + + {this.state.githubMessage} + + + + )} {user && showBanner && ( void } type repoSelectValue = { @@ -18,6 +21,7 @@ type repoSelectValue = { const GitHubRepositoriesSelect: FC = ({ disabled, githubId, + manageIntegration, organisationId, projectId, repositories, @@ -73,6 +77,30 @@ const GitHubRepositoriesSelect: FC = ({ Add Repository + <> + + + + Manage available GitHub Repositories + + { + window.location.reload() + }} + /> + + > ) } diff --git a/frontend/web/components/InfoMessage.js b/frontend/web/components/InfoMessage.js index 61e166887285..a2c000445fb0 100644 --- a/frontend/web/components/InfoMessage.js +++ b/frontend/web/components/InfoMessage.js @@ -8,7 +8,11 @@ export default class InfoMessage extends PureComponent { static displayName = 'InfoMessage' handleOpenNewWindow = () => { - window.open(this.props.url, '_blank') + if (this.props.goToIntegrations) { + this.props.goToIntegrations() + } else { + window.open(this.props.url, '_blank') + } } render() { diff --git a/frontend/web/components/IntegrationList.js b/frontend/web/components/IntegrationList.js index fad334c1ee2f..db76f997413d 100644 --- a/frontend/web/components/IntegrationList.js +++ b/frontend/web/components/IntegrationList.js @@ -224,17 +224,21 @@ class IntegrationList extends Component { componentDidMount() { this.fetch() - if (Utils.getFlagsmithHasFeature('github_integration')) { - getGithubIntegration(getStore(), { + } + fetchGithubIntegration() { + getGithubIntegration( + getStore(), + { organisation_id: AccountStore.getOrganisation().id, - }).then((res) => { - this.setState({ - githubId: res?.data?.results[0]?.id, - hasIntegrationWithGithub: !!res?.data?.results?.length, - installationId: res?.data?.results[0]?.installation_id, - }) + }, + { forceRefetch: true }, + ).then((res) => { + this.setState({ + githubId: res?.data?.results[0]?.id, + hasIntegrationWithGithub: !!res?.data?.results?.length, + installationId: res?.data?.results[0]?.installation_id, }) - } + }) } fetch = () => { @@ -280,6 +284,7 @@ class IntegrationList extends Component { }), ).then((res) => { console.log(res) + this.fetchGithubIntegration() this.setState({ activeIntegrations: _.map(res, (item) => !!item && item.length ? item : [], diff --git a/frontend/web/components/MyGitHubRepositoriesSelect.tsx b/frontend/web/components/MyGitHubRepositoriesSelect.tsx index 0bfe00a593e1..18982d7503e9 100644 --- a/frontend/web/components/MyGitHubRepositoriesSelect.tsx +++ b/frontend/web/components/MyGitHubRepositoriesSelect.tsx @@ -1,31 +1,53 @@ import { FC } from 'react' import { useGetGithubReposQuery } from 'common/services/useGithub' import GitHubRepositoriesSelect from './GitHubRepositoriesSelect' +import InvalidGithubIntegration from './modals/InvalidGithubIntegration' type MyGitHubRepositoriesSelectType = { installationId: string organisationId: string projectId: string githubId: string + manageIntegration: () => void + onComplete: () => void } const MyGitHubRepositoriesSelect: FC = ({ githubId, installationId, + manageIntegration, + onComplete, organisationId, projectId, }) => { - const { data } = useGetGithubReposQuery({ + const { data, error, isError, isFetching } = useGetGithubReposQuery({ installation_id: installationId, + is_github_installation: 'False', organisation_id: organisationId, }) return ( - + <> + {isFetching ? ( + + + + ) : !isError && data ? ( + + ) : ( + + )} + > ) } diff --git a/frontend/web/components/modals/CreateEditIntegrationModal.js b/frontend/web/components/modals/CreateEditIntegrationModal.js index d5b7586b6b9b..e63b1a040e6c 100644 --- a/frontend/web/components/modals/CreateEditIntegrationModal.js +++ b/frontend/web/components/modals/CreateEditIntegrationModal.js @@ -5,11 +5,9 @@ import _data from 'common/data/base/_data' import ErrorMessage from 'components/ErrorMessage' import ModalHR from './ModalHR' import Button from 'components/base/forms/Button' -import GithubRepositoriesTable from 'components/GithubRepositoriesTable' import classNames from 'classnames' import { getStore } from 'common/store' import { getGithubRepos } from 'common/services/useGithub' -import DeleteGithubIntegracion from 'components/DeleteGithubIntegracion' const GITHUB_INSTALLATION_UPDATE = 'update' @@ -233,29 +231,8 @@ const CreateEditIntegration = class extends Component { installationId={this.props.githubMeta.installationId} organisationId={AccountStore.getOrganisation().id} projectId={this.props.projectId} - /> - - - - - Manage available GitHub Repositories - - { - closeModal() - }} + manageIntegration={this.openGitHubWinInstallations} + onComplete={this.onComplete} /> > diff --git a/frontend/web/components/modals/CreateFlag.js b/frontend/web/components/modals/CreateFlag.js index f1fc7a61e3b9..341f51d0be22 100644 --- a/frontend/web/components/modals/CreateFlag.js +++ b/frontend/web/components/modals/CreateFlag.js @@ -691,7 +691,7 @@ const CreateFlag = class extends Component { orgId={AccountStore.getOrganisation().id} onChange={(v) => this.setState({ - featureExternalResource: v.value, + featureExternalResource: v, }) } repoOwner={repoOwner} diff --git a/frontend/web/components/modals/InvalidGithubIntegration.tsx b/frontend/web/components/modals/InvalidGithubIntegration.tsx new file mode 100644 index 000000000000..71dfe68af0c0 --- /dev/null +++ b/frontend/web/components/modals/InvalidGithubIntegration.tsx @@ -0,0 +1,82 @@ +import React, { FC, useState } from 'react' +import Utils from 'common/utils/utils' +import InputGroup from 'components/base/forms/InputGroup' +import Button from 'components/base/forms/Button' +import { useUpdateGithubIntegrationMutation } from 'common/services/useGithubIntegration' +import ErrorMessage from 'components/ErrorMessage' + +type InvalidGithubIntegrationType = { + onComplete?: () => void + organisationId: string + githubIntegrationId: string + errorMessage: string +} + +const InvalidGithubIntegration: FC = ({ + errorMessage, + githubIntegrationId, + onComplete, + organisationId, +}) => { + const [installationId, setInstallationId] = useState('') + + const [updateGithubIntegration, { isLoading: updating }] = + useUpdateGithubIntegrationMutation() + + return ( + { + Utils.preventDefault(e) + updateGithubIntegration({ + github_integration_id: githubIntegrationId, + installation_id: installationId, + organisation_id: organisationId, + }).then(() => { + onComplete?.() + }) + }} + > + + + ) => { + setInstallationId(Utils.safeParseEventValue(event)) + }} + type='number' + name='installationId' + id='installation-id' + placeholder='E.g. 12345678' + /> + + + + Cancel + + + {'Delete the GitHub integration'} + + + {updating ? 'Updating' : 'Update'} + + + + ) +} + +export default InvalidGithubIntegration diff --git a/frontend/web/components/pages/GitHubSetupPage.tsx b/frontend/web/components/pages/GitHubSetupPage.tsx index 0004007e7672..1a2a356efb82 100644 --- a/frontend/web/components/pages/GitHubSetupPage.tsx +++ b/frontend/web/components/pages/GitHubSetupPage.tsx @@ -1,5 +1,6 @@ import React, { FC, useState, useEffect } from 'react' import OrganisationSelect from 'components/OrganisationSelect' +import AppActions from 'common/dispatcher/app-actions' import Input from 'components/base/forms/Input' import Icon from 'components/Icon' import InputGroup from 'components/base/forms/InputGroup' @@ -45,9 +46,10 @@ const GitHubSetupPage: FC = ({ location }) => { const { data: repos, isSuccess: reposLoaded } = useGetGithubReposQuery( { installation_id: installationId, + is_github_installation: 'True', organisation_id: organisation, }, - { skip: !installationId }, + { skip: !installationId || !organisation }, ) const [createGithubIntegration] = useCreateGithubIntegrationMutation() @@ -72,6 +74,7 @@ const GitHubSetupPage: FC = ({ location }) => { ) { window.location.href = `${baseUrl}/` } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isSuccessCreatedGithubRepository]) useEffect(() => { @@ -124,7 +127,9 @@ const GitHubSetupPage: FC = ({ location }) => { Select your Flagsmith Organisation { - setOrganisation(organisationId) + setOrganisation(`${organisationId}`) + AppActions.selectOrganisation(organisationId) + AppActions.getOrganisation(organisationId) }} showSettings={false} firstOrganisation @@ -170,7 +175,13 @@ const GitHubSetupPage: FC = ({ location }) => { { { const newProjects = [...projects]