From e329344871763994aecc6bce76b4e7986f39be86 Mon Sep 17 00:00:00 2001 From: Florin Nania Date: Sat, 12 Oct 2024 00:01:18 +0300 Subject: [PATCH 1/3] #1661 add SignIn button in Github related card from entity page overview --- .../ContributorsCard/ContributorsCard.tsx | 13 ++++ .../Widgets/LanguagesCard/LanguagesCard.tsx | 19 +++++- .../Widgets/ReleasesCard/ReleasesCard.tsx | 19 +++++- .../src/hooks/useGithubLoggedIn.tsx | 67 +++++++++++++++++++ .../PullRequestsStatsCard.tsx | 12 +++- .../DependabotAlertsWidget.tsx | 16 ++++- .../SecurityInsightsWidget.tsx | 16 ++++- .../src/components/useGithubLoggedIn.tsx | 52 ++++++++++++++ 8 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 plugins/frontend/backstage-plugin-github-insights/src/hooks/useGithubLoggedIn.tsx create mode 100644 plugins/frontend/backstage-plugin-security-insights/src/components/useGithubLoggedIn.tsx diff --git a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.tsx b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.tsx index edc3a6114..10d1e6933 100644 --- a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.tsx +++ b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.tsx @@ -31,6 +31,10 @@ import { GITHUB_INSIGHTS_ANNOTATION, } from '../../utils/isGithubInsightsAvailable'; import { useEntity } from '@backstage/plugin-catalog-react'; +import { + GithubNotAuthorized, + useGithubLoggedIn, +} from '../../../hooks/useGithubLoggedIn'; const useStyles = makeStyles(theme => ({ infoCard: { @@ -48,12 +52,21 @@ const ContributorsCard = () => { const { hostname } = useEntityGithubScmIntegration(entity); const projectAlert = isGithubInsightsAvailable(entity); const { owner, repo } = useProjectEntity(entity); + const isLoggedIn = useGithubLoggedIn(); if (!projectAlert) { return ( ); } + if (!isLoggedIn) { + return ( + + + + ); + } + if (loading) { return ; } else if (error) { diff --git a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.tsx b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.tsx index dc816bf99..52bd8f9b3 100644 --- a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.tsx +++ b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.tsx @@ -26,6 +26,10 @@ import { import { useRequest } from '../../../hooks/useRequest'; import { colors } from './colors'; import { useProjectEntity } from '../../../hooks/useProjectEntity'; +import { + GithubNotAuthorized, + useGithubLoggedIn, +} from '../../../hooks/useGithubLoggedIn'; import { isGithubInsightsAvailable, GITHUB_INSIGHTS_ANNOTATION, @@ -66,7 +70,7 @@ type Language = { [key: string]: number; }; -const LanguagesCard = () => { +const LanguagesCardContent = () => { const { entity } = useEntity(); let barWidth = 0; const classes = useStyles(); @@ -154,4 +158,17 @@ const LanguagesCard = () => { ); }; +const LanguagesCard = () => { + const classes = useStyles(); + const isLoggedIn = useGithubLoggedIn(); + + return isLoggedIn ? ( + + ) : ( + + + + ); +}; + export default LanguagesCard; diff --git a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.tsx b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.tsx index 3366b947a..9d0d9f2b8 100644 --- a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.tsx +++ b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.tsx @@ -32,6 +32,10 @@ import { } from '../../utils/isGithubInsightsAvailable'; import { useEntity } from '@backstage/plugin-catalog-react'; import { styles as useStyles } from '../../utils/styles'; +import { + GithubNotAuthorized, + useGithubLoggedIn, +} from '../../../hooks/useGithubLoggedIn'; type Release = { id: number; @@ -41,7 +45,7 @@ type Release = { name: string; }; -const ReleasesCard = () => { +const ReleasesCardContent = () => { const classes = useStyles(); const { entity } = useEntity(); @@ -108,4 +112,17 @@ const ReleasesCard = () => { ); }; +const ReleasesCard = () => { + const classes = useStyles(); + const isLoggedIn = useGithubLoggedIn(); + + return isLoggedIn ? ( + + ) : ( + + + + ); +}; + export default ReleasesCard; diff --git a/plugins/frontend/backstage-plugin-github-insights/src/hooks/useGithubLoggedIn.tsx b/plugins/frontend/backstage-plugin-github-insights/src/hooks/useGithubLoggedIn.tsx new file mode 100644 index 000000000..e7ff05f44 --- /dev/null +++ b/plugins/frontend/backstage-plugin-github-insights/src/hooks/useGithubLoggedIn.tsx @@ -0,0 +1,67 @@ +/* + * Copyright 2024 Larder Software Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useEffect, useState } from 'react'; +import { + useApi, + githubAuthApiRef, + SessionState, +} from '@backstage/core-plugin-api'; +import { Grid, Tooltip, Button, Typography } from '@material-ui/core'; + +export const GithubNotAuthorized = () => { + const githubApi = useApi(githubAuthApiRef); + return ( + + + + You are not logged into github. You need to be signed in to see the + content of this card. + + + + + + + + + ); +}; + +export const useGithubLoggedIn = () => { + const githubApi = useApi(githubAuthApiRef); + const [isLoggedIn, setIsLoggedIn] = useState(false); + + useEffect(() => { + githubApi.getAccessToken('repo', { optional: true }); + const authSubscription = githubApi.sessionState$().subscribe(state => { + if (state === SessionState.SignedIn) { + setIsLoggedIn(true); + } + }); + return () => { + authSubscription.unsubscribe(); + }; + }, [githubApi]); + + return isLoggedIn; +}; diff --git a/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.tsx b/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.tsx index 1e4330b41..8f05580d2 100644 --- a/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.tsx +++ b/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.tsx @@ -39,6 +39,7 @@ import { Entity } from '@backstage/catalog-model'; import { useEntity } from '@backstage/plugin-catalog-react'; import { Tooltip } from '@material-ui/core'; import { TooltipContent } from './components/TooltipContent'; +import { GithubNotAuthorized, useGithubLoggedIn } from '../useGithubLoggedIn'; const useStyles = makeStyles(theme => ({ infoCard: { @@ -129,6 +130,8 @@ const StatsCard = (props: Props) => { const PullRequestsStatsCard = (props: Props) => { const { entity } = useEntity(); const projectName = isGithubSlugSet(entity); + const isLoggedIn = useGithubLoggedIn(); + if (!projectName || projectName === '') { return ( { /> ); } - return ; + + return isLoggedIn ? ( + + ) : ( + + + + ); }; export default PullRequestsStatsCard; diff --git a/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.tsx b/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.tsx index 55cd6ec84..19e1a231b 100644 --- a/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.tsx +++ b/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.tsx @@ -29,6 +29,7 @@ import { useProjectEntity } from '../useProjectEntity'; import { useUrl } from '../useUrl'; import { useEntity } from '@backstage/plugin-catalog-react'; import { InfoCard, Progress } from '@backstage/core-components'; +import { GithubNotAuthorized, useGithubLoggedIn } from '../useGithubLoggedIn'; const useStyles = makeStyles((theme: Theme) => ({ infoCard: { @@ -207,7 +208,7 @@ export const DependabotAlertInformations: FC = ({ ); }; -export const DependabotAlertsWidget = () => { +const DependabotAlertsWidgetContent = () => { const { entity } = useEntity(); const { owner, repo } = useProjectEntity(entity); const auth = useApi(githubAuthApiRef); @@ -283,3 +284,16 @@ export const DependabotAlertsWidget = () => { ) : null; }; + +export const DependabotAlertsWidget = () => { + const classes = useStyles(); + const isLoggedIn = useGithubLoggedIn(); + + return isLoggedIn ? ( + + ) : ( + + + + ); +}; diff --git a/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.tsx b/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.tsx index efceaa0e2..648455b20 100644 --- a/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.tsx +++ b/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.tsx @@ -35,6 +35,7 @@ import { SecurityInsightFilterState, } from '../../types'; import { useEntity } from '@backstage/plugin-catalog-react'; +import { GithubNotAuthorized, useGithubLoggedIn } from '../useGithubLoggedIn'; const useStyles = makeStyles(theme => ({ infoCard: { @@ -71,7 +72,7 @@ const IssuesCounter: FC = ({ ); }; -export const SecurityInsightsWidget = () => { +const SecurityInsightsWidgetContent = () => { const { entity } = useEntity(); const { owner, repo } = useProjectEntity(entity); const classes = useStyles(); @@ -138,3 +139,16 @@ export const SecurityInsightsWidget = () => { ); }; + +export const SecurityInsightsWidget = () => { + const classes = useStyles(); + const isLoggedIn = useGithubLoggedIn(); + + return isLoggedIn ? ( + + ) : ( + + + + ); +}; diff --git a/plugins/frontend/backstage-plugin-security-insights/src/components/useGithubLoggedIn.tsx b/plugins/frontend/backstage-plugin-security-insights/src/components/useGithubLoggedIn.tsx new file mode 100644 index 000000000..31eb6142a --- /dev/null +++ b/plugins/frontend/backstage-plugin-security-insights/src/components/useGithubLoggedIn.tsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react'; +import { + useApi, + githubAuthApiRef, + SessionState, +} from '@backstage/core-plugin-api'; +import { Grid, Tooltip, Button, Typography } from '@material-ui/core'; + +export const GithubNotAuthorized = () => { + const githubApi = useApi(githubAuthApiRef); + return ( + + + + You are not logged into github. You need to be signed in to see the + content of this card. + + + + + + + + + ); +}; + +export const useGithubLoggedIn = () => { + const githubApi = useApi(githubAuthApiRef); + const [isLoggedIn, setIsLoggedIn] = useState(false); + + useEffect(() => { + githubApi.getAccessToken('repo', { optional: true }); + const authSubscription = githubApi.sessionState$().subscribe(state => { + if (state === SessionState.SignedIn) { + setIsLoggedIn(true); + } + }); + return () => { + authSubscription.unsubscribe(); + }; + }, [githubApi]); + + return isLoggedIn; +}; From 729a3493665b442ff5ce02b360820051ca2c96ca Mon Sep 17 00:00:00 2001 From: Florin Nania Date: Sat, 12 Oct 2024 00:22:05 +0300 Subject: [PATCH 2/3] add changeset --- .changeset/wise-seahorses-swim.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/wise-seahorses-swim.md diff --git a/.changeset/wise-seahorses-swim.md b/.changeset/wise-seahorses-swim.md new file mode 100644 index 000000000..93def84ed --- /dev/null +++ b/.changeset/wise-seahorses-swim.md @@ -0,0 +1,7 @@ +--- +'@roadiehq/backstage-plugin-github-pull-requests': minor +'@roadiehq/backstage-plugin-security-insights': minor +'@roadiehq/backstage-plugin-github-insights': minor +--- + +Add SignIn button to all Github related cards from entity overwiew page. From cdbdf378faad099f5426d8997d6a7d6821be5c10 Mon Sep 17 00:00:00 2001 From: Florin Nania Date: Sun, 13 Oct 2024 23:51:39 +0300 Subject: [PATCH 3/3] fix tests --- .../Widgets/ContributorsCard/ContributorsCard.test.tsx | 6 ++++++ .../Widgets/LanguagesCard/LanguagesCard.test.tsx | 6 ++++++ .../Widgets/ReleasesCard/ReleasesCard.test.tsx | 6 ++++++ .../PullRequestsStatsCard.test.tsx | 6 ++++++ .../DependabotAlertsWidget.test.tsx | 10 ++++++---- .../SecurityInsightsWidget.test.tsx | 6 ++++++ 6 files changed, 36 insertions(+), 4 deletions(-) diff --git a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.test.tsx b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.test.tsx index 9ca69afe6..bae0c90e7 100644 --- a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.test.tsx +++ b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ContributorsCard/ContributorsCard.test.tsx @@ -38,6 +38,12 @@ import { defaultIntegrationsConfig } from '../../../mocks/scmIntegrationsApiMock const mockGithubAuth = { getAccessToken: async (_: string[]) => 'test-token', + sessionState$: jest.fn(() => ({ + subscribe: (fn: (a: string) => void) => { + fn('SignedIn'); + return { unsubscribe: jest.fn() }; + }, + })), }; const apis: [AnyApiRef, Partial][] = [ diff --git a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.test.tsx b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.test.tsx index d33fe8bb9..84314d0ab 100644 --- a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.test.tsx +++ b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/LanguagesCard/LanguagesCard.test.tsx @@ -38,6 +38,12 @@ import { defaultIntegrationsConfig } from '../../../mocks/scmIntegrationsApiMock const mockGithubAuth = { getAccessToken: async (_: string[]) => 'test-token', + sessionState$: jest.fn(() => ({ + subscribe: (fn: (a: string) => void) => { + fn('SignedIn'); + return { unsubscribe: jest.fn() }; + }, + })), }; const apis: [AnyApiRef, Partial][] = [ diff --git a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.test.tsx b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.test.tsx index a609e682a..287c9f66e 100644 --- a/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.test.tsx +++ b/plugins/frontend/backstage-plugin-github-insights/src/components/Widgets/ReleasesCard/ReleasesCard.test.tsx @@ -38,6 +38,12 @@ import { defaultIntegrationsConfig } from '../../../mocks/scmIntegrationsApiMock const mockGithubAuth = { getAccessToken: async (_: string[]) => 'test-token', + sessionState$: jest.fn(() => ({ + subscribe: (fn: (a: string) => void) => { + fn('SignedIn'); + return { unsubscribe: jest.fn() }; + }, + })), }; const apis: [AnyApiRef, Partial][] = [ diff --git a/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.test.tsx b/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.test.tsx index 4733d9324..58cb591bd 100644 --- a/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.test.tsx +++ b/plugins/frontend/backstage-plugin-github-pull-requests/src/components/PullRequestsStatsCard/PullRequestsStatsCard.test.tsx @@ -35,6 +35,12 @@ import { handlers } from '../../mocks/handlers'; const mockGithubAuth = { getAccessToken: async (_: string[]) => 'test-token', + sessionState$: jest.fn(() => ({ + subscribe: (fn: (a: string) => void) => { + fn('SignedIn'); + return { unsubscribe: jest.fn() }; + }, + })), }; const config = { diff --git a/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.test.tsx b/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.test.tsx index 7a4eabfe2..8bad59c84 100644 --- a/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.test.tsx +++ b/plugins/frontend/backstage-plugin-security-insights/src/components/DependabotAlertsWidget/DependabotAlertsWidget.test.tsx @@ -57,6 +57,12 @@ const GRAPHQL_GITHUB_API = graphql.link('https://api.github.com/graphql'); const mockGithubAuth = { getAccessToken: async (_: string[]) => 'test-token', + sessionState$: jest.fn(() => ({ + subscribe: (fn: (a: string) => void) => { + fn('SignedIn'); + return { unsubscribe: jest.fn() }; + }, + })), }; const config = { @@ -87,10 +93,6 @@ describe('Dependabot alerts overview', () => { const worker = setupServer(); setupRequestMockHandlers(worker); - beforeEach(() => { - jest.resetAllMocks(); - }); - describe('GithubDependabotAlertsTable', () => { beforeEach(() => { entity = entityStub; diff --git a/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.test.tsx b/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.test.tsx index 8b871bc3e..381881fd9 100644 --- a/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.test.tsx +++ b/plugins/frontend/backstage-plugin-security-insights/src/components/SecurityInsightsWidget/SecurityInsightsWidget.test.tsx @@ -35,6 +35,12 @@ import { EntityProvider } from '@backstage/plugin-catalog-react'; const mockGithubAuth = { getAccessToken: async (_: string[]) => 'test-token', + sessionState$: jest.fn(() => ({ + subscribe: (fn: (a: string) => void) => { + fn('SignedIn'); + return { unsubscribe: jest.fn() }; + }, + })), }; const config = {