diff --git a/plugins/catalog-import/src/api/AzureDevops.ts b/plugins/catalog-import/src/api/AzureDevops.ts index d2cf9eacd7..d028c75129 100644 --- a/plugins/catalog-import/src/api/AzureDevops.ts +++ b/plugins/catalog-import/src/api/AzureDevops.ts @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AzureIntegration } from '@backstage/integration'; import { ScmAuthApi } from '@backstage/integration-react'; import { ConfigApi } from '@backstage/core-plugin-api'; import { getBranchName, getCatalogFilename } from '../components/helpers'; import { createAzurePullRequest } from './AzureRepoApiClient'; -import parseGitUrl from 'git-url-parse'; +import { parseRepoUrl } from './util'; export interface AzureRepoParts { tenantUrl: string; @@ -26,17 +25,19 @@ export interface AzureRepoParts { project: string; } -export function parseAzureUrl( - repoUrl: string, - integration: AzureIntegration, -): AzureRepoParts { - const { organization, owner, name } = parseGitUrl(repoUrl); - const tenantUrl = `https://${integration.config.host}/${organization}`; - return { tenantUrl, repoName: name, project: owner }; +export function parseAzureUrl(repoUrl: string): AzureRepoParts { + const { org, repo, project, host } = parseRepoUrl(repoUrl); + if (!org || !repo || !project) { + throw new Error( + 'Invalid AzureDevops Repository. Please use a valid repository url and try again ', + ); + } + const tenantUrl = `https://${host}/${org}`; + + return { tenantUrl, repoName: repo, project: project }; } export async function submitAzurePrToRepo( - integration: AzureIntegration, options: { title: string; body: string; @@ -57,10 +58,7 @@ export async function submitAzurePrToRepo( repoWrite: true, }, }); - const { tenantUrl, repoName, project } = parseAzureUrl( - repositoryUrl, - integration, - ); + const { tenantUrl, repoName, project } = parseAzureUrl(repositoryUrl); const result = await createAzurePullRequest({ token, fileContent, diff --git a/plugins/catalog-import/src/api/CatalogImportClient.ts b/plugins/catalog-import/src/api/CatalogImportClient.ts index e05e3882c4..fe7fc5c59e 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.ts @@ -21,7 +21,6 @@ import { IdentityApi, } from '@backstage/core-plugin-api'; import { - AzureIntegration, GithubIntegration, ScmIntegrationRegistry, } from '@backstage/integration'; @@ -208,7 +207,6 @@ the component will become available.\n\nFor more information, read an \ } case 'azure': { return submitAzurePrToRepo( - provider as AzureIntegration, { repositoryUrl, fileContent, diff --git a/plugins/catalog-import/src/api/util.test.ts b/plugins/catalog-import/src/api/util.test.ts new file mode 100644 index 0000000000..3a14a2e7ae --- /dev/null +++ b/plugins/catalog-import/src/api/util.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2024 The Backstage Authors + * + * 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 { parseRepoUrl } from './util'; + +describe('parseRepoUrl', () => { + it('parses Azure DevOps Cloud url', async () => { + const result = parseRepoUrl( + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('dev.azure.com'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); + + it('parses Azure DevOps Server url', async () => { + const result = parseRepoUrl( + 'https://server.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('server.com'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); + + it('parses TFS subpath Url', async () => { + const result = parseRepoUrl( + 'https://server.com/tfs/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('server.com/tfs'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); +}); diff --git a/plugins/catalog-import/src/api/util.ts b/plugins/catalog-import/src/api/util.ts new file mode 100644 index 0000000000..dc6b4ae2a6 --- /dev/null +++ b/plugins/catalog-import/src/api/util.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2024 The Backstage Authors + * + * 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. + */ +export function parseRepoUrl(sourceUrl: string) { + const url = new URL(sourceUrl); + + let host = url.host; + let org; + let project; + let repo; + + const parts = url.pathname.split('/').map(part => decodeURIComponent(part)); + if (parts[2] === '_git') { + org = parts[1]; + project = repo = parts[3]; + } else if (parts[3] === '_git') { + org = parts[1]; + project = parts[2]; + repo = parts[4]; + } else if (parts[4] === '_git') { + host = `${host}/${parts[1]}`; + org = parts[2]; + project = parts[3]; + repo = parts[5]; + } + + return { host, org, project, repo }; +} diff --git a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx index 67979f1a3d..be8a457572 100644 --- a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx +++ b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx @@ -29,8 +29,7 @@ import { useCatalogFilename } from '../../hooks'; */ export interface ImportInfoCardProps { exampleLocationUrl?: string; - exampleGitRepositoryUrl?: string; - exampleAzureRepositoryUrl?: string; + exampleRepositoryUrl?: string; } /** @@ -41,8 +40,7 @@ export interface ImportInfoCardProps { export const ImportInfoCard = (props: ImportInfoCardProps) => { const { exampleLocationUrl = 'https://github.com/backstage/backstage/blob/master/catalog-info.yaml', - exampleGitRepositoryUrl = 'https://github.com/backstage/backstage', - exampleAzureRepositoryUrl = 'https://dev.azure.com/org-name/project-name/_git/repo-name, + exampleRepositoryUrl = 'https://github.com/backstage/backstage', } = props; const configApi = useApi(configApiRef); @@ -79,18 +77,10 @@ export const ImportInfoCard = (props: ImportInfoCardProps) => { <> Link to a repository{' '} - - - - - GitHub Example: {exampleGitRepositoryUrl} + - Azure Example: {exampleAzureRepositoryUrl} + Example: {exampleRepositoryUrl} The wizard discovers all {catalogFilename} files in the