From: Wouter Admiraal Date: Thu, 22 Apr 2021 15:08:28 +0000 (+0200) Subject: SONAR-14738 Make tutorial links available to project admins X-Git-Tag: 8.9.0.43852~70 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=58371ceb535bbeedfda77c9143867d34173d8170;p=sonarqube.git SONAR-14738 Make tutorial links available to project admins --- diff --git a/server/sonar-web/src/main/js/api/alm-settings.ts b/server/sonar-web/src/main/js/api/alm-settings.ts index 9491b1809ec..5f264a5f331 100644 --- a/server/sonar-web/src/main/js/api/alm-settings.ts +++ b/server/sonar-web/src/main/js/api/alm-settings.ts @@ -36,17 +36,15 @@ import { } from '../types/alm-settings'; export function getAlmDefinitions(): Promise { - return getAlmDefinitionsNoCatch().catch(throwGlobalError); -} - -export function getAlmDefinitionsNoCatch(): Promise { return getJSON('/api/alm_settings/list_definitions'); } export function getAlmSettings(project?: string): Promise { - return getJSON('/api/alm_settings/list', { project }) - .then(({ almSettings }) => almSettings) - .catch(throwGlobalError); + return getAlmSettingsNoCatch(project).catch(throwGlobalError); +} + +export function getAlmSettingsNoCatch(project?: string): Promise { + return getJSON('/api/alm_settings/list', { project }).then(({ almSettings }) => almSettings); } export function validateAlmSettings(key: string): Promise { diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx index 28d0bc67435..2598ce3a320 100644 --- a/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx @@ -20,9 +20,9 @@ import * as React from 'react'; import { WithRouterProps } from 'react-router'; import { getHostUrl } from 'sonar-ui-common/helpers/urls'; -import { getAlmDefinitionsNoCatch } from '../../api/alm-settings'; +import { getAlmSettingsNoCatch } from '../../api/alm-settings'; import { getValues } from '../../api/settings'; -import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../types/alm-settings'; +import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings'; import { SettingsKey } from '../../types/settings'; import { withRouter } from '../hoc/withRouter'; import TutorialSelectionRenderer from './TutorialSelectionRenderer'; @@ -35,7 +35,7 @@ interface Props extends Pick { } interface State { - almBinding?: AlmBindingDefinition; + almBinding?: AlmSettingsInstance; baseUrl: string; forceManual: boolean; loading: boolean; @@ -64,17 +64,16 @@ export class TutorialSelection extends React.PureComponent { } fetchAlmBindings = async () => { - const { projectBinding } = this.props; + const { component, projectBinding } = this.props; if (projectBinding === undefined) { this.setState({ forceManual: true }); } else { - const almDefinitions = await getAlmDefinitionsNoCatch().catch(() => undefined); + const almSettings = await getAlmSettingsNoCatch(component.key).catch(() => undefined); if (this.mounted) { let almBinding; - if (almDefinitions !== undefined) { - const specificDefinitions = almDefinitions[projectBinding.alm] as AlmBindingDefinition[]; - almBinding = specificDefinitions.find(d => d.key === projectBinding.key); + if (almSettings !== undefined) { + almBinding = almSettings.find(d => d.key === projectBinding.key); } this.setState({ almBinding, forceManual: false }); } diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx index 5685484eabf..ab996323948 100644 --- a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx @@ -20,12 +20,7 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import { - AlmBindingDefinition, - AlmKeys, - isGithubBindingDefinition, - ProjectAlmBindingResponse -} from '../../types/alm-settings'; +import { AlmKeys, AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings'; import AzurePipelinesTutorial from './azure-pipelines/AzurePipelinesTutorial'; import GitHubActionTutorial from './github-action/GitHubActionTutorial'; import GitLabCITutorial from './gitlabci/GitLabCITutorial'; @@ -34,7 +29,7 @@ import ManualTutorial from './manual/ManualTutorial'; import { TutorialModes } from './types'; export interface TutorialSelectionRendererProps { - almBinding?: AlmBindingDefinition; + almBinding?: AlmSettingsInstance; baseUrl: string; component: T.Component; currentUser: T.LoggedInUser; @@ -160,17 +155,15 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender )} - {selectedTutorial === TutorialModes.GitHubActions && - isGithubBindingDefinition(almBinding) && - projectBinding !== undefined && ( - - )} + {selectedTutorial === TutorialModes.GitHubActions && projectBinding !== undefined && ( + + )} {selectedTutorial === TutorialModes.Jenkins && projectBinding !== undefined && ( ({ })); jest.mock('../../../api/alm-settings', () => ({ - getAlmDefinitionsNoCatch: jest.fn().mockRejectedValue(null) + getAlmSettingsNoCatch: jest.fn().mockRejectedValue(null) })); jest.mock('../../../api/settings', () => ({ @@ -70,10 +69,11 @@ it('should not select anything if project is bound', async () => { it('should correctly find the global ALM binding definition', async () => { const key = 'foo'; - const almBinding = mockBitbucketBindingDefinition({ key }); - (getAlmDefinitionsNoCatch as jest.Mock).mockResolvedValueOnce({ - [AlmKeys.BitbucketServer]: [almBinding] - }); + const almBinding = mockAlmSettingsInstance({ key }); + (getAlmSettingsNoCatch as jest.Mock).mockResolvedValueOnce([ + almBinding, + mockAlmSettingsInstance({ key: 'bar' }) + ]); const wrapper = shallowRender({ projectBinding: mockProjectBitbucketBindingResponse({ key }) }); await waitAndUpdate(wrapper); expect(wrapper.state().almBinding).toBe(almBinding); diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx index b67a9349fc6..bad6a40ffbe 100644 --- a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx @@ -21,8 +21,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { click } from 'sonar-ui-common/helpers/testUtils'; import { - mockBitbucketBindingDefinition, - mockGithubBindingDefinition, + mockAlmSettingsInstance, mockProjectAzureBindingResponse, mockProjectBitbucketBindingResponse, mockProjectGithubBindingResponse, @@ -57,7 +56,6 @@ it('should render correctly', () => { ).toMatchSnapshot('jenkins tutorial'); expect( shallowRender({ - almBinding: mockGithubBindingDefinition(), selectedTutorial: TutorialModes.GitHubActions, projectBinding: mockProjectGithubBindingResponse() }) @@ -141,7 +139,7 @@ it('should allow mode selection for Azure DevOps', () => { function shallowRender(props: Partial = {}) { return shallow( { }); describe('buildGithubLink', () => { + const projectBinding = mockProjectGithubBindingResponse({ repository: 'owner/reponame' }); + it('should work for GitHub Enterprise', () => { expect( buildGithubLink( - mockGithubBindingDefinition({ url: 'https://github.company.com/api/v3' }), - mockProjectGithubBindingResponse({ repository: 'owner/reponame' }) + mockAlmSettingsInstance({ url: 'https://github.company.com/api/v3' }), + projectBinding ) ).toBe('https://github.company.com/owner/reponame'); }); it('should work for github.com', () => { expect( - buildGithubLink( - mockGithubBindingDefinition({ url: 'http://api.github.com/' }), - mockProjectGithubBindingResponse({ repository: 'owner/reponame' }) - ) + buildGithubLink(mockAlmSettingsInstance({ url: 'http://api.github.com/' }), projectBinding) ).toBe('https://github.com/owner/reponame'); }); + + it('should return null if there is no url defined', () => { + expect(buildGithubLink(mockAlmSettingsInstance({ url: undefined }), projectBinding)).toBeNull(); + }); }); diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx index 1303a4cd85d..61eded63854 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { GithubBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings'; import Step from '../components/Step'; import SecretStep from './SecretStep'; import YamlFileStep from './YamlFileStep'; @@ -30,7 +30,7 @@ export enum Steps { } export interface GitHubActionTutorialProps { - almBinding?: GithubBindingDefinition; + almBinding?: AlmSettingsInstance; baseUrl: string; component: T.Component; currentUser: T.LoggedInUser; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx index 60c5b27a8c0..db50249deae 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx @@ -22,13 +22,13 @@ import { FormattedMessage } from 'react-intl'; import { Button } from 'sonar-ui-common/components/controls/buttons'; import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { GithubBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings'; import SentenceWithHighlights from '../components/SentenceWithHighlights'; import TokenStepGenerator from '../components/TokenStepGenerator'; import { buildGithubLink } from '../utils'; export interface SecretStepProps { - almBinding?: GithubBindingDefinition; + almBinding?: AlmSettingsInstance; baseUrl: string; component: T.Component; currentUser: T.LoggedInUser; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx index 81d6aca3767..ad77015e156 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { - mockGithubBindingDefinition, + mockAlmSettingsInstance, mockProjectGithubBindingResponse } from '../../../../helpers/mocks/alm-settings'; import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks'; @@ -38,7 +38,7 @@ it('should render correctly', () => { function shallowRender(props: Partial = {}) { return shallow( { expect(shallowRender()).toMatchSnapshot('default'); expect( shallowRender({ - almBinding: mockGithubBindingDefinition() + almBinding: mockAlmSettingsInstance({ url: 'http://github.enterprise.com/api/v3' }) }) ).toMatchSnapshot('with binding information'); }); diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx index 15ba4fc05ba..3ed4d76d950 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx @@ -24,7 +24,7 @@ import { translate } from 'sonar-ui-common/helpers/l10n'; import { getAppState, getCurrentUserSetting, Store } from '../../../store/rootReducer'; import { setCurrentUserSetting } from '../../../store/users'; import { - AlmBindingDefinition, + AlmSettingsInstance, isProjectBitbucketBindingResponse, isProjectGitHubBindingResponse, isProjectGitLabBindingResponse, @@ -37,7 +37,7 @@ import PreRequisitesStep from './PreRequisitesStep'; import WebhookStep from './WebhookStep'; export interface JenkinsTutorialProps { - almBinding?: AlmBindingDefinition; + almBinding?: AlmSettingsInstance; branchesEnabled: boolean; component: T.Component; projectBinding: ProjectAlmBindingResponse; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx index eda16fc1a5b..cfd44f0a57b 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx @@ -21,8 +21,7 @@ import * as React from 'react'; import { Button } from 'sonar-ui-common/components/controls/buttons'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { - AlmBindingDefinition, - isGithubBindingDefinition, + AlmSettingsInstance, isProjectBitbucketBindingResponse, isProjectGitHubBindingResponse, isProjectGitLabBindingResponse, @@ -37,7 +36,7 @@ import Step from '../components/Step'; import { buildGithubLink } from '../utils'; export interface MultiBranchPipelineStepProps { - almBinding?: AlmBindingDefinition; + almBinding?: AlmSettingsInstance; finished: boolean; onDone: () => void; onOpen: () => void; @@ -101,10 +100,11 @@ export default function MultiBranchPipelineStep(props: MultiBranchPipelineStepPr
  • - {isGithubBindingDefinition(almBinding) ? ( + {almBinding !== undefined && + buildGithubLink(almBinding, projectBinding) !== null ? ( ) : ( diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx index 86d8129d9ed..2fcb02a05d6 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx @@ -22,10 +22,8 @@ import { FormattedMessage } from 'react-intl'; import { Button, ButtonLink } from 'sonar-ui-common/components/controls/buttons'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { - AlmBindingDefinition, AlmKeys, - isBitbucketBindingDefinition, - isGithubBindingDefinition, + AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings'; import Step from '../components/Step'; @@ -34,7 +32,7 @@ import WebhookStepGithub from './WebhookStepGithub'; import WebhookStepGitLab from './WebhookStepGitLab'; export interface WebhookStepProps { - almBinding?: AlmBindingDefinition; + almBinding?: AlmSettingsInstance; branchesEnabled: boolean; finished: boolean; onDone: () => void; @@ -50,7 +48,7 @@ function renderAlmSpecificInstructions(props: WebhookStepProps) { case AlmKeys.BitbucketServer: return ( @@ -59,7 +57,7 @@ function renderAlmSpecificInstructions(props: WebhookStepProps) { case AlmKeys.GitHub: return ( diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepBitbucket.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepBitbucket.tsx index 51b6813312d..03ab516429e 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepBitbucket.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepBitbucket.tsx @@ -21,13 +21,13 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { BitbucketBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings'; import CodeSnippet from '../../common/CodeSnippet'; import LabelActionPair from '../components/LabelActionPair'; import SentenceWithHighlights from '../components/SentenceWithHighlights'; export interface WebhookStepBitbucketProps { - almBinding?: BitbucketBindingDefinition; + almBinding?: AlmSettingsInstance; branchesEnabled: boolean; projectBinding: ProjectAlmBindingResponse; } @@ -43,6 +43,7 @@ export default function WebhookStepBitbucket(props: WebhookStepBitbucketProps) { const linkUrl = almBinding && + almBinding.url && `${almBinding.url}/plugins/servlet/webhooks/projects/${projectBinding.repository}/repos/${projectBinding.slug}/create`; return ( diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepGithub.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepGithub.tsx index 37ffc16ddb2..39fafeb28ac 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepGithub.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepGithub.tsx @@ -20,14 +20,14 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { GithubBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings'; import CodeSnippet from '../../common/CodeSnippet'; import LabelActionPair from '../components/LabelActionPair'; import SentenceWithHighlights from '../components/SentenceWithHighlights'; import { buildGithubLink } from '../utils'; export interface WebhookStepGithubProps { - almBinding?: GithubBindingDefinition; + almBinding?: AlmSettingsInstance; branchesEnabled: boolean; projectBinding: ProjectAlmBindingResponse; } diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx index 5be36aa3310..8b10fce9399 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx @@ -20,6 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { + mockAlmSettingsInstance, mockProjectBitbucketBindingResponse, mockProjectGithubBindingResponse, mockProjectGitLabBindingResponse @@ -32,7 +33,12 @@ it('should render correctly', () => { expect(wrapper).toMatchSnapshot('Step wrapper'); expect(renderStepContent(wrapper)).toMatchSnapshot('content for bitbucket'); expect( - renderStepContent(shallowRender({ projectBinding: mockProjectGithubBindingResponse() })) + renderStepContent( + shallowRender({ + almBinding: mockAlmSettingsInstance({ url: 'https://api.github.com/' }), + projectBinding: mockProjectGithubBindingResponse() + }) + ) ).toMatchSnapshot('content for github'); expect( renderStepContent(shallowRender({ projectBinding: mockProjectGitLabBindingResponse() })) diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/WebhookStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/WebhookStep-test.tsx index e08fa92d0c7..11406ff5d15 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/WebhookStep-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/WebhookStep-test.tsx @@ -20,10 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { - mockAzureBindingDefinition, - mockBitbucketBindingDefinition, - mockGithubBindingDefinition, - mockGitlabBindingDefinition, + mockAlmSettingsInstance, mockProjectAlmBindingResponse, mockProjectBitbucketBindingResponse, mockProjectGithubBindingResponse @@ -33,24 +30,12 @@ import { renderStepContent } from '../test-utils'; import WebhookStep, { WebhookStepProps } from '../WebhookStep'; it.each([ - [ - AlmKeys.Azure, - mockAzureBindingDefinition(), - mockProjectAlmBindingResponse({ alm: AlmKeys.Azure }) - ], - [ - AlmKeys.BitbucketServer, - mockBitbucketBindingDefinition(), - mockProjectBitbucketBindingResponse() - ], - [AlmKeys.GitHub, mockGithubBindingDefinition(), mockProjectGithubBindingResponse()], - [ - AlmKeys.GitLab, - mockGitlabBindingDefinition(), - mockProjectAlmBindingResponse({ alm: AlmKeys.GitLab }) - ] -])('it should render correctly for %s', (_, almBinding, projectBinding) => { - const wrapper = shallowRender({ almBinding, projectBinding }); + [AlmKeys.Azure, mockProjectAlmBindingResponse({ alm: AlmKeys.Azure })], + [AlmKeys.BitbucketServer, mockProjectBitbucketBindingResponse()], + [AlmKeys.GitHub, mockProjectGithubBindingResponse()], + [AlmKeys.GitLab, mockProjectAlmBindingResponse({ alm: AlmKeys.GitLab })] +])('it should render correctly for %s', (_, projectBinding) => { + const wrapper = shallowRender({ projectBinding }); expect(wrapper).toMatchSnapshot('wrapper'); expect(renderStepContent(wrapper)).toMatchSnapshot('content'); }); @@ -58,7 +43,7 @@ it.each([ function shallowRender(props: Partial = {}) { return shallow( { function shallowRender(props: Partial = {}) { return shallow( { function shallowRender(props: Partial = {}) { return shallow(
  • -
  • diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/WebhookStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/WebhookStep-test.tsx.snap index 17b5fd12b0f..b311ac84df7 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/WebhookStep-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/WebhookStep-test.tsx.snap @@ -70,9 +70,8 @@ exports[`it should render correctly for bitbucket: content 1`] = ` string { return os === 'win' ? (s: string) => `"${s}"` : (s: string) => s; @@ -56,9 +56,13 @@ export function getUniqueTokenName(tokens: T.UserToken[], initialTokenName = '') } export function buildGithubLink( - almBinding: GithubBindingDefinition, + almBinding: AlmSettingsInstance, projectBinding: ProjectAlmBindingResponse ) { + if (almBinding.url === undefined) { + return null; + } + // strip the api path: const urlRoot = almBinding.url .replace(/\/api\/v\d+\/?$/, '') // GH Enterprise