// exclude project_badge url, as they can be auth. by a token as queryparam
assertPathIsIgnored("/api/project_badges/measure");
assertPathIsIgnored("/api/project_badges/quality_gate");
+ assertPathIsIgnored("/api/project_badges/ai_code_assurance");
// exlude passcode urls
assertPathIsIgnoredWithAnonymousAccess("/api/ce/info");
"/api/server/version",
"/api/users/identity_providers", "/api/l10n/index",
"/api/authentication/login", "/api/authentication/logout", "/api/authentication/validate",
- "/api/project_badges/measure", "/api/project_badges/quality_gate",
+ "/api/project_badges/measure", "/api/project_badges/quality_gate", "/api/project_badges/ai_code_assurance",
"/api/settings/login_message");
private static final Set<String> URL_USING_PASSCODE = Set.of(
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.badge.ws;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Optional;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.commons.io.IOUtils.write;
+import static org.sonar.server.badge.ws.ETagUtils.RFC1123_DATE;
+import static org.sonar.server.badge.ws.ETagUtils.getETag;
+import static org.sonarqube.ws.MediaTypes.SVG;
+
+public abstract class AbstractProjectBadgesWsAction implements ProjectBadgesWsAction {
+ protected final ProjectBadgesSupport support;
+ protected final SvgGenerator svgGenerator;
+
+ protected AbstractProjectBadgesWsAction(ProjectBadgesSupport support, SvgGenerator svgGenerator) {
+ this.support = support;
+ this.svgGenerator = svgGenerator;
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws IOException {
+ response.setHeader("Cache-Control", "no-cache");
+ response.stream().setMediaType(SVG);
+ try {
+ support.validateToken(request);
+ String result = getBadge(request);
+ String eTag = getETag(result);
+ Optional<String> requestedETag = request.header("If-None-Match");
+ if (requestedETag.filter(eTag::equals).isPresent()) {
+ response.stream().setStatus(304);
+ return;
+ }
+ response.setHeader("ETag", eTag);
+ write(result, response.stream().output(), UTF_8);
+ } catch (ProjectBadgesException | ForbiddenException | NotFoundException e) {
+ // There is an issue, so do not return any ETag but make this response expire now
+ SimpleDateFormat sdf = new SimpleDateFormat(RFC1123_DATE, Locale.US);
+ response.setHeader("Expires", sdf.format(new Date()));
+ write(svgGenerator.generateError(e.getMessage()), response.stream().output(), UTF_8);
+ }
+ }
+
+ protected abstract String getBadge(Request request);
+}
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Resources;
-import java.text.SimpleDateFormat;
-import java.util.Date;
import java.util.EnumMap;
-import java.util.Locale;
import java.util.Map;
-import java.util.Optional;
import java.util.function.Function;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.NewAction;
import org.sonar.db.DbClient;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.badge.ws.SvgGenerator.Color;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.measure.Rating;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.commons.io.IOUtils.write;
import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
import static org.sonar.api.measures.Metric.Level.ERROR;
import static org.sonar.api.measures.Metric.Level.OK;
import static org.sonar.api.measures.Metric.ValueType;
-import static org.sonar.server.badge.ws.ETagUtils.RFC1123_DATE;
-import static org.sonar.server.badge.ws.ETagUtils.getETag;
import static org.sonar.server.badge.ws.SvgFormatter.formatDuration;
import static org.sonar.server.badge.ws.SvgFormatter.formatNumeric;
import static org.sonar.server.badge.ws.SvgFormatter.formatPercent;
import static org.sonar.server.measure.Rating.D;
import static org.sonar.server.measure.Rating.E;
import static org.sonar.server.measure.Rating.valueOf;
-import static org.sonarqube.ws.MediaTypes.SVG;
-public class MeasureAction implements ProjectBadgesWsAction {
+public class MeasureAction extends AbstractProjectBadgesWsAction {
private static final String PARAM_METRIC = "metric";
E, Color.RATING_E));
private final DbClient dbClient;
- private final ProjectBadgesSupport support;
- private final SvgGenerator svgGenerator;
public MeasureAction(DbClient dbClient, ProjectBadgesSupport support, SvgGenerator svgGenerator) {
+ super(support, svgGenerator);
this.dbClient = dbClient;
- this.support = support;
- this.svgGenerator = svgGenerator;
}
@Override
.setDescription("Generate badge for project's measure as an SVG.<br/>" +
"Requires 'Browse' permission on the specified project.")
.setSince("7.1")
- .setChangelog(new Change("10.4", String.format("The following metric keys are now deprecated: %s", String.join(", ", DEPRECATED_METRIC_KEYS))))
+ .setChangelog(new Change("10.4", String.format("The following metric keys are now deprecated: %s", String.join(", ",
+ DEPRECATED_METRIC_KEYS))))
.setResponseExample(Resources.getResource(getClass(), "measure-example.svg"));
support.addProjectAndBranchParams(action);
action.createParam(PARAM_METRIC)
}
@Override
- public void handle(Request request, Response response) throws Exception {
- response.setHeader("Cache-Control", "no-cache");
- response.stream().setMediaType(SVG);
- String metricKey = request.mandatoryParam(PARAM_METRIC);
+ protected String getBadge(Request request) {
try (DbSession dbSession = dbClient.openSession(false)) {
- support.validateToken(request);
+ String metricKey = request.mandatoryParam(PARAM_METRIC);
BranchDto branch = support.getBranch(dbSession, request);
MetricDto metric = dbClient.metricDao().selectByKey(dbSession, metricKey);
checkState(metric != null && metric.isEnabled(), "Metric '%s' hasn't been found", metricKey);
LiveMeasureDto measure = getMeasure(dbSession, branch, metricKey);
- String result = generateSvg(metric, measure);
- String eTag = getETag(result);
- Optional<String> requestedETag = request.header("If-None-Match");
- if (requestedETag.filter(eTag::equals).isPresent()) {
- response.stream().setStatus(304);
- return;
- }
- response.setHeader("ETag", eTag);
- write(result, response.stream().output(), UTF_8);
- } catch (ProjectBadgesException | ForbiddenException | NotFoundException e) {
- // There is an issue, so do not return any ETag but make this response expire now
- SimpleDateFormat sdf = new SimpleDateFormat(RFC1123_DATE, Locale.US);
- response.setHeader("Expires", sdf.format(new Date()));
- write(svgGenerator.generateError(e.getMessage()), response.stream().output(), UTF_8);
+ return generateSvg(metric, measure);
}
}
}
void addProjectAndBranchParams(WebService.NewAction action) {
- action.createParam(PARAM_PROJECT)
- .setDescription("Project or application key")
- .setRequired(true)
- .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+ addProjectAndTokenParams(action);
action
.createParam(PARAM_BRANCH)
.setDescription("Branch key")
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+ }
+
+ public void addProjectAndTokenParams(WebService.NewAction action) {
+ action.createParam(PARAM_PROJECT)
+ .setDescription("Project or application key")
+ .setRequired(true)
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001);
action
.createParam(PARAM_TOKEN)
.setDescription("Project badge token")
BranchDto getBranch(DbSession dbSession, Request request) {
try {
- String projectKey = request.mandatoryParam(PARAM_PROJECT);
String branchName = request.param(PARAM_BRANCH);
- ProjectDto project = componentFinder.getProjectOrApplicationByKey(dbSession, projectKey);
+ ProjectDto project = getProject(dbSession, request);
BranchDto branch = componentFinder.getBranchOrPullRequest(dbSession, project, branchName, null);
}
}
+ public ProjectDto getProject(DbSession dbSession, Request request) {
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ return componentFinder.getProjectOrApplicationByKey(dbSession, projectKey);
+ }
+
private static ProjectBadgesException generateInvalidProjectException() {
return new ProjectBadgesException("Project is invalid");
}
import org.sonar.server.ws.WsAction;
-interface ProjectBadgesWsAction extends WsAction {
+public interface ProjectBadgesWsAction extends WsAction {
// Marker interface
}
package org.sonar.server.badge.ws;
import com.google.common.io.Resources;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Optional;
import org.sonar.api.measures.Metric.Level;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.NewAction;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
import org.sonar.db.measure.LiveMeasureDto;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.exceptions.NotFoundException;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.commons.io.IOUtils.write;
import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
-import static org.sonar.server.badge.ws.ETagUtils.RFC1123_DATE;
-import static org.sonar.server.badge.ws.ETagUtils.getETag;
-import static org.sonarqube.ws.MediaTypes.SVG;
-public class QualityGateAction implements ProjectBadgesWsAction {
+public class QualityGateAction extends AbstractProjectBadgesWsAction {
private final DbClient dbClient;
- private final ProjectBadgesSupport support;
- private final SvgGenerator svgGenerator;
public QualityGateAction(DbClient dbClient, ProjectBadgesSupport support, SvgGenerator svgGenerator) {
+ super(support, svgGenerator);
this.dbClient = dbClient;
- this.support = support;
- this.svgGenerator = svgGenerator;
}
@Override
}
@Override
- public void handle(Request request, Response response) throws Exception {
- response.setHeader("Cache-Control", "no-cache");
- response.stream().setMediaType(SVG);
+ protected String getBadge(Request request) {
try (DbSession dbSession = dbClient.openSession(false)) {
- support.validateToken(request);
BranchDto branch = support.getBranch(dbSession, request);
Level qualityGateStatus = getQualityGate(dbSession, branch);
- String result = svgGenerator.generateQualityGate(qualityGateStatus);
- String eTag = getETag(result);
- Optional<String> requestedETag = request.header("If-None-Match");
- if (requestedETag.filter(eTag::equals).isPresent()) {
- response.stream().setStatus(304);
- return;
- }
- response.setHeader("ETag", eTag);
- write(result, response.stream().output(), UTF_8);
- } catch (ProjectBadgesException | ForbiddenException | NotFoundException e) {
- // There is an issue, so do not return any ETag but make this response expire now
- SimpleDateFormat sdf = new SimpleDateFormat(RFC1123_DATE, Locale.US);
- response.setHeader("Expires", sdf.format(new Date()));
- write(svgGenerator.generateError(e.getMessage()), response.stream().output(), UTF_8);
+ return svgGenerator.generateQualityGate(qualityGateStatus);
}
}
return strSubstitutor.replace(errorTemplate);
}
+ public static String readTemplate(String template, Class<?> clazz) {
+ try {
+ return IOUtils.toString(clazz.getResource(template), UTF_8);
+ } catch (IOException e) {
+ throw new IllegalStateException(String.format("Can't read svg template '%s'", template), e);
+ }
+ }
+
private static int computeWidth(String text) {
return text.chars()
.mapToObj(i -> (char) i)
}
private String readTemplate(String template) {
- try {
- return IOUtils.toString(getClass().getResource(template), UTF_8);
- } catch (IOException e) {
- throw new IllegalStateException(String.format("Can't read svg template '%s'", template), e);
- }
+ return readTemplate(template, getClass());
}
static class Color {