diff --git a/.changeset/funny-pants-unite.md b/.changeset/funny-pants-unite.md new file mode 100644 index 0000000000..349cf54b8f --- /dev/null +++ b/.changeset/funny-pants-unite.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-import': patch +--- + +Fix importing yaml files from URLs with trailing query parameters. diff --git a/plugins/catalog-import/src/api/CatalogImportClient.test.ts b/plugins/catalog-import/src/api/CatalogImportClient.test.ts index bae363d700..44f33dc660 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.test.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.test.ts @@ -17,7 +17,7 @@ const octokit = { repos: { get: () => Promise.resolve({ data: { default_branch: 'main' } }), - createOrUpdateFileContents: jest.fn().mockImplementation(async () => {}), + createOrUpdateFileContents: jest.fn(async () => {}), }, search: { code: jest.fn(), @@ -26,10 +26,10 @@ const octokit = { getRef: async () => ({ data: { object: { sha: 'any' } }, }), - createRef: jest.fn().mockImplementation(async () => {}), + createRef: jest.fn(async () => {}), }, pulls: { - create: jest.fn().mockImplementation(async () => ({ + create: jest.fn(async () => ({ data: { html_url: 'http://pull/request/0', }, @@ -46,6 +46,8 @@ jest.doMock('@octokit/rest', () => { return { Octokit }; }); +import { ConfigReader, UrlPatternDiscovery } from '@backstage/core-app-api'; +import { OAuthApi } from '@backstage/core-plugin-api'; import { ScmIntegrations } from '@backstage/integration'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { msw } from '@backstage/test-utils'; @@ -54,9 +56,6 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { CatalogImportClient } from './CatalogImportClient'; -import { ConfigReader, UrlPatternDiscovery } from '@backstage/core-app-api'; -import { OAuthApi } from '@backstage/core-plugin-api'; - describe('CatalogImportClient', () => { const server = setupServer(); msw.setupDefaultHandlers(server); @@ -119,6 +118,11 @@ describe('CatalogImportClient', () => { }); }); + afterEach(() => { + jest.restoreAllMocks(); + jest.clearAllMocks(); + }); + describe('analyzeUrl', () => { it('should add yaml location', async () => { catalogApi.addLocation.mockResolvedValueOnce({ @@ -167,6 +171,53 @@ describe('CatalogImportClient', () => { }); }); + it('should add yaml location, if url includes query parameters', async () => { + catalogApi.addLocation.mockResolvedValueOnce({ + location: { + id: 'id-0', + type: 'url', + target: 'http://example.com/folder/catalog-info.yaml?branch=test', + }, + entities: [ + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'my-entity', + namespace: 'my-namespace', + }, + }, + ], + }); + + await expect( + catalogImportClient.analyzeUrl( + 'http://example.com/folder/catalog-info.yaml?branch=test', + ), + ).resolves.toEqual({ + locations: [ + { + entities: [ + { + kind: 'Component', + name: 'my-entity', + namespace: 'my-namespace', + }, + ], + target: 'http://example.com/folder/catalog-info.yaml?branch=test', + }, + ], + type: 'locations', + }); + + expect(catalogApi.addLocation).toBeCalledTimes(1); + expect(catalogApi.addLocation.mock.calls[0][0]).toEqual({ + type: 'url', + target: 'http://example.com/folder/catalog-info.yaml?branch=test', + dryRun: true, + }); + }); + it('should reject for integrations that are not github ones', async () => { await expect( catalogImportClient.analyzeUrl( diff --git a/plugins/catalog-import/src/api/CatalogImportClient.ts b/plugins/catalog-import/src/api/CatalogImportClient.ts index aa29c95247..d7a2028330 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.ts @@ -16,20 +16,20 @@ import { CatalogApi } from '@backstage/catalog-client'; import { EntityName } from '@backstage/catalog-model'; -import { - GitHubIntegrationConfig, - ScmIntegrationRegistry, -} from '@backstage/integration'; -import { Base64 } from 'js-base64'; -import { Octokit } from '@octokit/rest'; -import { PartialEntity } from '../types'; -import { AnalyzeResult, CatalogImportApi } from './CatalogImportApi'; -import { getGithubIntegrationConfig } from './GitHub'; import { DiscoveryApi, IdentityApi, OAuthApi, } from '@backstage/core-plugin-api'; +import { + GitHubIntegrationConfig, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { Octokit } from '@octokit/rest'; +import { Base64 } from 'js-base64'; +import { PartialEntity } from '../types'; +import { AnalyzeResult, CatalogImportApi } from './CatalogImportApi'; +import { getGithubIntegrationConfig } from './GitHub'; export class CatalogImportClient implements CatalogImportApi { private readonly discoveryApi: DiscoveryApi; @@ -53,7 +53,7 @@ export class CatalogImportClient implements CatalogImportApi { } async analyzeUrl(url: string): Promise { - if (url.match(/\.ya?ml$/)) { + if (new URL(url).pathname.match(/\.ya?ml$/)) { const location = await this.catalogApi.addLocation({ type: 'url', target: url,