diff --git a/.changeset/spotty-carrots-refuse.md b/.changeset/spotty-carrots-refuse.md new file mode 100644 index 0000000000..47dcee7239 --- /dev/null +++ b/.changeset/spotty-carrots-refuse.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-import': patch +--- + +fix related to component name not adhering to kubernetes valid object name. diff --git a/plugins/catalog-import/src/api/CatalogImportClient.test.ts b/plugins/catalog-import/src/api/CatalogImportClient.test.ts index 646a3b7bf9..df44ec94c4 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.test.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.test.ts @@ -556,10 +556,29 @@ describe('CatalogImportClient', () => { describe('submitPullRequest', () => { it('should create GitHub pull request', async () => { + catalogApi.validateEntity.mockResolvedValueOnce({ + valid: true, + }); await expect( catalogImportClient.submitPullRequest({ repositoryUrl: 'https://github.com/backstage/backstage', - fileContent: 'some content 🤖', + fileContent: ` + { + "apiVersion": "backstage.io/v1alpha1", + "kind": "Component", + "metadata": { + "name": "valid-name", + "annotations": { + "github.com/project-slug": "backstage/example-repo" + } + }, + "spec": { + "type": "other", + "lifecycle": "unknown", + "owner": "backstage" + } + } + `, title: 'A title/message', body: 'A body', }), @@ -568,7 +587,7 @@ describe('CatalogImportClient', () => { location: 'https://github.com/backstage/backstage/blob/main/catalog-info.yaml', }); - + expect(catalogApi.validateEntity).toHaveBeenCalledTimes(1); expect( (new Octokit().git.createRef as any as jest.Mock).mock.calls[0][0], ).toEqual({ @@ -585,7 +604,8 @@ describe('CatalogImportClient', () => { repo: 'backstage', path: 'catalog-info.yaml', message: 'A title/message', - content: 'c29tZSBjb250ZW50IPCfpJY=', + content: + 'CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJhcGlWZXJzaW9uIjogImJhY2tzdGFnZS5pby92MWFscGhhMSIsCiAgICAgICAgICAgICAgICAia2luZCI6ICJDb21wb25lbnQiLAogICAgICAgICAgICAgICAgIm1ldGFkYXRhIjogewogICAgICAgICAgICAgICAgICAibmFtZSI6ICJ2YWxpZC1uYW1lIiwKICAgICAgICAgICAgICAgICAgImFubm90YXRpb25zIjogewogICAgICAgICAgICAgICAgICAgICAgImdpdGh1Yi5jb20vcHJvamVjdC1zbHVnIjogImJhY2tzdGFnZS9leGFtcGxlLXJlcG8iCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAic3BlYyI6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiOiAib3RoZXIiLAogICAgICAgICAgICAgICAgICAibGlmZWN5Y2xlIjogInVua25vd24iLAogICAgICAgICAgICAgICAgICAib3duZXIiOiAiYmFja3N0YWdlIgogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAg', branch: 'backstage-integration', }); expect( @@ -599,8 +619,39 @@ describe('CatalogImportClient', () => { base: 'main', }); }); - + it('Submit Pull Request with invalid component name', async () => { + const ErrorMessage = + 'Policy check failed for component:default/invalid name; caused by Error: "metadata.name" is not valid; expected a string that is sequences of [a-zA-Z0-9] separated by any of [-_.], at most 63 characters in total but found "invalid name". To learn more about catalog file format, visit: https://github.com/backstage/backstage/blob/master/docs/architecture-decisions/adr002-default-catalog-file-format.md'; + catalogApi.validateEntity.mockRejectedValueOnce(new Error(ErrorMessage)); + await expect( + catalogImportClient.submitPullRequest({ + repositoryUrl: 'https://github.com/acme-corp/our-awesome-api', + fileContent: ` + { + "apiVersion": "backstage.io/v1alpha1", + "kind": "Component", + "metadata": { + "name": "invalid name", + "annotations": { + "github.com/project-slug": "backstage/example-repo" + } + }, + "spec": { + "type": "other", + "lifecycle": "unknown", + "owner": "backstage" + } + } + `, + title: 'A title/message', + body: 'A body', + }), + ).rejects.toThrow(ErrorMessage); + }); it('should create GitHub pull request with custom filename and branch name', async () => { + catalogApi.validateEntity.mockResolvedValueOnce({ + valid: true, + }); const entityFilename = 'anvil.yaml'; const pullRequestBranchName = 'anvil-integration'; @@ -623,7 +674,23 @@ describe('CatalogImportClient', () => { await expect( catalogImportClient.submitPullRequest({ repositoryUrl: 'https://github.com/acme-corp/our-awesome-api', - fileContent: '', + fileContent: ` + { + "apiVersion": "backstage.io/v1alpha1", + "kind": "Component", + "metadata": { + "name": "valid-name", + "annotations": { + "github.com/project-slug": "backstage/example-repo" + } + }, + "spec": { + "type": "other", + "lifecycle": "unknown", + "owner": "backstage" + } + } + `, title: `Add ${entityFilename} config file`, body: `Add ${entityFilename} config file`, }), diff --git a/plugins/catalog-import/src/api/CatalogImportClient.ts b/plugins/catalog-import/src/api/CatalogImportClient.ts index 7b582c90fa..cecb85f9a3 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.ts @@ -28,6 +28,7 @@ import { ScmAuthApi } from '@backstage/integration-react'; import { Octokit } from '@octokit/rest'; import { Base64 } from 'js-base64'; import { AnalyzeResult, CatalogImportApi } from './CatalogImportApi'; +import YAML from 'yaml'; import { getGithubIntegrationConfig } from './GitHub'; import { getBranchName, getCatalogFilename } from '../components/helpers'; import { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common'; @@ -175,7 +176,15 @@ the component will become available.\n\nFor more information, read an \ body: string; }): Promise<{ link: string; location: string }> { const { repositoryUrl, fileContent, title, body } = options; + const parseData = YAML.parse(fileContent); + const validationResponse = await this.catalogApi.validateEntity( + parseData, + `url:${repositoryUrl}`, + ); + if (!validationResponse.valid) { + throw new Error(validationResponse.errors[0].message); + } const ghConfig = getGithubIntegrationConfig( this.scmIntegrationsApi, repositoryUrl, @@ -190,7 +199,6 @@ the component will become available.\n\nFor more information, read an \ body, }); } - throw new Error('unimplemented!'); } diff --git a/plugins/catalog-import/src/components/StepFinishImportLocation/StepFinishImportLocation.tsx b/plugins/catalog-import/src/components/StepFinishImportLocation/StepFinishImportLocation.tsx index cf5427517a..9e19eea6d2 100644 --- a/plugins/catalog-import/src/components/StepFinishImportLocation/StepFinishImportLocation.tsx +++ b/plugins/catalog-import/src/components/StepFinishImportLocation/StepFinishImportLocation.tsx @@ -81,7 +81,6 @@ export const StepFinishImportLocation = ({ prepareResult, onReset }: Props) => { Register another - ; ); }