From 265b33682fc23b83ab8bb693f78c98478660f5be Mon Sep 17 00:00:00 2001 From: Kevin Silva Date: Wed, 16 Aug 2023 14:04:16 +0200 Subject: [PATCH] SONAR-20011 - Tutorial link shouldn't show if no permission --- .../js/apps/projects/__tests__/utils-test.ts | 11 +++++-- .../components/__tests__/AllProjects-test.tsx | 1 + .../__snapshots__/AllProjects-test.tsx.snap | 5 ++++ .../components/project-card/ProjectCard.tsx | 15 ++++++---- .../__tests__/ProjectCard-test.tsx | 11 +++++-- .../src/main/js/apps/projects/types.ts | 1 + .../src/main/js/apps/projects/utils.ts | 30 ++++++++++++++++--- 7 files changed, 60 insertions(+), 14 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts index 230acd91907..666cae33310 100644 --- a/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts @@ -27,6 +27,7 @@ jest.mock('../../../api/components', () => ({ searchProjects: jest .fn() .mockResolvedValue({ components: [], facets: [], paging: { total: 10 } }), + getScannableProjects: jest.fn().mockResolvedValue({ projects: [] }), })); jest.mock('../../../api/measures', () => ({ @@ -136,17 +137,23 @@ describe('fetchProjects', () => { languages: { css: 10, js: 2 }, }, projects: components.map( - (component: Component & { measures: { languages?: string; new_coverage?: string } }) => { + ( + component: Component & { + measures: { languages?: string; new_coverage?: string }; + isScannable: boolean; + } + ) => { // eslint-disable-next-line jest/no-conditional-in-test if (component.key === 'foo') { component.measures = { new_coverage: '10' }; } else { component.measures = { languages: '20' }; } - + component.isScannable = false; return component; } ), + total: 2, }); }); diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx index 04f2d3b99db..5e5dfe72c14 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx @@ -203,6 +203,7 @@ function shallowRender({ qualifier: ComponentQualifier.Project, tags: [], visibility: Visibility.Public, + isScannable: false, }, ], total: 0, diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap index 75136c21226..86fbdf88a0c 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap @@ -3,6 +3,7 @@ exports[`handles showing favorite projects on load 1`] = ` [ { + "isScannable": false, "key": "foo", "measures": {}, "name": "Foo", @@ -17,6 +18,7 @@ exports[`handles showing favorite projects on load 2`] = ` [ { "isFavorite": true, + "isScannable": false, "key": "foo", "measures": {}, "name": "Foo", @@ -30,6 +32,7 @@ exports[`handles showing favorite projects on load 2`] = ` exports[`handles updating the favorite status of a project 1`] = ` [ { + "isScannable": false, "key": "foo", "measures": {}, "name": "Foo", @@ -44,6 +47,7 @@ exports[`handles updating the favorite status of a project 2`] = ` [ { "isFavorite": true, + "isScannable": false, "key": "foo", "measures": {}, "name": "Foo", @@ -162,6 +166,7 @@ exports[`renders 1`] = ` projects={ [ { + "isScannable": false, "key": "foo", "measures": {}, "name": "Foo", diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx index 2b5118b3576..a13ee6a354c 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx @@ -196,7 +196,7 @@ function renderSecondLine( project: Props['project'], isNewCode: boolean ) { - const { analysisDate, key, leakPeriodDate, measures, qualifier } = project; + const { analysisDate, key, leakPeriodDate, measures, qualifier, isScannable } = project; if (analysisDate && (!isNewCode || leakPeriodDate)) { return ( @@ -215,11 +215,14 @@ function renderSecondLine( ? translate('projects.no_new_code_period', qualifier) : translate('projects.not_analyzed', qualifier)} - {qualifier !== ComponentQualifier.Application && !analysisDate && isLoggedIn(currentUser) && ( - - {translate('projects.configure_analysis')} - - )} + {qualifier !== ComponentQualifier.Application && + !analysisDate && + isLoggedIn(currentUser) && + isScannable && ( + + {translate('projects.configure_analysis')} + + )} ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx index bff0e35fd2a..65881b798d4 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx @@ -41,6 +41,7 @@ const PROJECT: Project = { qualifier: ComponentQualifier.Project, tags: [], visibility: Visibility.Public, + isScannable: false, }; const USER_LOGGED_OUT = mockCurrentUser(); @@ -64,11 +65,17 @@ it('should display private badge', () => { expect(screen.getByLabelText('visibility.private')).toBeInTheDocument(); }); -it('should display configure analysis button for logged in user', () => { - renderProjectCard({ ...PROJECT, analysisDate: undefined }, USER_LOGGED_IN); +it('should display configure analysis button for logged in user and scan rights', () => { + const user = mockLoggedInUser(); + renderProjectCard({ ...PROJECT, isScannable: true, analysisDate: undefined }, user); expect(screen.getByText('projects.configure_analysis')).toBeInTheDocument(); }); +it('should not display configure analysis button for logged in user and without scan rights', () => { + renderProjectCard({ ...PROJECT, analysisDate: undefined }, USER_LOGGED_IN); + expect(screen.queryByText('projects.configure_analysis')).not.toBeInTheDocument(); +}); + it('should display applications', () => { renderProjectCard({ ...PROJECT, qualifier: ComponentQualifier.Application }); expect(screen.getByLabelText('qualifier.APP')).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/apps/projects/types.ts b/server/sonar-web/src/main/js/apps/projects/types.ts index e31c013859a..b0579e60f3c 100644 --- a/server/sonar-web/src/main/js/apps/projects/types.ts +++ b/server/sonar-web/src/main/js/apps/projects/types.ts @@ -31,6 +31,7 @@ export interface Project { qualifier: ComponentQualifier; tags: string[]; visibility: Visibility; + isScannable: boolean; } export interface Facet { diff --git a/server/sonar-web/src/main/js/apps/projects/utils.ts b/server/sonar-web/src/main/js/apps/projects/utils.ts index 5dc045f5f05..efa7eb7183e 100644 --- a/server/sonar-web/src/main/js/apps/projects/utils.ts +++ b/server/sonar-web/src/main/js/apps/projects/utils.ts @@ -19,7 +19,7 @@ */ import { invert } from 'lodash'; -import { Facet, searchProjects } from '../../api/components'; +import { Facet, getScannableProjects, searchProjects } from '../../api/components'; import { getMeasuresForProjects } from '../../api/measures'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { isDiffMetric } from '../../helpers/measures'; @@ -149,6 +149,7 @@ export const LEAK_FACETS = [ ]; const REVERSED_FACETS = ['coverage', 'new_coverage']; +let scannableProjectsCached: { key: string; name: string }[] | null = null; export function localizeSorting(sort?: string): string { return translate('projects.sort', sort ?? 'name'); @@ -160,6 +161,19 @@ export function parseSorting(sort: string): { sortValue: string; sortDesc: boole return { sortValue: desc ? sort.substring(1) : sort, sortDesc: desc }; } +export async function fetchScannableProjects() { + if (scannableProjectsCached) { + return Promise.resolve({ scannableProjects: scannableProjectsCached }); + } + + const response = await getScannableProjects().then(({ projects }) => { + scannableProjectsCached = projects; + return projects; + }); + + return { scannableProjects: response }; +} + export function fetchProjects({ isFavorite, query, @@ -180,9 +194,13 @@ export function fetchProjects({ return searchProjects(data) .then((response) => - Promise.all([fetchProjectMeasures(response.components, query), Promise.resolve(response)]) + Promise.all([ + fetchProjectMeasures(response.components, query), + Promise.resolve(response), + fetchScannableProjects(), + ]) ) - .then(([measures, { components, facets, paging }]) => { + .then(([measures, { components, facets, paging }, { scannableProjects }]) => { return { facets: getFacetsMap(facets), projects: components.map((component) => { @@ -196,7 +214,11 @@ export function fetchProjects({ } }); - return { ...component, measures: componentMeasures }; + return { + ...component, + measures: componentMeasures, + isScannable: scannableProjects.find((p) => p.key === component.key) !== undefined, + }; }), total: paging.total, }; -- 2.39.5