--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.exceptions;
+
+import com.google.common.base.MoreObjects;
+import java.util.List;
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.util.Collections.singletonList;
+
+/**
+ * Provided request is not valid within given scope and can not be processed.
+ */
+public class BadConfigurationException extends ServerException {
+ private final String scope;
+ private final transient List<String> errors;
+
+ public BadConfigurationException(String scope, String errorMessage) {
+ super(HTTP_BAD_REQUEST, errorMessage);
+ this.scope = scope;
+ this.errors = singletonList(errorMessage);
+ }
+
+ public String scope() {
+ return scope;
+ }
+
+ public List<String> errors() {
+ return this.errors;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("scope", this.scope)
+ .add("errors", this.errors())
+ .toString();
+ }
+
+}
private final transient List<String> errors;
- private BadRequestException(List<String> errors) {
+ BadRequestException(List<String> errors) {
super(HTTP_BAD_REQUEST, errors.get(0));
this.errors = errors;
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.exceptions;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BadConfigurationExceptionTest {
+
+ @Test
+ public void testMethods() {
+ BadConfigurationException ex = new BadConfigurationException("my-scope", "error");
+
+ assertThat(ex.scope()).isEqualTo("my-scope");
+ assertThat(ex).hasToString("BadConfigurationException{scope=my-scope, errors=[error]}");
+ }
+
+}
if (!multipleAlmFeatureProvider.enabled() && !dbClient.almSettingDao().selectByAlm(dbSession, alm).isEmpty()) {
throw BadRequestException.create("A " + alm + " setting is already defined");
}
-
}
}
- public ProjectDto getProject(DbSession dbSession, String projectKey) {
+ public ProjectDto getProjectAsAdmin(DbSession dbSession, String projectKey) {
+ return getProject(dbSession, projectKey, ADMIN);
+ }
+
+ public ProjectDto getProject(DbSession dbSession, String projectKey, String projectPermission) {
ProjectDto project = componentFinder.getProjectByKey(dbSession, projectKey);
- userSession.checkProjectPermission(ADMIN, project);
+ userSession.checkProjectPermission(projectPermission, project);
return project;
}
import javax.annotation.Nullable;
import org.apache.catalina.connector.ClientAbortException;
import org.picocontainer.Startable;
+import org.sonar.api.impl.ws.ValidatingRequest;
import org.sonar.api.server.ServerSide;
import org.sonar.api.server.ws.LocalConnector;
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.impl.ws.ValidatingRequest;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.BadConfigurationException;
import org.sonar.server.exceptions.ServerException;
import org.sonarqube.ws.MediaTypes;
import static org.apache.commons.lang.StringUtils.substring;
import static org.apache.commons.lang.StringUtils.substringAfterLast;
import static org.apache.commons.lang.StringUtils.substringBeforeLast;
+import static org.sonar.server.exceptions.NotFoundException.checkFound;
import static org.sonar.server.ws.RequestVerifier.verifyRequest;
import static org.sonar.server.ws.ServletRequest.SUPPORTED_MEDIA_TYPES_BY_URL_SUFFIX;
-import static org.sonar.server.exceptions.NotFoundException.checkFound;
/**
* @since 4.2
action.handler().handle(request, response);
} catch (IllegalArgumentException e) {
sendErrors(request, response, e, 400, singletonList(e.getMessage()));
+ } catch (BadConfigurationException e) {
+ sendErrors(request, response, e, 400, e.errors(), e.scope());
} catch (BadRequestException e) {
sendErrors(request, response, e, 400, e.errors());
} catch (ServerException e) {
}
private static void sendErrors(Request request, Response response, Exception exception, int status, List<String> errors) {
+ sendErrors(request, response, exception, status, errors, null);
+ }
+
+ private static void sendErrors(Request request, Response response, Exception exception, int status, List<String> errors, @Nullable String scope) {
if (isRequestAbortedByClient(exception)) {
// do not pollute logs. We can't do anything -> use DEBUG level
// see org.sonar.server.ws.ServletResponse#output()
stream.setMediaType(MediaTypes.JSON);
try (JsonWriter json = JsonWriter.of(new OutputStreamWriter(stream.output(), StandardCharsets.UTF_8))) {
json.beginObject();
+ writeScope(scope, json);
writeErrors(json, errors);
json.endObject();
} catch (Exception e) {
}
}
+ private static void writeScope(@Nullable String scope, JsonWriter json) {
+ if (scope != null) {
+ json.prop("scope", scope);
+ }
+ }
+
private static boolean isRequestAbortedByClient(Exception exception) {
return Throwables.getCausalChain(exception).stream().anyMatch(t -> t instanceof ClientAbortException);
}
import org.apache.catalina.connector.ClientAbortException;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.server.exceptions.BadConfigurationException;
import org.sonar.server.exceptions.BadRequestException;
import org.sonarqube.ws.MediaTypes;
@Rule
public LogTester logTester = new LogTester();
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
@Test
public void load_ws_definitions_at_startup() {
WebServiceEngine underTest = new WebServiceEngine(new WebService[] {
assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
}
+ @Test
+ public void return_400_on_BadConfigurationException_with_single_message_and_scope() {
+ Request request = new TestRequest().setPath("api/foo");
+
+ DumbResponse response = run(request, newWs("api/foo", a -> a.setHandler((req, resp) -> {
+ throw new BadConfigurationException("PROJECT", "Bad request !");
+ })));
+
+ assertThat(response.stream().outputAsString()).isEqualTo(
+ "{\"scope\":\"PROJECT\",\"errors\":[{\"msg\":\"Bad request !\"}]}");
+ assertThat(response.stream().status()).isEqualTo(400);
+ assertThat(response.stream().mediaType()).isEqualTo(MediaTypes.JSON);
+ assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
+ }
+
@Test
public void return_error_message_containing_character_percent() {
Request request = new TestRequest().setPath("api/foo");