From 2b555e3b839f58b2883df80079b3e04205ec1b03 Mon Sep 17 00:00:00 2001 From: Magnus Persson Date: Tue, 22 Nov 2022 11:13:15 +0100 Subject: [PATCH 1/5] Add @backstage/plugin-sonarqube-react Signed-off-by: Magnus Persson --- plugins/sonarqube-react/.eslintrc.js | 1 + plugins/sonarqube-react/package.json | 48 ++++++++ .../src/api/SonarQubeApi.ts | 32 +++++- plugins/sonarqube-react/src/api/index.ts | 24 ++++ .../sonarqube-react/src/components/index.ts | 21 ++++ .../components/isSonarQubeAvailable.test.ts | 58 ++++++++++ .../src/components/isSonarQubeAvailable.ts | 24 ++++ plugins/sonarqube-react/src/hooks/index.ts | 17 +++ .../src/hooks/useProjectInfo.test.ts | 108 ++++++++++++++++++ .../src/hooks/useProjectInfo.ts | 59 ++++++++++ plugins/sonarqube-react/src/index.ts | 19 +++ 11 files changed, 409 insertions(+), 2 deletions(-) create mode 100644 plugins/sonarqube-react/.eslintrc.js create mode 100644 plugins/sonarqube-react/package.json rename plugins/{sonarqube => sonarqube-react}/src/api/SonarQubeApi.ts (67%) create mode 100644 plugins/sonarqube-react/src/api/index.ts create mode 100644 plugins/sonarqube-react/src/components/index.ts create mode 100644 plugins/sonarqube-react/src/components/isSonarQubeAvailable.test.ts create mode 100644 plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts create mode 100644 plugins/sonarqube-react/src/hooks/index.ts create mode 100644 plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts create mode 100644 plugins/sonarqube-react/src/hooks/useProjectInfo.ts create mode 100644 plugins/sonarqube-react/src/index.ts diff --git a/plugins/sonarqube-react/.eslintrc.js b/plugins/sonarqube-react/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/sonarqube-react/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/sonarqube-react/package.json b/plugins/sonarqube-react/package.json new file mode 100644 index 0000000000..99e794e6cd --- /dev/null +++ b/plugins/sonarqube-react/package.json @@ -0,0 +1,48 @@ +{ + "name": "@backstage/plugin-sonarqube-react", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts", + "alphaTypes": "dist/index.alpha.d.ts" + }, + "backstage": { + "role": "web-library" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/sonarqube-react" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli package build --experimental-type-build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean", + "start": "backstage-cli package start" + }, + "dependencies": { + "@backstage/catalog-model": "workspace:^", + "@backstage/core-plugin-api": "workspace:^" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist", + "alpha" + ] +} diff --git a/plugins/sonarqube/src/api/SonarQubeApi.ts b/plugins/sonarqube-react/src/api/SonarQubeApi.ts similarity index 67% rename from plugins/sonarqube/src/api/SonarQubeApi.ts rename to plugins/sonarqube-react/src/api/SonarQubeApi.ts index 9dbb6331dd..232ca535a9 100644 --- a/plugins/sonarqube/src/api/SonarQubeApi.ts +++ b/plugins/sonarqube-react/src/api/SonarQubeApi.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2022 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. @@ -14,9 +14,36 @@ * limitations under the License. */ -import { MetricKey, SonarUrlProcessorFunc } from './types'; import { createApiRef } from '@backstage/core-plugin-api'; +export type MetricKey = + // alert status + | 'alert_status' + + // bugs and rating (-> reliability) + | 'bugs' + | 'reliability_rating' + + // vulnerabilities and rating (-> security) + | 'vulnerabilities' + | 'security_rating' + + // code smells and rating (-> maintainability) + | 'code_smells' + | 'sqale_rating' + + // security hotspots + | 'security_hotspots_reviewed' + | 'security_review_rating' + + // coverage + | 'coverage' + + // duplicated lines + | 'duplicated_lines_density'; + +export type SonarUrlProcessorFunc = (identifier: string) => string; + /** * Define a type to make sure that all metrics are used */ @@ -37,6 +64,7 @@ export const sonarQubeApiRef = createApiRef({ id: 'plugin.sonarqube.service', }); +/** @alpha */ export type SonarQubeApi = { getFindingSummary(options: { componentKey?: string; diff --git a/plugins/sonarqube-react/src/api/index.ts b/plugins/sonarqube-react/src/api/index.ts new file mode 100644 index 0000000000..35ebdacabe --- /dev/null +++ b/plugins/sonarqube-react/src/api/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2022 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 { sonarQubeApiRef } from './SonarQubeApi'; +export type { + Metrics, + MetricKey, + SonarQubeApi, + FindingSummary, + SonarUrlProcessorFunc, +} from './SonarQubeApi'; diff --git a/plugins/sonarqube-react/src/components/index.ts b/plugins/sonarqube-react/src/components/index.ts new file mode 100644 index 0000000000..a000986306 --- /dev/null +++ b/plugins/sonarqube-react/src/components/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2022 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 { + isSonarQubeAvailable, + SONARQUBE_PROJECT_KEY_ANNOTATION, + SONARQUBE_PROJECT_INSTANCE_SEPARATOR, +} from './isSonarQubeAvailable'; diff --git a/plugins/sonarqube-react/src/components/isSonarQubeAvailable.test.ts b/plugins/sonarqube-react/src/components/isSonarQubeAvailable.test.ts new file mode 100644 index 0000000000..9053b1a347 --- /dev/null +++ b/plugins/sonarqube-react/src/components/isSonarQubeAvailable.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2022 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 { Entity } from '@backstage/catalog-model'; +import { + isSonarQubeAvailable, + SONARQUBE_PROJECT_KEY_ANNOTATION, +} from './isSonarQubeAvailable'; + +const createDummyEntity = (sonarqubeAnnotationValue: string): Entity => { + return { + apiVersion: '', + kind: '', + metadata: { + name: 'dummy', + annotations: { + [SONARQUBE_PROJECT_KEY_ANNOTATION]: sonarqubeAnnotationValue, + }, + }, + }; +}; + +describe('isSonarQubeAvailable', () => { + it('returns true if sonarqube annotation defined', () => { + const entity = createDummyEntity('dummy'); + expect(isSonarQubeAvailable(entity)).toBe(true); + }); + + it('returns false if sonarqube annotation empty', () => { + const entity = createDummyEntity(''); + expect(isSonarQubeAvailable(entity)).toBe(false); + }); + + it('returns false if sonarqube annotation not defined', () => { + const entity = { + apiVersion: '', + kind: '', + metadata: { + name: 'dummy', + annotations: {}, + }, + }; + expect(isSonarQubeAvailable(entity)).toBe(false); + }); +}); diff --git a/plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts b/plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts new file mode 100644 index 0000000000..b8504809c3 --- /dev/null +++ b/plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2022 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 { Entity } from '@backstage/catalog-model'; + +export const SONARQUBE_PROJECT_KEY_ANNOTATION = 'sonarqube.org/project-key'; +export const SONARQUBE_PROJECT_INSTANCE_SEPARATOR = '/'; + +/** @public */ +export const isSonarQubeAvailable = (entity: Entity) => + Boolean(entity.metadata.annotations?.[SONARQUBE_PROJECT_KEY_ANNOTATION]); diff --git a/plugins/sonarqube-react/src/hooks/index.ts b/plugins/sonarqube-react/src/hooks/index.ts new file mode 100644 index 0000000000..16892a5e3d --- /dev/null +++ b/plugins/sonarqube-react/src/hooks/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 { useProjectInfo } from './useProjectInfo'; diff --git a/plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts b/plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts new file mode 100644 index 0000000000..2babb4cd42 --- /dev/null +++ b/plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts @@ -0,0 +1,108 @@ +/* + * Copyright 2022 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 { Entity } from '@backstage/catalog-model'; +import { + SONARQUBE_PROJECT_INSTANCE_SEPARATOR, + SONARQUBE_PROJECT_KEY_ANNOTATION, +} from '../components'; +import { useProjectInfo } from './useProjectInfo'; + +const createDummyEntity = (sonarqubeAnnotationValue: string): Entity => { + return { + apiVersion: '', + kind: '', + metadata: { + name: 'dummy', + annotations: { + [SONARQUBE_PROJECT_KEY_ANNOTATION]: sonarqubeAnnotationValue, + }, + }, + }; +}; + +describe('useProjectInfo', () => { + const DUMMY_INSTANCE = 'dummyInstance'; + const DUMMY_KEY = 'dummyKey'; + + it('parse annotation with key and instance', () => { + const entity = createDummyEntity( + DUMMY_INSTANCE + SONARQUBE_PROJECT_INSTANCE_SEPARATOR + DUMMY_KEY, + ); + expect(useProjectInfo(entity)).toEqual({ + projectInstance: DUMMY_INSTANCE, + projectKey: DUMMY_KEY, + }); + }); + + it('parse annotation with instance, tenant/project-key', () => { + const DUMMY_KEY_WITH_TENANT = 'dummy-tenant/dummyKey'; + const entity = createDummyEntity( + DUMMY_INSTANCE + + SONARQUBE_PROJECT_INSTANCE_SEPARATOR + + DUMMY_KEY_WITH_TENANT, + ); + expect(useProjectInfo(entity)).toEqual({ + projectInstance: DUMMY_INSTANCE, + projectKey: DUMMY_KEY_WITH_TENANT, + }); + }); + + it('parse annotation with instance, tenant:project-key', () => { + const DUMMY_KEY_WITH_TENANT = 'dummy-tenant:dummyKey'; + const entity = createDummyEntity( + DUMMY_INSTANCE + + SONARQUBE_PROJECT_INSTANCE_SEPARATOR + + DUMMY_KEY_WITH_TENANT, + ); + expect(useProjectInfo(entity)).toEqual({ + projectInstance: DUMMY_INSTANCE, + projectKey: DUMMY_KEY_WITH_TENANT, + }); + }); + + // compatibility with previous mono-instance sonarqube config + it('parse annotation with only key', () => { + const entity = createDummyEntity(DUMMY_KEY); + expect(useProjectInfo(entity)).toEqual({ + projectInstance: undefined, + projectKey: DUMMY_KEY, + }); + }); + + it('handle empty annotation', () => { + const entity = createDummyEntity(''); + expect(useProjectInfo(entity)).toEqual({ + projectInstance: undefined, + projectKey: undefined, + }); + }); + + it('handle non-existent annotation', () => { + const entity = { + apiVersion: '', + kind: '', + metadata: { + name: 'dummy', + annotations: {}, + }, + }; + expect(useProjectInfo(entity)).toEqual({ + projectInstance: undefined, + projectKey: undefined, + }); + }); +}); diff --git a/plugins/sonarqube-react/src/hooks/useProjectInfo.ts b/plugins/sonarqube-react/src/hooks/useProjectInfo.ts new file mode 100644 index 0000000000..61c8baed98 --- /dev/null +++ b/plugins/sonarqube-react/src/hooks/useProjectInfo.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2022 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 { Entity } from '@backstage/catalog-model'; +import { + SONARQUBE_PROJECT_INSTANCE_SEPARATOR, + SONARQUBE_PROJECT_KEY_ANNOTATION, +} from '../components'; + +/** + * + * Try to parse sonarqube information from an entity. + * + * If part or all info are not found, they will default to undefined + * + * @alpha + * @param entity entity to find the sonarqube information from. + * @return a ProjectInfo properly populated. + */ +export const useProjectInfo = ( + entity: Entity, +): { + projectInstance: string | undefined; + projectKey: string | undefined; +} => { + let projectInstance = undefined; + let projectKey = undefined; + const annotation = + entity?.metadata.annotations?.[SONARQUBE_PROJECT_KEY_ANNOTATION]; + if (annotation) { + const instanceSeparatorIndex = annotation.indexOf( + SONARQUBE_PROJECT_INSTANCE_SEPARATOR, + ); + if (instanceSeparatorIndex > -1) { + // Examples: + // instanceA/projectA -> projectInstance = "instanceA" & projectKey = "projectA" + // instanceA/tenantA:projectA -> projectInstance = "instanceA" & projectKey = "tenantA:projectA" + // instanceA/tenantA/projectA -> projectInstance = "instanceA" & projectKey = "tenantA/projectA" + projectInstance = annotation.substring(0, instanceSeparatorIndex); + projectKey = annotation.substring(instanceSeparatorIndex + 1); + } else { + projectKey = annotation; + } + } + return { projectInstance, projectKey }; +}; diff --git a/plugins/sonarqube-react/src/index.ts b/plugins/sonarqube-react/src/index.ts new file mode 100644 index 0000000000..fe04a0459f --- /dev/null +++ b/plugins/sonarqube-react/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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 * from './api'; +export * from './components'; +export * from './hooks'; From b3a304e78e6a2276e25fd763f21a3a561c275b96 Mon Sep 17 00:00:00 2001 From: Magnus Persson Date: Tue, 22 Nov 2022 11:37:12 +0100 Subject: [PATCH 2/5] Reference @backstage/plugin-sonarqube-react from @backstage/plugin-sonarqube Signed-off-by: Magnus Persson --- plugins/sonarqube/dev/index.tsx | 8 +- plugins/sonarqube/package.json | 1 + .../sonarqube/src/api/SonarQubeClient.test.ts | 3 +- plugins/sonarqube/src/api/SonarQubeClient.ts | 6 +- plugins/sonarqube/src/api/index.ts | 2 - plugins/sonarqube/src/api/types.ts | 30 +--- .../SonarQubeCard/SonarQubeCard.tsx | 10 +- .../SonarQubeContentPage.test.tsx | 5 +- .../SonarQubeContentPage.tsx | 4 +- plugins/sonarqube/src/components/index.ts | 4 - .../src/components/useProjectKey.test.ts | 132 ------------------ .../sonarqube/src/components/useProjectKey.ts | 61 -------- plugins/sonarqube/src/plugin.ts | 3 +- yarn.lock | 13 ++ 14 files changed, 41 insertions(+), 241 deletions(-) delete mode 100644 plugins/sonarqube/src/components/useProjectKey.test.ts delete mode 100644 plugins/sonarqube/src/components/useProjectKey.ts diff --git a/plugins/sonarqube/dev/index.tsx b/plugins/sonarqube/dev/index.tsx index bba859195f..a34c4d4e82 100644 --- a/plugins/sonarqube/dev/index.tsx +++ b/plugins/sonarqube/dev/index.tsx @@ -19,9 +19,13 @@ import { createDevApp, EntityGridItem } from '@backstage/dev-utils'; import { Grid } from '@material-ui/core'; import React from 'react'; import { EntitySonarQubeCard, sonarQubePlugin } from '../src'; -import { FindingSummary, SonarQubeApi, sonarQubeApiRef } from '../src/api'; -import { SONARQUBE_PROJECT_KEY_ANNOTATION } from '../src/components/useProjectKey'; import { Content, Header, Page } from '@backstage/core-components'; +import { + FindingSummary, + SONARQUBE_PROJECT_KEY_ANNOTATION, + SonarQubeApi, + sonarQubeApiRef, +} from '@backstage/plugin-sonarqube-react'; const entity = (name?: string) => ({ diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index ad6df1db8c..a5465eee78 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -38,6 +38,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-sonarqube-react": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/sonarqube/src/api/SonarQubeClient.test.ts b/plugins/sonarqube/src/api/SonarQubeClient.test.ts index dc5d7f077e..592f08457f 100644 --- a/plugins/sonarqube/src/api/SonarQubeClient.test.ts +++ b/plugins/sonarqube/src/api/SonarQubeClient.test.ts @@ -17,10 +17,11 @@ import { setupRequestMockHandlers } from '@backstage/test-utils'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; -import { FindingSummary, SonarQubeClient } from './index'; +import { SonarQubeClient } from './index'; import { InstanceUrlWrapper, FindingsWrapper } from './types'; import { UrlPatternDiscovery } from '@backstage/core-app-api'; import { IdentityApi } from '@backstage/core-plugin-api'; +import { FindingSummary } from '@backstage/plugin-sonarqube-react'; const server = setupServer(); diff --git a/plugins/sonarqube/src/api/SonarQubeClient.ts b/plugins/sonarqube/src/api/SonarQubeClient.ts index 9ec40e8420..cf893ccef5 100644 --- a/plugins/sonarqube/src/api/SonarQubeClient.ts +++ b/plugins/sonarqube/src/api/SonarQubeClient.ts @@ -15,7 +15,11 @@ */ import fetch from 'cross-fetch'; -import { FindingSummary, Metrics, SonarQubeApi } from './SonarQubeApi'; +import { + FindingSummary, + Metrics, + SonarQubeApi, +} from '@backstage/plugin-sonarqube-react'; import { InstanceUrlWrapper, FindingsWrapper } from './types'; import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; diff --git a/plugins/sonarqube/src/api/index.ts b/plugins/sonarqube/src/api/index.ts index 8c418016cd..ddf355af33 100644 --- a/plugins/sonarqube/src/api/index.ts +++ b/plugins/sonarqube/src/api/index.ts @@ -14,6 +14,4 @@ * limitations under the License. */ -export type { Metrics, FindingSummary, SonarQubeApi } from './SonarQubeApi'; -export { sonarQubeApiRef } from './SonarQubeApi'; export { SonarQubeClient } from './SonarQubeClient'; diff --git a/plugins/sonarqube/src/api/types.ts b/plugins/sonarqube/src/api/types.ts index 348986149d..48c7c41c03 100644 --- a/plugins/sonarqube/src/api/types.ts +++ b/plugins/sonarqube/src/api/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { MetricKey } from '@backstage/plugin-sonarqube-react'; + export interface InstanceUrlWrapper { instanceUrl: string; } @@ -23,35 +25,7 @@ export interface FindingsWrapper { measures: Measure[]; } -export type MetricKey = - // alert status - | 'alert_status' - - // bugs and rating (-> reliability) - | 'bugs' - | 'reliability_rating' - - // vulnerabilities and rating (-> security) - | 'vulnerabilities' - | 'security_rating' - - // code smells and rating (-> maintainability) - | 'code_smells' - | 'sqale_rating' - - // security hotspots - | 'security_hotspots_reviewed' - | 'security_review_rating' - - // coverage - | 'coverage' - - // duplicated lines - | 'duplicated_lines_density'; - export interface Measure { metric: MetricKey; value: string; } - -export type SonarUrlProcessorFunc = (identifier: string) => string; diff --git a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx index b32a66aaa3..900c8d082c 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/SonarQubeCard.tsx @@ -15,6 +15,11 @@ */ import { useEntity } from '@backstage/plugin-catalog-react'; +import { + SONARQUBE_PROJECT_KEY_ANNOTATION, + sonarQubeApiRef, + useProjectInfo, +} from '@backstage/plugin-sonarqube-react'; import { Chip, Grid } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import BugReport from '@material-ui/icons/BugReport'; @@ -23,11 +28,6 @@ import Security from '@material-ui/icons/Security'; import SentimentVeryDissatisfied from '@material-ui/icons/SentimentVeryDissatisfied'; import React, { useMemo } from 'react'; import useAsync from 'react-use/lib/useAsync'; -import { sonarQubeApiRef } from '../../api'; -import { - SONARQUBE_PROJECT_KEY_ANNOTATION, - useProjectInfo, -} from '../useProjectKey'; import { Percentage } from './Percentage'; import { Rating } from './Rating'; import { RatingCard } from './RatingCard'; diff --git a/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.test.tsx b/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.test.tsx index 8e847d7c31..22175d14bc 100644 --- a/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.test.tsx +++ b/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.test.tsx @@ -22,8 +22,9 @@ import React from 'react'; import { isSonarQubeAvailable, SONARQUBE_PROJECT_KEY_ANNOTATION, -} from '../useProjectKey'; -import { SonarQubeApi, sonarQubeApiRef } from '../../api'; + SonarQubeApi, + sonarQubeApiRef, +} from '@backstage/plugin-sonarqube-react'; const Providers = ({ annotation, diff --git a/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.tsx b/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.tsx index 33a145b746..f577d08eeb 100644 --- a/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.tsx +++ b/plugins/sonarqube/src/components/SonarQubeContentPage/SonarQubeContentPage.tsx @@ -22,11 +22,11 @@ import { import { MissingAnnotationEmptyState } from '@backstage/core-components'; import { useEntity } from '@backstage/plugin-catalog-react'; import React from 'react'; +import { SonarQubeCard } from '../SonarQubeCard'; import { isSonarQubeAvailable, SONARQUBE_PROJECT_KEY_ANNOTATION, -} from '../useProjectKey'; -import { SonarQubeCard } from '../SonarQubeCard'; +} from '@backstage/plugin-sonarqube-react'; /** @public */ export type SonarQubeContentPageProps = { diff --git a/plugins/sonarqube/src/components/index.ts b/plugins/sonarqube/src/components/index.ts index 8e1d93f85b..85ccfe9621 100644 --- a/plugins/sonarqube/src/components/index.ts +++ b/plugins/sonarqube/src/components/index.ts @@ -17,7 +17,3 @@ export { SonarQubeCard } from './SonarQubeCard'; export type { DuplicationRating } from './SonarQubeCard'; export type { SonarQubeContentPageProps } from './SonarQubeContentPage'; -export { - isSonarQubeAvailable, - SONARQUBE_PROJECT_KEY_ANNOTATION, -} from './useProjectKey'; diff --git a/plugins/sonarqube/src/components/useProjectKey.test.ts b/plugins/sonarqube/src/components/useProjectKey.test.ts deleted file mode 100644 index 8686ac82de..0000000000 --- a/plugins/sonarqube/src/components/useProjectKey.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2022 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 { - isSonarQubeAvailable, - SONARQUBE_PROJECT_INSTANCE_SEPARATOR, - SONARQUBE_PROJECT_KEY_ANNOTATION, - useProjectInfo, -} from './useProjectKey'; -import { Entity } from '@backstage/catalog-model'; - -const createDummyEntity = (sonarqubeAnnotationValue: string): Entity => { - return { - apiVersion: '', - kind: '', - metadata: { - name: 'dummy', - annotations: { - [SONARQUBE_PROJECT_KEY_ANNOTATION]: sonarqubeAnnotationValue, - }, - }, - }; -}; - -describe('isSonarQubeAvailable', () => { - it('returns true if sonarqube annotation defined', () => { - const entity = createDummyEntity('dummy'); - expect(isSonarQubeAvailable(entity)).toBe(true); - }); - - it('returns false if sonarqube annotation empty', () => { - const entity = createDummyEntity(''); - expect(isSonarQubeAvailable(entity)).toBe(false); - }); - - it('returns false if sonarqube annotation not defined', () => { - const entity = { - apiVersion: '', - kind: '', - metadata: { - name: 'dummy', - annotations: {}, - }, - }; - expect(isSonarQubeAvailable(entity)).toBe(false); - }); -}); - -describe('useProjectInfo', () => { - const DUMMY_INSTANCE = 'dummyInstance'; - const DUMMY_KEY = 'dummyKey'; - - it('parse annotation with key and instance', () => { - const entity = createDummyEntity( - DUMMY_INSTANCE + SONARQUBE_PROJECT_INSTANCE_SEPARATOR + DUMMY_KEY, - ); - expect(useProjectInfo(entity)).toEqual({ - projectInstance: DUMMY_INSTANCE, - projectKey: DUMMY_KEY, - }); - }); - - it('parse annotation with instance, tenant/project-key', () => { - const DUMMY_KEY_WITH_TENANT = 'dummy-tenant/dummyKey'; - const entity = createDummyEntity( - DUMMY_INSTANCE + - SONARQUBE_PROJECT_INSTANCE_SEPARATOR + - DUMMY_KEY_WITH_TENANT, - ); - expect(useProjectInfo(entity)).toEqual({ - projectInstance: DUMMY_INSTANCE, - projectKey: DUMMY_KEY_WITH_TENANT, - }); - }); - - it('parse annotation with instance, tenant:project-key', () => { - const DUMMY_KEY_WITH_TENANT = 'dummy-tenant:dummyKey'; - const entity = createDummyEntity( - DUMMY_INSTANCE + - SONARQUBE_PROJECT_INSTANCE_SEPARATOR + - DUMMY_KEY_WITH_TENANT, - ); - expect(useProjectInfo(entity)).toEqual({ - projectInstance: DUMMY_INSTANCE, - projectKey: DUMMY_KEY_WITH_TENANT, - }); - }); - - // compatibility with previous mono-instance sonarqube config - it('parse annotation with only key', () => { - const entity = createDummyEntity(DUMMY_KEY); - expect(useProjectInfo(entity)).toEqual({ - projectInstance: undefined, - projectKey: DUMMY_KEY, - }); - }); - - it('handle empty annotation', () => { - const entity = createDummyEntity(''); - expect(useProjectInfo(entity)).toEqual({ - projectInstance: undefined, - projectKey: undefined, - }); - }); - - it('handle non-existent annotation', () => { - const entity = { - apiVersion: '', - kind: '', - metadata: { - name: 'dummy', - annotations: {}, - }, - }; - expect(useProjectInfo(entity)).toEqual({ - projectInstance: undefined, - projectKey: undefined, - }); - }); -}); diff --git a/plugins/sonarqube/src/components/useProjectKey.ts b/plugins/sonarqube/src/components/useProjectKey.ts deleted file mode 100644 index 20f7f03bb3..0000000000 --- a/plugins/sonarqube/src/components/useProjectKey.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020 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 { Entity } from '@backstage/catalog-model'; - -/** @public */ -export const SONARQUBE_PROJECT_KEY_ANNOTATION = 'sonarqube.org/project-key'; -export const SONARQUBE_PROJECT_INSTANCE_SEPARATOR = '/'; - -/** @public */ -export const isSonarQubeAvailable = (entity: Entity) => - Boolean(entity.metadata.annotations?.[SONARQUBE_PROJECT_KEY_ANNOTATION]); - -/** - * Try to parse sonarqube information from an entity. - * - * If part or all info are not found, they will default to undefined - * - * @param entity entity to find the sonarqube information from. - * @return a ProjectInfo properly populated. - */ -export const useProjectInfo = ( - entity: Entity, -): { - projectInstance: string | undefined; - projectKey: string | undefined; -} => { - let projectInstance = undefined; - let projectKey = undefined; - const annotation = - entity?.metadata.annotations?.[SONARQUBE_PROJECT_KEY_ANNOTATION]; - if (annotation) { - const instanceSeparatorIndex = annotation.indexOf( - SONARQUBE_PROJECT_INSTANCE_SEPARATOR, - ); - if (instanceSeparatorIndex > -1) { - // Examples: - // instanceA/projectA -> projectInstance = "instanceA" & projectKey = "projectA" - // instanceA/tenantA:projectA -> projectInstance = "instanceA" & projectKey = "tenantA:projectA" - // instanceA/tenantA/projectA -> projectInstance = "instanceA" & projectKey = "tenantA/projectA" - projectInstance = annotation.substring(0, instanceSeparatorIndex); - projectKey = annotation.substring(instanceSeparatorIndex + 1); - } else { - projectKey = annotation; - } - } - return { projectInstance, projectKey }; -}; diff --git a/plugins/sonarqube/src/plugin.ts b/plugins/sonarqube/src/plugin.ts index 5eb5ea82f3..c66d8c4e96 100644 --- a/plugins/sonarqube/src/plugin.ts +++ b/plugins/sonarqube/src/plugin.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { sonarQubeApiRef, SonarQubeClient } from './api'; +import { SonarQubeClient } from './api'; import { createApiFactory, createComponentExtension, @@ -22,6 +22,7 @@ import { discoveryApiRef, identityApiRef, } from '@backstage/core-plugin-api'; +import { sonarQubeApiRef } from '@backstage/plugin-sonarqube-react'; /** @public */ export const sonarQubePlugin = createPlugin({ diff --git a/yarn.lock b/yarn.lock index 2048c5ff5e..bcaf28da01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7785,6 +7785,18 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-sonarqube-react@workspace:^, @backstage/plugin-sonarqube-react@workspace:plugins/sonarqube-react": + version: 0.0.0-use.local + resolution: "@backstage/plugin-sonarqube-react@workspace:plugins/sonarqube-react" + dependencies: + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + peerDependencies: + react: ^16.13.1 || ^17.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-sonarqube@workspace:plugins/sonarqube": version: 0.0.0-use.local resolution: "@backstage/plugin-sonarqube@workspace:plugins/sonarqube" @@ -7796,6 +7808,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" + "@backstage/plugin-sonarqube-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.12.2 From 19f8ad42626ff082b4022cf6671df763dec3d8e9 Mon Sep 17 00:00:00 2001 From: Magnus Persson Date: Tue, 13 Dec 2022 22:49:54 +0100 Subject: [PATCH 3/5] Keep types but marked deprecated Signed-off-by: Magnus Persson --- plugins/sonarqube/src/api/types.ts | 13 ++++++++++++- plugins/sonarqube/src/components/index.ts | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/plugins/sonarqube/src/api/types.ts b/plugins/sonarqube/src/api/types.ts index 48c7c41c03..470d05903a 100644 --- a/plugins/sonarqube/src/api/types.ts +++ b/plugins/sonarqube/src/api/types.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { MetricKey } from '@backstage/plugin-sonarqube-react'; +import { MetricKey as NonDeprecatedMetricKey } from '@backstage/plugin-sonarqube-react'; +import { SonarUrlProcessorFunc as NonDeprecatedSonarUrlProcessorFunc } from '@backstage/plugin-sonarqube-react'; export interface InstanceUrlWrapper { instanceUrl: string; @@ -25,7 +26,17 @@ export interface FindingsWrapper { measures: Measure[]; } +/** + * @deprecated use the same type from `@backstage/plugin-sonarqube-react` instead + */ +export type MetricKey = NonDeprecatedMetricKey; + export interface Measure { metric: MetricKey; value: string; } + +/** + * @deprecated use the same type from `@backstage/plugin-sonarqube-react` instead + */ +export type SonarUrlProcessorFunc = NonDeprecatedSonarUrlProcessorFunc; diff --git a/plugins/sonarqube/src/components/index.ts b/plugins/sonarqube/src/components/index.ts index 85ccfe9621..08af5591fd 100644 --- a/plugins/sonarqube/src/components/index.ts +++ b/plugins/sonarqube/src/components/index.ts @@ -14,6 +14,22 @@ * limitations under the License. */ +import { + isSonarQubeAvailable as NonDeprecatedIsSonarQubeAvailable, + SONARQUBE_PROJECT_KEY_ANNOTATION as NON_DEPRECATED_SONARQUBE_PROJECT_KEY_ANNOTATION, +} from '@backstage/plugin-sonarqube-react'; + export { SonarQubeCard } from './SonarQubeCard'; export type { DuplicationRating } from './SonarQubeCard'; export type { SonarQubeContentPageProps } from './SonarQubeContentPage'; + +/** + * @deprecated use the same type from `@backstage/plugin-sonarqube-react` instead + */ +export const isSonarQubeAvailable = NonDeprecatedIsSonarQubeAvailable; + +/** + * @deprecated use the same type from `@backstage/plugin-sonarqube-react` instead + */ +export const SONARQUBE_PROJECT_KEY_ANNOTATION = + NON_DEPRECATED_SONARQUBE_PROJECT_KEY_ANNOTATION; From 836b30e61ca59846db6db2fe2e450f0f66660448 Mon Sep 17 00:00:00 2001 From: Magnus Persson Date: Tue, 22 Nov 2022 12:53:57 +0100 Subject: [PATCH 4/5] Update api-report.md Signed-off-by: Magnus Persson --- plugins/sonarqube-react/api-report.md | 71 +++++++++++++++++++ .../sonarqube-react/src/api/SonarQubeApi.ts | 6 ++ .../sonarqube-react/src/components/index.ts | 1 - .../src/components/isSonarQubeAvailable.ts | 2 +- .../src/hooks/useProjectInfo.test.ts | 6 +- .../src/hooks/useProjectInfo.ts | 11 ++- plugins/sonarqube/api-report.md | 4 +- plugins/sonarqube/src/components/index.ts | 4 ++ 8 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 plugins/sonarqube-react/api-report.md diff --git a/plugins/sonarqube-react/api-report.md b/plugins/sonarqube-react/api-report.md new file mode 100644 index 0000000000..be84c45cce --- /dev/null +++ b/plugins/sonarqube-react/api-report.md @@ -0,0 +1,71 @@ +## API Report File for "@backstage/plugin-sonarqube-react" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ApiRef } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; + +// @alpha (undocumented) +export interface FindingSummary { + // (undocumented) + getComponentMeasuresUrl: SonarUrlProcessorFunc; + // (undocumented) + getIssuesUrl: SonarUrlProcessorFunc; + // (undocumented) + getSecurityHotspotsUrl: () => string; + // (undocumented) + lastAnalysis: string; + // (undocumented) + metrics: Metrics; + // (undocumented) + projectUrl: string; +} + +// @public (undocumented) +export const isSonarQubeAvailable: (entity: Entity) => boolean; + +// @alpha (undocumented) +export type MetricKey = + | 'alert_status' + | 'bugs' + | 'reliability_rating' + | 'vulnerabilities' + | 'security_rating' + | 'code_smells' + | 'sqale_rating' + | 'security_hotspots_reviewed' + | 'security_review_rating' + | 'coverage' + | 'duplicated_lines_density'; + +// @alpha +export type Metrics = { + [key in MetricKey]: string | undefined; +}; + +// @public (undocumented) +export const SONARQUBE_PROJECT_KEY_ANNOTATION = 'sonarqube.org/project-key'; + +// @alpha (undocumented) +export type SonarQubeApi = { + getFindingSummary(options: { + componentKey?: string; + projectInstance?: string; + }): Promise; +}; + +// @alpha (undocumented) +export const sonarQubeApiRef: ApiRef; + +// @alpha (undocumented) +export type SonarUrlProcessorFunc = (identifier: string) => string; + +// @alpha +export const useProjectInfo: (entity: Entity) => { + projectInstance: string | undefined; + projectKey: string | undefined; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/sonarqube-react/src/api/SonarQubeApi.ts b/plugins/sonarqube-react/src/api/SonarQubeApi.ts index 232ca535a9..ee6b1bb6a3 100644 --- a/plugins/sonarqube-react/src/api/SonarQubeApi.ts +++ b/plugins/sonarqube-react/src/api/SonarQubeApi.ts @@ -16,6 +16,7 @@ import { createApiRef } from '@backstage/core-plugin-api'; +/** @alpha */ export type MetricKey = // alert status | 'alert_status' @@ -42,15 +43,19 @@ export type MetricKey = // duplicated lines | 'duplicated_lines_density'; +/** @alpha */ export type SonarUrlProcessorFunc = (identifier: string) => string; /** + * @alpha + * * Define a type to make sure that all metrics are used */ export type Metrics = { [key in MetricKey]: string | undefined; }; +/** @alpha */ export interface FindingSummary { lastAnalysis: string; metrics: Metrics; @@ -60,6 +65,7 @@ export interface FindingSummary { getSecurityHotspotsUrl: () => string; } +/** @alpha */ export const sonarQubeApiRef = createApiRef({ id: 'plugin.sonarqube.service', }); diff --git a/plugins/sonarqube-react/src/components/index.ts b/plugins/sonarqube-react/src/components/index.ts index a000986306..44c63fe052 100644 --- a/plugins/sonarqube-react/src/components/index.ts +++ b/plugins/sonarqube-react/src/components/index.ts @@ -17,5 +17,4 @@ export { isSonarQubeAvailable, SONARQUBE_PROJECT_KEY_ANNOTATION, - SONARQUBE_PROJECT_INSTANCE_SEPARATOR, } from './isSonarQubeAvailable'; diff --git a/plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts b/plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts index b8504809c3..d538b700f3 100644 --- a/plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts +++ b/plugins/sonarqube-react/src/components/isSonarQubeAvailable.ts @@ -16,8 +16,8 @@ import { Entity } from '@backstage/catalog-model'; +/** @public */ export const SONARQUBE_PROJECT_KEY_ANNOTATION = 'sonarqube.org/project-key'; -export const SONARQUBE_PROJECT_INSTANCE_SEPARATOR = '/'; /** @public */ export const isSonarQubeAvailable = (entity: Entity) => diff --git a/plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts b/plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts index 2babb4cd42..db5e1f9689 100644 --- a/plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts +++ b/plugins/sonarqube-react/src/hooks/useProjectInfo.test.ts @@ -15,11 +15,9 @@ */ import { Entity } from '@backstage/catalog-model'; -import { - SONARQUBE_PROJECT_INSTANCE_SEPARATOR, - SONARQUBE_PROJECT_KEY_ANNOTATION, -} from '../components'; +import { SONARQUBE_PROJECT_KEY_ANNOTATION } from '../components'; import { useProjectInfo } from './useProjectInfo'; +import { SONARQUBE_PROJECT_INSTANCE_SEPARATOR } from './useProjectInfo'; const createDummyEntity = (sonarqubeAnnotationValue: string): Entity => { return { diff --git a/plugins/sonarqube-react/src/hooks/useProjectInfo.ts b/plugins/sonarqube-react/src/hooks/useProjectInfo.ts index 61c8baed98..119e568e46 100644 --- a/plugins/sonarqube-react/src/hooks/useProjectInfo.ts +++ b/plugins/sonarqube-react/src/hooks/useProjectInfo.ts @@ -15,10 +15,9 @@ */ import { Entity } from '@backstage/catalog-model'; -import { - SONARQUBE_PROJECT_INSTANCE_SEPARATOR, - SONARQUBE_PROJECT_KEY_ANNOTATION, -} from '../components'; +import { SONARQUBE_PROJECT_KEY_ANNOTATION } from '../components'; + +export const SONARQUBE_PROJECT_INSTANCE_SEPARATOR = '/'; /** * @@ -27,8 +26,8 @@ import { * If part or all info are not found, they will default to undefined * * @alpha - * @param entity entity to find the sonarqube information from. - * @return a ProjectInfo properly populated. + * @param entity - entity to find the sonarqube information from. + * @returns a ProjectInfo properly populated. */ export const useProjectInfo = ( entity: Entity, diff --git a/plugins/sonarqube/api-report.md b/plugins/sonarqube/api-report.md index 2edf80618e..31e98a0f4a 100644 --- a/plugins/sonarqube/api-report.md +++ b/plugins/sonarqube/api-report.md @@ -26,10 +26,10 @@ export const EntitySonarQubeContentPage: ( props: SonarQubeContentPageProps, ) => JSX.Element; -// @public (undocumented) +// @public @deprecated (undocumented) export const isSonarQubeAvailable: (entity: Entity) => boolean; -// @public (undocumented) +// @public @deprecated (undocumented) export const SONARQUBE_PROJECT_KEY_ANNOTATION = 'sonarqube.org/project-key'; // @public (undocumented) diff --git a/plugins/sonarqube/src/components/index.ts b/plugins/sonarqube/src/components/index.ts index 08af5591fd..252c12c5db 100644 --- a/plugins/sonarqube/src/components/index.ts +++ b/plugins/sonarqube/src/components/index.ts @@ -24,11 +24,15 @@ export type { DuplicationRating } from './SonarQubeCard'; export type { SonarQubeContentPageProps } from './SonarQubeContentPage'; /** + * @public + * * @deprecated use the same type from `@backstage/plugin-sonarqube-react` instead */ export const isSonarQubeAvailable = NonDeprecatedIsSonarQubeAvailable; /** + * @public + * * @deprecated use the same type from `@backstage/plugin-sonarqube-react` instead */ export const SONARQUBE_PROJECT_KEY_ANNOTATION = From 6b59903bfad487f4b69717a3f04f9c3eb395de8e Mon Sep 17 00:00:00 2001 From: Magnus Persson Date: Wed, 14 Dec 2022 14:52:30 +0100 Subject: [PATCH 5/5] Add changeset Signed-off-by: Magnus Persson --- .changeset/short-pigs-juggle.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .changeset/short-pigs-juggle.md diff --git a/.changeset/short-pigs-juggle.md b/.changeset/short-pigs-juggle.md new file mode 100644 index 0000000000..c7ef43e62a --- /dev/null +++ b/.changeset/short-pigs-juggle.md @@ -0,0 +1,22 @@ +--- +'@backstage/plugin-sonarqube': minor +'@backstage/plugin-sonarqube-react': minor +--- + +Parts of plugin-sonarqube have been moved into a new plugin-sonarqube-react package. Additionally some types that were +previously internal to plugin-sonarqube have been made public and will allow access for third-parties. As the sonarqube +plugin has not yet reached 1.0 breaking changes are expected in the future. As such exports of plugin-sonarqube-react +require importing via the `/alpha` entrypoint: + +```ts +import { sonarQubeApiRef } from '@backstage/plugin-sonarqube-react/alpha'; + +const sonarQubeApi = useApi(sonarQubeApiRef); +``` + +Moved from plugin-sonarqube to plugin-sonarqube-react: + +- isSonarQubeAvailable +- SONARQUBE_PROJECT_KEY_ANNOTATION + +Exports that been introduced to plugin-sonarqube-react are documented in the [API report](https://github.com/backstage/backstage/blob/master/plugins/sonarqube-react/api-report.md).