3 * Copyright (C) 2009-2020 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.qualitygate.ws;
22 import com.google.gson.JsonArray;
23 import com.google.gson.JsonElement;
24 import com.google.gson.JsonObject;
25 import com.google.gson.JsonParser;
26 import java.util.Optional;
27 import java.util.function.Predicate;
28 import java.util.stream.StreamSupport;
29 import javax.annotation.Nullable;
30 import org.sonar.db.component.SnapshotDto;
31 import org.sonarqube.ws.Qualitygates.ProjectStatusResponse;
33 import static com.google.common.base.Strings.isNullOrEmpty;
34 import static org.sonar.api.utils.DateUtils.formatDateTime;
36 public class QualityGateDetailsFormatter {
37 private final Optional<String> optionalMeasureData;
38 private final Optional<SnapshotDto> optionalSnapshot;
39 private final ProjectStatusResponse.ProjectStatus.Builder projectStatusBuilder;
41 public QualityGateDetailsFormatter(Optional<String> measureData, Optional<SnapshotDto> snapshot) {
42 this.optionalMeasureData = measureData;
43 this.optionalSnapshot = snapshot;
44 this.projectStatusBuilder = ProjectStatusResponse.ProjectStatus.newBuilder();
47 public ProjectStatusResponse.ProjectStatus format() {
48 if (!optionalMeasureData.isPresent()) {
49 return newResponseWithoutQualityGateDetails();
52 JsonObject json = JsonParser.parseString(optionalMeasureData.get()).getAsJsonObject();
54 ProjectStatusResponse.Status qualityGateStatus = measureLevelToQualityGateStatus(json.get("level").getAsString());
55 projectStatusBuilder.setStatus(qualityGateStatus);
57 formatIgnoredConditions(json);
58 formatConditions(json.getAsJsonArray("conditions"));
61 return projectStatusBuilder.build();
64 private void formatIgnoredConditions(JsonObject json) {
65 JsonElement ignoredConditions = json.get("ignoredConditions");
66 if (ignoredConditions != null) {
67 projectStatusBuilder.setIgnoredConditions(ignoredConditions.getAsBoolean());
69 projectStatusBuilder.setIgnoredConditions(false);
73 private void formatPeriods() {
74 if (!optionalSnapshot.isPresent()) {
78 ProjectStatusResponse.Period.Builder periodBuilder = ProjectStatusResponse.Period.newBuilder();
79 periodBuilder.clear();
81 SnapshotDto snapshot = this.optionalSnapshot.get();
83 if (isNullOrEmpty(snapshot.getPeriodMode())) {
86 periodBuilder.setIndex(1);
87 periodBuilder.setMode(snapshot.getPeriodMode());
88 Long periodDate = snapshot.getPeriodDate();
89 if (periodDate != null) {
90 periodBuilder.setDate(formatDateTime(periodDate));
92 String periodModeParameter = snapshot.getPeriodModeParameter();
93 if (!isNullOrEmpty(periodModeParameter)) {
94 periodBuilder.setParameter(periodModeParameter);
96 projectStatusBuilder.addPeriods(periodBuilder);
99 private void formatConditions(@Nullable JsonArray jsonConditions) {
100 if (jsonConditions == null) {
104 StreamSupport.stream(jsonConditions.spliterator(), false)
105 .map(JsonElement::getAsJsonObject)
106 .filter(isConditionOnValidPeriod())
107 .forEach(this::formatCondition);
110 private void formatCondition(JsonObject jsonCondition) {
111 ProjectStatusResponse.Condition.Builder conditionBuilder = ProjectStatusResponse.Condition.newBuilder();
113 formatConditionLevel(conditionBuilder, jsonCondition);
114 formatConditionMetric(conditionBuilder, jsonCondition);
115 formatConditionOperation(conditionBuilder, jsonCondition);
116 formatConditionPeriod(conditionBuilder, jsonCondition);
117 formatConditionWarningThreshold(conditionBuilder, jsonCondition);
118 formatConditionErrorThreshold(conditionBuilder, jsonCondition);
119 formatConditionActual(conditionBuilder, jsonCondition);
121 projectStatusBuilder.addConditions(conditionBuilder);
124 private static void formatConditionActual(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
125 JsonElement actual = jsonCondition.get("actual");
126 if (actual != null && !isNullOrEmpty(actual.getAsString())) {
127 conditionBuilder.setActualValue(actual.getAsString());
131 private static void formatConditionErrorThreshold(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
132 JsonElement error = jsonCondition.get("error");
133 if (error != null && !isNullOrEmpty(error.getAsString())) {
134 conditionBuilder.setErrorThreshold(error.getAsString());
138 private static void formatConditionWarningThreshold(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
139 JsonElement warning = jsonCondition.get("warning");
140 if (warning != null && !isNullOrEmpty(warning.getAsString())) {
141 conditionBuilder.setWarningThreshold(warning.getAsString());
145 private static void formatConditionPeriod(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
146 JsonElement periodIndex = jsonCondition.get("period");
147 if (periodIndex == null) {
150 conditionBuilder.setPeriodIndex(periodIndex.getAsInt());
153 private static void formatConditionOperation(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
154 JsonElement op = jsonCondition.get("op");
155 if (op != null && !isNullOrEmpty(op.getAsString())) {
156 String stringOp = op.getAsString();
157 ProjectStatusResponse.Comparator comparator = measureOpToQualityGateComparator(stringOp);
158 conditionBuilder.setComparator(comparator);
162 private static void formatConditionMetric(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
163 JsonElement metric = jsonCondition.get("metric");
164 if (metric != null && !isNullOrEmpty(metric.getAsString())) {
165 conditionBuilder.setMetricKey(metric.getAsString());
169 private static void formatConditionLevel(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
170 JsonElement measureLevel = jsonCondition.get("level");
171 if (measureLevel != null && !isNullOrEmpty(measureLevel.getAsString())) {
172 conditionBuilder.setStatus(measureLevelToQualityGateStatus(measureLevel.getAsString()));
176 private static ProjectStatusResponse.Status measureLevelToQualityGateStatus(String measureLevel) {
177 for (ProjectStatusResponse.Status status : ProjectStatusResponse.Status.values()) {
178 if (status.name().equals(measureLevel)) {
183 throw new IllegalStateException(String.format("Unknown quality gate status '%s'", measureLevel));
186 private static ProjectStatusResponse.Comparator measureOpToQualityGateComparator(String measureOp) {
187 for (ProjectStatusResponse.Comparator comparator : ProjectStatusResponse.Comparator.values()) {
188 if (comparator.name().equals(measureOp)) {
193 throw new IllegalStateException(String.format("Unknown quality gate comparator '%s'", measureOp));
196 private static ProjectStatusResponse.ProjectStatus newResponseWithoutQualityGateDetails() {
197 return ProjectStatusResponse.ProjectStatus.newBuilder().setStatus(ProjectStatusResponse.Status.NONE).build();
200 private static Predicate<JsonObject> isConditionOnValidPeriod() {
201 return jsonCondition -> {
202 JsonElement periodIndex = jsonCondition.get("period");
203 if (periodIndex == null) {
206 int period = periodIndex.getAsInt();
207 // Ignore period that was set on non leak period (for retro compatibility with project that hasn't been analyzed since a while)