diff --git a/.changeset/ten-cups-relax.md b/.changeset/ten-cups-relax.md new file mode 100644 index 0000000000..9e32ad3856 --- /dev/null +++ b/.changeset/ten-cups-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Make git config optional for techdocs feedback links diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 632feac3c1..55e5dadb54 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -34,6 +34,8 @@ "@backstage/config": "^0.1.4", "@backstage/catalog-model": "^0.7.7", "@backstage/core": "^0.7.7", + "@backstage/integration": "^0.5.0", + "@backstage/integration-react": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.4", "@backstage/theme": "^0.2.6", "@backstage/errors": "^0.1.1", diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts index 97dd6f2150..e25f245fb4 100644 --- a/plugins/techdocs/src/plugin.ts +++ b/plugins/techdocs/src/plugin.ts @@ -13,21 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Copyright 2020 Spotify AB - * - * 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 { configApiRef, diff --git a/plugins/techdocs/src/reader/components/Reader.test.tsx b/plugins/techdocs/src/reader/components/Reader.test.tsx index b39134b0cd..fbdac95658 100644 --- a/plugins/techdocs/src/reader/components/Reader.test.tsx +++ b/plugins/techdocs/src/reader/components/Reader.test.tsx @@ -14,7 +14,12 @@ * limitations under the License. */ +import { ConfigReader } from '@backstage/config'; import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { + ScmIntegrationsApi, + scmIntegrationsApiRef, +} from '@backstage/integration-react'; import { wrapInTestApp } from '@backstage/test-utils'; import { act, render } from '@testing-library/react'; import React from 'react'; @@ -39,9 +44,15 @@ describe('', () => { entityId: 'Component::backstage', }); + const scmIntegrationsApi: ScmIntegrationsApi = ScmIntegrationsApi.fromConfig( + new ConfigReader({ + integrations: {}, + }), + ); const techdocsStorageApi: Partial = {}; const apiRegistry = ApiRegistry.from([ + [scmIntegrationsApiRef, scmIntegrationsApi], [techdocsStorageApiRef, techdocsStorageApi], ]); diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index b16a566afa..82b5245cfa 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import { EntityName } from '@backstage/catalog-model'; -import { configApiRef, useApi } from '@backstage/core'; +import { useApi } from '@backstage/core'; +import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { BackstageTheme } from '@backstage/theme'; import { useTheme } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; @@ -54,7 +55,7 @@ export const Reader = ({ entityId, onReady }: Props) => { const [loadedPath, setLoadedPath] = useState(''); const [atInitialLoad, setAtInitialLoad] = useState(true); const [newerDocsExist, setNewerDocsExist] = useState(false); - const configApi = useApi(configApiRef); + const scmIntegrationsApi = useApi(scmIntegrationsApiRef); const { value: isSynced, @@ -145,7 +146,7 @@ export const Reader = ({ entityId, onReady }: Props) => { rewriteDocLinks(), removeMkdocsHeader(), simplifyMkdocsFooter(), - addGitFeedbackLink(configApi), + addGitFeedbackLink(scmIntegrationsApi), injectCss({ css: ` body { @@ -327,7 +328,7 @@ export const Reader = ({ entityId, onReady }: Props) => { theme.palette.background.default, newerDocsExist, isSynced, - configApi, + scmIntegrationsApi, ]); // docLoadError not considered an error state if sync request is still ongoing diff --git a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx index 52ddd3794e..5b638ff668 100644 --- a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx @@ -16,6 +16,11 @@ import React from 'react'; import { TechDocsPage } from './TechDocsPage'; import { render, act } from '@testing-library/react'; +import { ConfigReader } from '@backstage/config'; +import { + ScmIntegrationsApi, + scmIntegrationsApiRef, +} from '@backstage/integration-react'; import { wrapInTestApp } from '@backstage/test-utils'; import { ApiRegistry, ApiProvider } from '@backstage/core'; import { @@ -50,6 +55,11 @@ describe('', () => { entityId: 'Component::backstage', }); + const scmIntegrationsApi: ScmIntegrationsApi = ScmIntegrationsApi.fromConfig( + new ConfigReader({ + integrations: {}, + }), + ); const techdocsApi: Partial = { getEntityMetadata: () => Promise.resolve({ @@ -72,6 +82,7 @@ describe('', () => { }; const apiRegistry = ApiRegistry.from([ + [scmIntegrationsApiRef, scmIntegrationsApi], [techdocsApiRef, techdocsApi], [techdocsStorageApiRef, techdocsStorageApi], ]); diff --git a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts index 4c9ea04fe9..1b53b0141e 100644 --- a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts +++ b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts @@ -14,16 +14,18 @@ * limitations under the License. */ +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; import { createTestShadowDom } from '../../test-utils'; import { addGitFeedbackLink } from './addGitFeedbackLink'; -const configApi = { - getConfigArray: function getConfigArray(key: string) { - return key === 'integrations.github' - ? [{ data: { host: 'self-hosted-git-hub-provider.com' } }] - : []; - }, -}; +const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + github: [{ host: 'self-hosted-git-hub-provider.com' }], + }, + }), +); describe('addGitFeedbackLink', () => { it('adds a feedback link when a Gitlab source edit link is available', () => { @@ -38,7 +40,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -63,7 +65,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -87,7 +89,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -107,7 +109,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -127,7 +129,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); diff --git a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts index d19d90228c..0fc25d8cf3 100644 --- a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts +++ b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts @@ -15,12 +15,15 @@ */ import type { Transformer } from './index'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import FeedbackOutlinedIcon from '@material-ui/icons/FeedbackOutlined'; import React from 'react'; import ReactDOM from 'react-dom'; // requires repo -export const addGitFeedbackLink = (configApi: any): Transformer => { +export const addGitFeedbackLink = ( + scmIntegrationsApi: ScmIntegrationRegistry, +): Transformer => { return dom => { // attempting to use selectors that are more likely to be static as MkDocs updates over time const sourceAnchor = dom.querySelector( @@ -31,28 +34,12 @@ export const addGitFeedbackLink = (configApi: any): Transformer => { if (!sourceAnchor || !sourceAnchor.href) { return dom; } - let gitHost = ''; const sourceURL = new URL(sourceAnchor.href); - const githubHosts = configApi - .getConfigArray('integrations.github') - .map((integration: any) => integration.data.host); - const gitlabHosts = configApi - .getConfigArray('integrations.gitlab') - .map((integration: any) => integration.data.host); + const integration = scmIntegrationsApi.byUrl(sourceURL); // don't show if can't identify edit link hostname as a gitlab/github hosting - if ( - githubHosts.includes(sourceURL.hostname) || - sourceURL.origin.includes('github') - ) { - gitHost = 'github'; - } else if ( - gitlabHosts.includes(sourceURL.hostname) || - sourceURL.origin.includes('gitlab') - ) { - gitHost = 'gitlab'; - } else { + if (integration?.type !== 'github' && integration?.type !== 'gitlab') { return dom; } @@ -66,7 +53,7 @@ export const addGitFeedbackLink = (configApi: any): Transformer => { const repoPath = sourceURL.pathname.split('/').slice(0, 3).join('/'); const feedbackLink = sourceAnchor.cloneNode() as HTMLAnchorElement; - switch (gitHost) { + switch (integration?.type) { case 'gitlab': feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?issue[title]=${issueTitle}&issue[description]=${issueDesc}`; break;