3 * Copyright (C) 2009-2022 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;
32 import org.sonarqube.ws.Qualitygates.ProjectStatusResponse.NewCodePeriod;
33 import org.sonarqube.ws.Qualitygates.ProjectStatusResponse.Period;
35 import static com.google.common.base.Strings.isNullOrEmpty;
36 import static org.sonar.api.utils.DateUtils.formatDateTime;
38 public class QualityGateDetailsFormatter {
39 private final Optional<String> optionalMeasureData;
40 private final Optional<SnapshotDto> optionalSnapshot;
41 private final ProjectStatusResponse.ProjectStatus.Builder projectStatusBuilder;
43 public QualityGateDetailsFormatter(Optional<String> measureData, Optional<SnapshotDto> snapshot) {
44 this.optionalMeasureData = measureData;
45 this.optionalSnapshot = snapshot;
46 this.projectStatusBuilder = ProjectStatusResponse.ProjectStatus.newBuilder();
49 public ProjectStatusResponse.ProjectStatus format() {
50 if (!optionalMeasureData.isPresent()) {
51 return newResponseWithoutQualityGateDetails();
54 JsonObject json = JsonParser.parseString(optionalMeasureData.get()).getAsJsonObject();
56 ProjectStatusResponse.Status qualityGateStatus = measureLevelToQualityGateStatus(json.get("level").getAsString());
57 projectStatusBuilder.setStatus(qualityGateStatus);
59 formatIgnoredConditions(json);
60 formatConditions(json.getAsJsonArray("conditions"));
63 return projectStatusBuilder.build();
66 private void formatIgnoredConditions(JsonObject json) {
67 JsonElement ignoredConditions = json.get("ignoredConditions");
68 if (ignoredConditions != null) {
69 projectStatusBuilder.setIgnoredConditions(ignoredConditions.getAsBoolean());
71 projectStatusBuilder.setIgnoredConditions(false);
75 private void formatPeriods() {
76 if (!optionalSnapshot.isPresent()) {
80 Period.Builder periodsBuilder = Period.newBuilder();
81 NewCodePeriod.Builder periodBuilder = NewCodePeriod.newBuilder();
83 SnapshotDto snapshot = this.optionalSnapshot.get();
85 if (isNullOrEmpty(snapshot.getPeriodMode())) {
88 periodsBuilder.setIndex(1);
89 periodsBuilder.setMode(snapshot.getPeriodMode());
90 periodBuilder.setMode(snapshot.getPeriodMode());
91 Long periodDate = snapshot.getPeriodDate();
92 if (periodDate != null) {
93 String formattedDateTime = formatDateTime(periodDate);
94 periodsBuilder.setDate(formattedDateTime);
95 periodBuilder.setDate(formattedDateTime);
97 String periodModeParameter = snapshot.getPeriodModeParameter();
98 if (!isNullOrEmpty(periodModeParameter)) {
99 periodsBuilder.setParameter(periodModeParameter);
100 periodBuilder.setParameter(periodModeParameter);
102 projectStatusBuilder.addPeriods(periodsBuilder);
103 projectStatusBuilder.setPeriod(periodBuilder);
106 private void formatConditions(@Nullable JsonArray jsonConditions) {
107 if (jsonConditions == null) {
111 StreamSupport.stream(jsonConditions.spliterator(), false)
112 .map(JsonElement::getAsJsonObject)
113 .filter(isConditionOnValidPeriod())
114 .forEach(this::formatCondition);
117 private void formatCondition(JsonObject jsonCondition) {
118 ProjectStatusResponse.Condition.Builder conditionBuilder = ProjectStatusResponse.Condition.newBuilder();
120 formatConditionLevel(conditionBuilder, jsonCondition);
121 formatConditionMetric(conditionBuilder, jsonCondition);
122 formatConditionOperation(conditionBuilder, jsonCondition);
123 formatConditionPeriod(conditionBuilder, jsonCondition);
124 formatConditionWarningThreshold(conditionBuilder, jsonCondition);
125 formatConditionErrorThreshold(conditionBuilder, jsonCondition);
126 formatConditionActual(conditionBuilder, jsonCondition);
128 projectStatusBuilder.addConditions(conditionBuilder);
131 private static void formatConditionActual(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
132 JsonElement actual = jsonCondition.get("actual");
133 if (actual != null && !isNullOrEmpty(actual.getAsString())) {
134 conditionBuilder.setActualValue(actual.getAsString());
138 private static void formatConditionErrorThreshold(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
139 JsonElement error = jsonCondition.get("error");
140 if (error != null && !isNullOrEmpty(error.getAsString())) {
141 conditionBuilder.setErrorThreshold(error.getAsString());
145 private static void formatConditionWarningThreshold(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
146 JsonElement warning = jsonCondition.get("warning");
147 if (warning != null && !isNullOrEmpty(warning.getAsString())) {
148 conditionBuilder.setWarningThreshold(warning.getAsString());
152 private static void formatConditionPeriod(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
153 JsonElement periodIndex = jsonCondition.get("period");
154 if (periodIndex == null) {
157 conditionBuilder.setPeriodIndex(periodIndex.getAsInt());
160 private static void formatConditionOperation(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
161 JsonElement op = jsonCondition.get("op");
162 if (op != null && !isNullOrEmpty(op.getAsString())) {
163 String stringOp = op.getAsString();
164 ProjectStatusResponse.Comparator comparator = measureOpToQualityGateComparator(stringOp);
165 conditionBuilder.setComparator(comparator);
169 private static void formatConditionMetric(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
170 JsonElement metric = jsonCondition.get("metric");
171 if (metric != null && !isNullOrEmpty(metric.getAsString())) {
172 conditionBuilder.setMetricKey(metric.getAsString());
176 private static void formatConditionLevel(ProjectStatusResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) {
177 JsonElement measureLevel = jsonCondition.get("level");
178 if (measureLevel != null && !isNullOrEmpty(measureLevel.getAsString())) {
179 conditionBuilder.setStatus(measureLevelToQualityGateStatus(measureLevel.getAsString()));
183 private static ProjectStatusResponse.Status measureLevelToQualityGateStatus(String measureLevel) {
184 for (ProjectStatusResponse.Status status : ProjectStatusResponse.Status.values()) {
185 if (status.name().equals(measureLevel)) {
190 throw new IllegalStateException(String.format("Unknown quality gate status '%s'", measureLevel));
193 private static ProjectStatusResponse.Comparator measureOpToQualityGateComparator(String measureOp) {
194 for (ProjectStatusResponse.Comparator comparator : ProjectStatusResponse.Comparator.values()) {
195 if (comparator.name().equals(measureOp)) {
200 throw new IllegalStateException(String.format("Unknown quality gate comparator '%s'", measureOp));
203 private static ProjectStatusResponse.ProjectStatus newResponseWithoutQualityGateDetails() {
204 return ProjectStatusResponse.ProjectStatus.newBuilder().setStatus(ProjectStatusResponse.Status.NONE).build();
207 private static Predicate<JsonObject> isConditionOnValidPeriod() {
208 return jsonCondition -> {
209 JsonElement periodIndex = jsonCondition.get("period");
210 if (periodIndex == null) {
213 int period = periodIndex.getAsInt();
214 // Ignore period that was set on non leak period (for retro compatibility with project that hasn't been analyzed since a while)