From: Julien Lancelot Date: Tue, 25 Jun 2019 11:50:25 +0000 (+0200) Subject: SONAR-12140 Do not return "Warning" in quality gate facet when no project in warning X-Git-Tag: 8.0~432 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2629f81a5881ea77e5d3c9a5e56d774f1e70f8b2;p=sonarqube.git SONAR-12140 Do not return "Warning" in quality gate facet when no project in warning --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java index f9bf6565af1..3091a1780c2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java @@ -57,6 +57,7 @@ import org.sonar.server.es.SearchOptions; import org.sonar.server.measure.index.ProjectMeasuresIndex; import org.sonar.server.measure.index.ProjectMeasuresQuery; import org.sonar.server.project.Visibility; +import org.sonar.server.qualitygate.ProjectsInWarning; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Common; import org.sonarqube.ws.Components.Component; @@ -104,11 +105,13 @@ public class SearchProjectsAction implements ComponentsWsAction { private final DbClient dbClient; private final ProjectMeasuresIndex index; private final UserSession userSession; + private final ProjectsInWarning projectsInWarning; - public SearchProjectsAction(DbClient dbClient, ProjectMeasuresIndex index, UserSession userSession) { + public SearchProjectsAction(DbClient dbClient, ProjectMeasuresIndex index, UserSession userSession, ProjectsInWarning projectsInWarning) { this.dbClient = dbClient; this.index = index; this.userSession = userSession; + this.projectsInWarning = projectsInWarning; } @Override @@ -239,6 +242,7 @@ public class SearchProjectsAction implements ComponentsWsAction { Set favoriteProjectUuids = loadFavoriteProjectUuids(dbSession); List criteria = FilterParser.parse(firstNonNull(request.getFilter(), "")); ProjectMeasuresQuery query = newProjectMeasuresQuery(criteria, hasFavoriteFilter(criteria) ? favoriteProjectUuids : null) + .setIgnoreWarning(projectsInWarning.count() == 0L) .setSort(request.getSort()) .setAsc(request.getAsc()); Optional.ofNullable(organization) diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java index 16965c8ad28..e60cf06a1db 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java @@ -50,6 +50,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilde import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.NestedSortBuilder; +import org.sonar.api.measures.Metric; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.System2; import org.sonar.core.util.stream.MoreCollectors; @@ -150,7 +151,7 @@ public class ProjectMeasuresIndex { .put(NEW_RELIABILITY_RATING_KEY, (esSearch, query, facetBuilder) -> addRatingFacet(esSearch, NEW_RELIABILITY_RATING_KEY, facetBuilder)) .put(SECURITY_RATING_KEY, (esSearch, query, facetBuilder) -> addRatingFacet(esSearch, SECURITY_RATING_KEY, facetBuilder)) .put(NEW_SECURITY_RATING_KEY, (esSearch, query, facetBuilder) -> addRatingFacet(esSearch, NEW_SECURITY_RATING_KEY, facetBuilder)) - .put(ALERT_STATUS_KEY, (esSearch, query, facetBuilder) -> esSearch.addAggregation(createStickyFacet(ALERT_STATUS_KEY, facetBuilder, createQualityGateFacet()))) + .put(ALERT_STATUS_KEY, (esSearch, query, facetBuilder) -> esSearch.addAggregation(createStickyFacet(ALERT_STATUS_KEY, facetBuilder, createQualityGateFacet(query)))) .put(FILTER_LANGUAGES, ProjectMeasuresIndex::addLanguagesFacet) .put(FIELD_TAGS, ProjectMeasuresIndex::addTagsFacet) .build(); @@ -338,10 +339,13 @@ public class ProjectMeasuresIndex { new KeyedFilter("5", termQuery(FIELD_MEASURES_VALUE, 5d))))); } - private static AbstractAggregationBuilder createQualityGateFacet() { + private static AbstractAggregationBuilder createQualityGateFacet(ProjectMeasuresQuery projectMeasuresQuery) { return filters( ALERT_STATUS_KEY, - QUALITY_GATE_STATUS.entrySet().stream() + QUALITY_GATE_STATUS + .entrySet() + .stream() + .filter(qgs -> !(projectMeasuresQuery.isIgnoreWarning() && qgs.getKey().equals(Metric.Level.WARN.name()))) .map(entry -> new KeyedFilter(entry.getKey(), termQuery(FIELD_QUALITY_GATE_STATUS, entry.getValue()))) .toArray(KeyedFilter[]::new)); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java index 197f6309874..2074358fcae 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java @@ -46,6 +46,7 @@ public class ProjectMeasuresQuery { private boolean asc = true; private String queryText; private boolean ignoreAuthorization; + private boolean ignoreWarning; public ProjectMeasuresQuery addMetricCriterion(MetricCriterion metricCriterion) { this.metricCriteria.add(metricCriterion); @@ -137,6 +138,15 @@ public class ProjectMeasuresQuery { return this; } + public boolean isIgnoreWarning() { + return ignoreWarning; + } + + public ProjectMeasuresQuery setIgnoreWarning(boolean ignoreWarning) { + this.ignoreWarning = ignoreWarning; + return this; + } + public static class MetricCriterion { private final String metricKey; private final Operator operator; diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java index 8028c359981..c1c13f64918 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java @@ -29,10 +29,12 @@ import java.util.List; import java.util.function.Consumer; import java.util.stream.IntStream; import java.util.stream.Stream; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; +import org.sonar.api.measures.Metric; import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.Param; @@ -52,6 +54,7 @@ import org.sonar.server.measure.index.ProjectMeasuresIndex; import org.sonar.server.measure.index.ProjectMeasuresIndexer; import org.sonar.server.permission.index.PermissionIndexerTester; import org.sonar.server.permission.index.WebAuthorizationTypeSupport; +import org.sonar.server.qualitygate.ProjectsInWarning; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; @@ -64,6 +67,7 @@ import static java.util.Collections.singletonList; import static java.util.Optional.ofNullable; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; +import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY; import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY; import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY; @@ -114,12 +118,12 @@ public class SearchProjectsActionTest { @DataProvider public static Object[][] rating_metric_keys() { - return new Object[][]{{SQALE_RATING_KEY}, {RELIABILITY_RATING_KEY}, {SECURITY_RATING_KEY}}; + return new Object[][] {{SQALE_RATING_KEY}, {RELIABILITY_RATING_KEY}, {SECURITY_RATING_KEY}}; } @DataProvider public static Object[][] new_rating_metric_keys() { - return new Object[][]{{NEW_MAINTAINABILITY_RATING_KEY}, {NEW_RELIABILITY_RATING_KEY}, {NEW_SECURITY_RATING_KEY}}; + return new Object[][] {{NEW_MAINTAINABILITY_RATING_KEY}, {NEW_RELIABILITY_RATING_KEY}, {NEW_SECURITY_RATING_KEY}}; } private DbClient dbClient = db.getDbClient(); @@ -128,12 +132,17 @@ public class SearchProjectsActionTest { private PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, new ProjectMeasuresIndexer(dbClient, es.client())); private ProjectMeasuresIndex index = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE); private ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(db.getDbClient(), es.client()); + private ProjectsInWarning projectsInWarning = new ProjectsInWarning(); - private WsActionTester ws = new WsActionTester( - new SearchProjectsAction(dbClient, index, userSession)); + private WsActionTester ws = new WsActionTester(new SearchProjectsAction(dbClient, index, userSession, projectsInWarning)); private RequestBuilder request = SearchProjectsRequest.builder(); + @Before + public void setUp() throws Exception { + projectsInWarning.update(0L); + } + @Test public void verify_definition() { WebService.Action def = ws.getDef(); @@ -199,18 +208,18 @@ public class SearchProjectsActionTest { MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT")); ComponentDto project1 = insertProject(organization1Dto, c -> c - .setDbKey(KEY_PROJECT_EXAMPLE_001) - .setName("My Project 1") - .setTagsString("finance, java"), + .setDbKey(KEY_PROJECT_EXAMPLE_001) + .setName("My Project 1") + .setTagsString("finance, java"), new Measure(coverage, c -> c.setValue(80d))); ComponentDto project2 = insertProject(organization1Dto, c -> c - .setDbKey(KEY_PROJECT_EXAMPLE_002) - .setName("My Project 2"), + .setDbKey(KEY_PROJECT_EXAMPLE_002) + .setName("My Project 2"), new Measure(coverage, c -> c.setValue(90d))); ComponentDto project3 = insertProject(organization2Dto, c -> c - .setDbKey(KEY_PROJECT_EXAMPLE_003) - .setName("My Project 3") - .setTagsString("sales, offshore, java"), + .setDbKey(KEY_PROJECT_EXAMPLE_003) + .setName("My Project 3") + .setTagsString("sales, offshore, java"), new Measure(coverage, c -> c.setValue(20d))); addFavourite(project1); @@ -629,6 +638,30 @@ public class SearchProjectsActionTest { tuple("500000.0-*", 1L)); } + @Test + public void return_new_lines_facet() { + userSession.logIn(); + OrganizationDto organizationDto = db.organizations().insert(); + MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_LINES_KEY).setValueType(INT.name())); + insertProject(organizationDto, new Measure(coverage, c -> c.setVariation(100d))); + insertProject(organizationDto, new Measure(coverage, c -> c.setVariation(15_000d))); + insertProject(organizationDto, new Measure(coverage, c -> c.setVariation(50_000d))); + + SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_LINES_KEY))); + + Common.Facet facet = result.getFacets().getFacetsList().stream() + .filter(oneFacet -> NEW_LINES_KEY.equals(oneFacet.getProperty())) + .findFirst().orElseThrow(IllegalStateException::new); + assertThat(facet.getValuesList()) + .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount) + .containsExactly( + tuple("*-1000.0", 1L), + tuple("1000.0-10000.0", 0L), + tuple("10000.0-100000.0", 2L), + tuple("100000.0-500000.0", 0L), + tuple("500000.0-*", 0L)); + } + @Test public void return_languages_facet() { userSession.logIn(); @@ -873,51 +906,49 @@ public class SearchProjectsActionTest { } @Test - public void return_ncloc_facet() { + public void return_quality_gate_facet() { userSession.logIn(); OrganizationDto organizationDto = db.organizations().insert(); - MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name())); - insertProject(organizationDto, new Measure(coverage, c -> c.setValue(100d))); - insertProject(organizationDto, new Measure(coverage, c -> c.setValue(15_000d))); - insertProject(organizationDto, new Measure(coverage, c -> c.setValue(50_000d))); + MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(ALERT_STATUS_KEY).setValueType(LEVEL.name())); + insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null))); + insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null))); + insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData(Metric.Level.WARN.name()).setValue(null))); + insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData(Metric.Level.OK.name()).setValue(null))); + projectsInWarning.update(1L); - SearchProjectsWsResponse result = call(request.setFacets(singletonList(NCLOC))); + SearchProjectsWsResponse result = call(request.setFacets(singletonList(ALERT_STATUS_KEY))); Common.Facet facet = result.getFacets().getFacetsList().stream() - .filter(oneFacet -> NCLOC.equals(oneFacet.getProperty())) + .filter(oneFacet -> ALERT_STATUS_KEY.equals(oneFacet.getProperty())) .findFirst().orElseThrow(IllegalStateException::new); assertThat(facet.getValuesList()) .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount) - .containsExactly( - tuple("*-1000.0", 1L), - tuple("1000.0-10000.0", 0L), - tuple("10000.0-100000.0", 2L), - tuple("100000.0-500000.0", 0L), - tuple("500000.0-*", 0L)); + .containsOnly( + tuple("OK", 1L), + tuple("ERROR", 2L), + tuple("WARN", 1L)); } @Test - public void return_new_lines_facet() { + public void return_quality_gate_facet_without_warning_when_no_projects_in_warning() { userSession.logIn(); OrganizationDto organizationDto = db.organizations().insert(); - MetricDto coverage = db.measures().insertMetric(c -> c.setKey(NEW_LINES_KEY).setValueType(INT.name())); - insertProject(organizationDto, new Measure(coverage, c -> c.setVariation(100d))); - insertProject(organizationDto, new Measure(coverage, c -> c.setVariation(15_000d))); - insertProject(organizationDto, new Measure(coverage, c -> c.setVariation(50_000d))); + MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(ALERT_STATUS_KEY).setValueType(LEVEL.name())); + insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null))); + insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData(Metric.Level.ERROR.name()).setValue(null))); + insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData(Metric.Level.OK.name()).setValue(null))); + projectsInWarning.update(0L); - SearchProjectsWsResponse result = call(request.setFacets(singletonList(NEW_LINES_KEY))); + SearchProjectsWsResponse result = call(request.setFacets(singletonList(ALERT_STATUS_KEY))); Common.Facet facet = result.getFacets().getFacetsList().stream() - .filter(oneFacet -> NEW_LINES_KEY.equals(oneFacet.getProperty())) + .filter(oneFacet -> ALERT_STATUS_KEY.equals(oneFacet.getProperty())) .findFirst().orElseThrow(IllegalStateException::new); assertThat(facet.getValuesList()) .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount) - .containsExactly( - tuple("*-1000.0", 1L), - tuple("1000.0-10000.0", 0L), - tuple("10000.0-100000.0", 2L), - tuple("100000.0-500000.0", 0L), - tuple("500000.0-*", 0L)); + .containsOnly( + tuple("OK", 1L), + tuple("ERROR", 2L)); } @Test @@ -1099,7 +1130,7 @@ public class SearchProjectsActionTest { List projects = call(request .setFilter("alert_status = WARN")) - .getComponentsList(); + .getComponentsList(); assertThat(projects) .extracting(Component::getKey) diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java index e9853f9093b..741d823a6e8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java @@ -1144,6 +1144,27 @@ public class ProjectMeasuresIndexTest { entry(OK.name(), 2L)); } + @Test + public void facet_quality_gate_does_not_return_deprecated_warning_when_set_ignore_warning_is_true() { + index( + // 2 docs with QG OK + newDoc().setQualityGateStatus(OK.name()), + newDoc().setQualityGateStatus(OK.name()), + // 4 docs with QG ERROR + newDoc().setQualityGateStatus(ERROR.name()), + newDoc().setQualityGateStatus(ERROR.name()), + newDoc().setQualityGateStatus(ERROR.name()), + newDoc().setQualityGateStatus(ERROR.name())); + + assertThat(underTest.search(new ProjectMeasuresQuery().setIgnoreWarning(true), new SearchOptions().addFacets(ALERT_STATUS_KEY)).getFacets().get(ALERT_STATUS_KEY)).containsOnly( + entry(ERROR.name(), 4L), + entry(OK.name(), 2L)); + assertThat(underTest.search(new ProjectMeasuresQuery().setIgnoreWarning(false), new SearchOptions().addFacets(ALERT_STATUS_KEY)).getFacets().get(ALERT_STATUS_KEY)).containsOnly( + entry(ERROR.name(), 4L), + entry(WARN.name(), 0L), + entry(OK.name(), 2L)); + } + @Test public void facet_languages() { index( diff --git a/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx b/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx index 7fa456cadc3..b5f4d8f4c54 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx +++ b/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx @@ -37,6 +37,9 @@ export interface Props { } export default function QualityGateFilter(props: Props) { + const hasWarnStatus = props.facet && props.facet['WARN'] !== undefined; + const options = hasWarnStatus ? ['OK', 'WARN', 'ERROR'] : ['OK', 'ERROR']; + return ( } maxFacetValue={props.maxFacetValue} onQueryChange={props.onQueryChange} - options={['OK', 'WARN', 'ERROR']} + options={options} organization={props.organization} property="gate" query={props.query} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx index 8c5c6981207..49eb71d6cf5 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx @@ -19,15 +19,27 @@ */ import * as React from 'react'; import { shallow } from 'enzyme'; -import QualityGateFilter from '../QualityGateFilter'; +import QualityGateFilter, { Props } from '../QualityGateFilter'; it('renders', () => { - const wrapper = shallow(); + const wrapper = shallowRender(); expect(wrapper).toMatchSnapshot(); const renderOption = wrapper.prop('renderOption'); expect(renderOption(2, false)).toMatchSnapshot(); const getFacetValueForOption = wrapper.prop('getFacetValueForOption'); - expect(getFacetValueForOption({ ERROR: 1, WARN: 2, OK: 3 }, 'WARN')).toBe(2); + expect(getFacetValueForOption({ ERROR: 1, OK: 3 }, 'OK')).toBe(3); }); + +it('should render with warning facet', () => { + expect( + shallowRender({ facet: { ERROR: 1, WARN: 2, OK: 3 } }) + .find('Filter') + .prop('options') + ).toEqual(['OK', 'WARN', 'ERROR']); +}); + +function shallowRender(props: Partial = {}) { + return shallow(); +} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap index b17e817a49c..66fc1206b81 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap @@ -12,7 +12,6 @@ exports[`renders 1`] = ` options={ Array [ "OK", - "WARN", "ERROR", ] }