@Override
public void handle(Request request, Response response) throws Exception {
response.stream().setMediaType(SVG);
- String projectKey = request.mandatoryParam(PARAM_PROJECT);
- String branch = request.param(PARAM_BRANCH);
- String pullRequest = request.param(PARAM_PULL_REQUEST);
String metricKey = request.mandatoryParam(PARAM_METRIC);
try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest);
- userSession.checkComponentPermission(USER, project);
+ ComponentDto project = getProject(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, project, metricKey);
}
}
+ private ComponentDto getProject(DbSession dbSession, Request request) {
+ try {
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
+ ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest);
+ userSession.checkComponentPermission(USER, project);
+ return project;
+ } catch (NotFoundException e) {
+ throw new NotFoundException("Component not found");
+ }
+ }
+
private LiveMeasureDto getMeasure(DbSession dbSession, ComponentDto project, String metricKey) {
return dbClient.liveMeasureDao().selectMeasure(dbSession, project.uuid(), metricKey)
- .orElseThrow(() -> new ProjectBadgesException(format("Measure '%s' has not been found for project '%s' and branch '%s'", metricKey, project.getKey(), project.getBranch())));
+ .orElseThrow(() -> new ProjectBadgesException("Measure has not been found"));
}
private String generateSvg(MetricDto metric, LiveMeasureDto measure) {
@Override
public void handle(Request request, Response response) throws Exception {
response.stream().setMediaType(SVG);
- String projectKey = request.mandatoryParam(PARAM_PROJECT);
- String branch = request.param(PARAM_BRANCH);
- String pullRequest = request.param(PARAM_PULL_REQUEST);
try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest);
+ ComponentDto project = getProject(dbSession, request);
userSession.checkComponentPermission(USER, project);
Level qualityGateStatus = getQualityGate(dbSession, project);
write(svgGenerator.generateQualityGate(qualityGateStatus), response.stream().output(), UTF_8);
}
}
+ private ComponentDto getProject(DbSession dbSession, Request request) {
+ try {
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
+ ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest);
+ userSession.checkComponentPermission(USER, project);
+ return project;
+ } catch (NotFoundException e) {
+ throw new NotFoundException("Component not found");
+ }
+ }
+
private Level getQualityGate(DbSession dbSession, ComponentDto project) {
return Level.valueOf(dbClient.liveMeasureDao().selectMeasure(dbSession, project.uuid(), CoreMetrics.ALERT_STATUS_KEY)
.map(LiveMeasureDto::getTextValue)
package org.sonar.server.badge.ws;
import com.google.common.collect.ImmutableMap;
-import java.awt.Font;
-import java.awt.font.FontRenderContext;
-import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.sonar.api.measures.Metric;
import org.sonar.api.server.ServerSide;
+import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.valueOf;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.sonar.api.measures.Metric.Level.ERROR;
@ServerSide
public class SvgGenerator {
+ private static final Map<Character, Integer> CHAR_LENGTH = ImmutableMap.<Character, Integer>builder()
+ .put('0', 7)
+ .put('1', 7)
+ .put('2', 7)
+ .put('3', 7)
+ .put('4', 7)
+ .put('5', 7)
+ .put('6', 7)
+ .put('7', 7)
+ .put('8', 7)
+ .put('9', 7)
+ .put('a', 7)
+ .put('b', 7)
+ .put('c', 6)
+ .put('d', 7)
+ .put('e', 6)
+ .put('f', 4)
+ .put('g', 7)
+ .put('h', 7)
+ .put('i', 3)
+ .put('j', 5)
+ .put('k', 6)
+ .put('l', 3)
+ .put('m', 11)
+ .put('n', 7)
+ .put('o', 7)
+ .put('p', 7)
+ .put('q', 7)
+ .put('r', 5)
+ .put('s', 6)
+ .put('t', 4)
+ .put('u', 7)
+ .put('v', 6)
+ .put('w', 9)
+ .put('x', 6)
+ .put('y', 6)
+ .put('z', 6)
+ .put('A', 7)
+ .put('B', 7)
+ .put('C', 8)
+ .put('D', 8)
+ .put('E', 7)
+ .put('F', 6)
+ .put('G', 8)
+ .put('H', 8)
+ .put('I', 5)
+ .put('J', 5)
+ .put('K', 7)
+ .put('L', 6)
+ .put('M', 9)
+ .put('N', 8)
+ .put('O', 9)
+ .put('P', 7)
+ .put('Q', 9)
+ .put('R', 8)
+ .put('S', 7)
+ .put('T', 7)
+ .put('U', 8)
+ .put('V', 10)
+ .put('W', 10)
+ .put('X', 7)
+ .put('Y', 7)
+ .put('Z', 7)
+ .put('%', 12)
+ .put(' ', 4)
+ .put('.', 4)
+ .put('_', 7)
+ .put('\'', 3)
+ .build();
+
private static final String TEMPLATES_SONARCLOUD = "templates/sonarcloud";
private static final String TEMPLATES_SONARQUBE = "templates/sonarqube";
- private static final FontRenderContext FONT_RENDER_CONTEXT = new FontRenderContext(new AffineTransform(), true, true);
- private static final Font FONT = new Font("Verdana", Font.PLAIN, 11);
-
private static final int MARGIN = 6;
private static final int ICON_WIDTH = 20;
private static final String PARAMETER_ICON_WIDTH_PLUS_MARGIN = "iconWidthPlusMargin";
private static final String PARAMETER_TOTAL_WIDTH = "totalWidth";
+ private static final String PARAMETER_LEFT_WIDTH = "leftWidth";
+ private static final String PARAMETER_LEFT_WIDTH_PLUS_MARGIN = "leftWidthPlusMargin";
+ private static final String PARAMETER_RIGHT_WIDTH = "rightWidth";
private static final String PARAMETER_LABEL_WIDTH = "labelWidth";
- private static final String PARAMETER_LABEL_WIDTH_PLUS_MARGIN = "labelWidthPlusMargin";
private static final String PARAMETER_VALUE_WIDTH = "valueWidth";
private static final String PARAMETER_COLOR = "color";
private static final String PARAMETER_LABEL = "label";
int valueWidth = computeWidth(value);
Map<String, String> values = ImmutableMap.<String, String>builder()
- .put(PARAMETER_ICON_WIDTH_PLUS_MARGIN, valueOf(MARGIN + ICON_WIDTH))
.put(PARAMETER_TOTAL_WIDTH, valueOf(MARGIN * 4 + ICON_WIDTH + labelWidth + valueWidth))
- .put(PARAMETER_LABEL_WIDTH, valueOf(MARGIN * 2 + ICON_WIDTH + labelWidth))
- .put(PARAMETER_LABEL_WIDTH_PLUS_MARGIN, valueOf( MARGIN * 3 + ICON_WIDTH + labelWidth))
- .put(PARAMETER_VALUE_WIDTH, valueOf(MARGIN * 2 + valueWidth))
+ .put(PARAMETER_LABEL_WIDTH, valueOf(labelWidth))
+ .put(PARAMETER_VALUE_WIDTH, valueOf(valueWidth))
+ .put(PARAMETER_LEFT_WIDTH, valueOf(MARGIN * 2 + ICON_WIDTH + labelWidth))
+ .put(PARAMETER_LEFT_WIDTH_PLUS_MARGIN, valueOf(MARGIN * 3 + ICON_WIDTH + labelWidth))
+ .put(PARAMETER_RIGHT_WIDTH, valueOf(MARGIN * 2 + valueWidth))
+ .put(PARAMETER_ICON_WIDTH_PLUS_MARGIN, valueOf(MARGIN + ICON_WIDTH))
.put(PARAMETER_COLOR, backgroundValueColor.getValue())
.put(PARAMETER_LABEL, label)
.put(PARAMETER_VALUE, value)
return strSubstitutor.replace(badgeTemplate);
}
- public String generateQualityGate(Metric.Level level){
+ public String generateQualityGate(Metric.Level level) {
return qualityGateTemplates.get(level);
}
}
private static int computeWidth(String text) {
- return (int) FONT.getStringBounds(text, FONT_RENDER_CONTEXT).getWidth();
+ return text.chars()
+ .mapToObj(i -> (char)i)
+ .mapToInt(c -> {
+ Integer length = CHAR_LENGTH.get(c);
+ checkState(length != null, "Invalid character '%s'", c);
+ return length;
+ })
+ .sum();
}
private String readTemplate(String template) {
.setParam("metric", metric.getKey())
.execute().getInput();
- checkError(response, "Component key 'unknown' not found");
+ checkError(response, "Component not found");
}
@Test
.setParam("metric", metric.getKey())
.execute().getInput();
- checkError(response, String.format("Component '%s' on branch 'unknown' not found", branch.getKey()));
+ checkError(response, "Component not found");
}
@Test
.setParam("metric", metric.getKey())
.execute().getInput();
- checkError(response, String.format("Measure '%s' has not been found for project '%s' and branch 'null'", metric.getKey(), project.getKey()));
+ checkError(response, "Measure has not been found");
}
@Test
.setParam("project", "unknown")
.execute().getInput();
- checkError(response, "Component key 'unknown' not found");
+ checkError(response, "Component not found");
}
@Test
.setParam("branch", "unknown")
.execute().getInput();
- checkError(response, format("Component '%s' on branch 'unknown' not found", branch.getKey()));
+ checkError(response, format("Component not found", branch.getKey()));
}
@Test
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 org.apache.commons.io.IOUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.measures.Metric;
+import org.sonar.db.DbTester;
+import org.sonar.server.tester.UserSessionRule;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.measures.Metric.Level.WARN;
+import static org.sonar.server.badge.ws.SvgGenerator.Color.DEFAULT;
+
+public class SvgGeneratorTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+ @Rule
+ public DbTester db = DbTester.create();
+
+ private MapSettings mapSettings = new MapSettings();
+
+ private SvgGenerator underTest;
+
+ @Test
+ public void generate_badge() {
+ mapSettings.setProperty("sonar.sonarcloud.enabled", false);
+ initSvgGenerator();
+
+ String result = underTest.generateBadge("label", "10", DEFAULT);
+
+ checkBadge(result, "label", "10", DEFAULT);
+ }
+
+ @Test
+ public void generate_quality_gate() {
+ mapSettings.setProperty("sonar.sonarcloud.enabled", false);
+ initSvgGenerator();
+
+ String result = underTest.generateQualityGate(WARN);
+
+ checkQualityGate(result, WARN);
+ }
+
+ @Test
+ public void generate_error() {
+ mapSettings.setProperty("sonar.sonarcloud.enabled", false);
+ initSvgGenerator();
+
+ String result = underTest.generateError("Error");
+
+ assertThat(result).contains("<text", ">Error</text>");
+ }
+
+ @Test
+ public void fail_when_unknown_character() {
+ mapSettings.setProperty("sonar.sonarcloud.enabled", false);
+ initSvgGenerator();
+
+ expectedException.expectMessage("Invalid character 'é'");
+
+ underTest.generateError("Méssage with accent");
+ }
+
+ private void initSvgGenerator() {
+ underTest = new SvgGenerator(mapSettings.asConfig());
+ }
+
+ private void checkBadge(String svg, String expectedLabel, String expectedValue, SvgGenerator.Color expectedColorValue) {
+ assertThat(svg).contains(
+ "<text", expectedLabel + "</text>",
+ "<text", expectedValue + "</text>",
+ "rect fill=\"" + expectedColorValue.getValue() + "\"");
+ }
+
+ private void checkQualityGate(String response, Metric.Level status) {
+ switch (status) {
+ case OK:
+ assertThat(response).isEqualTo(readTemplate("quality_gate_passed.svg"));
+ break;
+ case WARN:
+ assertThat(response).isEqualTo(readTemplate("quality_gate_warn.svg"));
+ break;
+ case ERROR:
+ assertThat(response).isEqualTo(readTemplate("quality_gate_failed.svg"));
+ break;
+ }
+ }
+
+ private String readTemplate(String template) {
+ try {
+ return IOUtils.toString(getClass().getResource("templates/sonarqube/" + template), UTF_8);
+ } catch (IOException e) {
+ throw new IllegalStateException(String.format("Can't read svg template '%s'", template), e);
+ }
+ }
+
+}