import org.sonar.server.platform.ws.UpgradesAction;
import org.sonar.server.plugins.PluginDownloader;
import org.sonar.server.plugins.ServerExtensionInstaller;
-import org.sonar.server.plugins.UpdateCenterClient;
-import org.sonar.server.plugins.UpdateCenterMatrixFactory;
import org.sonar.server.plugins.privileged.PrivilegedPluginsBootstraper;
import org.sonar.server.plugins.privileged.PrivilegedPluginsStopper;
import org.sonar.server.plugins.ws.AvailableAction;
import org.sonar.server.ui.ws.GlobalNavigationAction;
import org.sonar.server.ui.ws.NavigationWs;
import org.sonar.server.ui.ws.SettingsNavigationAction;
-import org.sonar.server.updatecenter.ws.UpdateCenterWs;
+import org.sonar.server.updatecenter.UpdateCenterModule;
import org.sonar.server.user.DefaultUserFinder;
import org.sonar.server.user.DefaultUserService;
import org.sonar.server.user.DeprecatedUserFinder;
IssueTagCloudWidget.class,
// update center
- UpdateCenterClient.class,
- UpdateCenterMatrixFactory.class,
- UpdateCenterWs.class,
+ UpdateCenterModule.class,
// quality profile
ActiveRuleIndexer.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.updatecenter;
+
+import org.sonar.core.platform.Module;
+import org.sonar.server.plugins.UpdateCenterClient;
+import org.sonar.server.plugins.UpdateCenterMatrixFactory;
+import org.sonar.server.updatecenter.ws.UpdateCenterWs;
+import org.sonar.server.updatecenter.ws.UploadAction;
+
+public class UpdateCenterModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ UpdateCenterClient.class,
+ UpdateCenterMatrixFactory.class,
+ UploadAction.class,
+ UpdateCenterWs.class);
+ }
+}
public class UpdateCenterWs implements WebService {
+ private final UpdateCenterWsAction[] actions;
+
+ public UpdateCenterWs(UpdateCenterWsAction... actions) {
+ this.actions = actions;
+ }
+
@Override
public void define(Context context) {
NewController controller = context.createController("api/updatecenter")
.setSince("2.10");
defineInstalledPluginsAction(controller);
+ for (UpdateCenterWsAction action : actions) {
+ action.define(controller);
+ }
controller.done();
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.updatecenter.ws;
+
+import org.sonar.server.ws.WsAction;
+
+interface UpdateCenterWsAction extends WsAction {
+ // Marker interface
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.updatecenter.ws;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.apache.commons.io.IOUtils.closeQuietly;
+import static org.sonar.api.server.ws.Request.Part;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.file.Files;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.user.UserSession;
+
+public class UploadAction implements UpdateCenterWsAction {
+
+ public static final String PARAM_FILE = "file";
+
+ private final UserSession userSession;
+ private final File downloadDir;
+
+ public UploadAction(UserSession userSession, DefaultServerFileSystem fileSystem) {
+ this.userSession = userSession;
+ this.downloadDir = fileSystem.getDownloadedPluginsDir();
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction("upload")
+ .setDescription("Upload a plugin.<br /> Requires 'Administer System' permission.")
+ .setSince("6.0")
+ .setPost(true)
+ .setInternal(true)
+ .setHandler(this);
+
+ action.createParam(PARAM_FILE)
+ .setDescription("The jar file of the plugin to install")
+ .setRequired(true);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.checkPermission(GlobalPermissions.SYSTEM_ADMIN);
+
+ Part part = request.mandatoryParamAsPart(PARAM_FILE);
+ String fileName = part.getFileName();
+ checkArgument(fileName.endsWith(".jar"), "Only jar file is allowed");
+ InputStream inputStream = part.getInputStream();
+ try {
+ File destPlugin = new File(downloadDir, fileName);
+ Files.copy(inputStream, destPlugin.toPath(), REPLACE_EXISTING);
+ response.noContent();
+ } finally {
+ closeQuietly(inputStream);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.updatecenter.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
return new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8));
}
+ @Override
+ protected Part readPart(String key) {
+ throw new UnsupportedOperationException("reading part is not supported yet by local WS calls");
+ }
+
@Override
public boolean hasParam(String key) {
return localRequest.hasParam(key);
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.Part;
+import org.sonar.api.server.ws.internal.PartImpl;
import org.sonar.api.server.ws.internal.ValidatingRequest;
import org.sonarqube.ws.MediaTypes;
@Override
protected InputStream readInputStreamParam(String key) {
+ Part part = readPart(key);
+ return (part == null) ? null : part.getInputStream();
+ }
+
+ @Override
+ @CheckForNull
+ public Part readPart(String key) {
try {
if (!isMultipartContent()) {
return null;
}
- Part part = source.getPart(key);
+ javax.servlet.http.Part part = source.getPart(key);
if (part == null || part.getSize() == 0) {
return null;
}
- return part.getInputStream();
+ return new PartImpl(part.getInputStream(), part.getSubmittedFileName());
} catch (Exception e) {
throw new IllegalStateException("Can't read file part", e);
}
public String getPath() {
return source.getRequestURI().replaceFirst(source.getContextPath(), "");
}
+
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.updatecenter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+public class UpdateCenterModuleTest {
+ @Test
+ public void verify_count_of_added_components() {
+ ComponentContainer container = new ComponentContainer();
+ new UpdateCenterModule().configure(container);
+ assertThat(container.size()).isEqualTo(2 + 4);
+ }
+}
*/
package org.sonar.server.updatecenter.ws;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;
+import org.sonar.server.platform.DefaultServerFileSystem;
import org.sonar.server.ws.WsTester;
-import static org.assertj.core.api.Assertions.assertThat;
-
public class UpdateCenterWsTest {
WsTester tester;
@Before
public void setUp() {
- tester = new WsTester(new UpdateCenterWs());
+ tester = new WsTester(new UpdateCenterWs(
+ new UploadAction(null, mock(DefaultServerFileSystem.class))));
}
@Test
assertThat(controller).isNotNull();
assertThat(controller.since()).isEqualTo("2.10");
assertThat(controller.description()).isNotEmpty();
- assertThat(controller.actions()).hasSize(1);
+ assertThat(controller.actions()).hasSize(2);
}
@Test
assertThat(action.params()).hasSize(1);
}
+ @Test
+ public void define_upload_action() throws Exception {
+ WebService.Controller controller = tester.controller("api/updatecenter");
+
+ WebService.Action action = controller.action("upload");
+ assertThat(action).isNotNull();
+ assertThat(action.handler()).isNotNull();
+ assertThat(action.isInternal()).isTrue();
+ assertThat(action.isPost()).isTrue();
+ assertThat(action.params()).hasSize(1);
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.updatecenter.ws;
+
+import static java.nio.file.Files.newInputStream;
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.test.ExceptionCauseMatcher.hasType;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.channels.ClosedChannelException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+public class UploadActionTest {
+
+ static final String PLUGIN_NAME = "plugin.jar";
+
+ @Rule
+ public TemporaryFolder folder = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ DefaultServerFileSystem fileSystem = mock(DefaultServerFileSystem.class);
+ File pluginDirectory;
+
+ File plugin = new File(getClass().getResource("UploadActionTest/plugin.jar").getFile());
+ WsActionTester wsTester;
+
+ @Before
+ public void setUp() throws Exception {
+ pluginDirectory = folder.newFolder();
+ when(fileSystem.getDownloadedPluginsDir()).thenReturn(pluginDirectory);
+ wsTester = new WsActionTester(new UploadAction(userSession, fileSystem));
+ }
+
+ @Test
+ public void upload_plugin() throws Exception {
+ setSystemAdminUser();
+
+ TestResponse response = call(newInputStream(plugin.toPath()), PLUGIN_NAME);
+
+ assertThat(response.getStatus()).isEqualTo(204);
+ assertPluginIsUploaded(PLUGIN_NAME);
+ }
+
+ @Test
+ public void erase_existing_plugin_if_already_exists() throws Exception {
+ setSystemAdminUser();
+
+ File plugin1 = new File(getClass().getResource("UploadActionTest/plugin.jar").getFile());
+ call(newInputStream(plugin1.toPath()), PLUGIN_NAME);
+
+ File plugin2 = new File(getClass().getResource("UploadActionTest/anotherPlugin.jar").getFile());
+ call(newInputStream(plugin2.toPath()), PLUGIN_NAME);
+
+ File result = new File(pluginDirectory, PLUGIN_NAME);
+ assertThat(result.exists()).isTrue();
+ assertThat(result.length()).isNotEqualTo(plugin1.length()).isEqualTo(plugin2.length());
+ }
+
+ @Test
+ public void fail_when_plugin_extension_is_not_jar() throws Exception {
+ setSystemAdminUser();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Only jar file is allowed");
+ call(newInputStream(plugin.toPath()), "plugin.zip");
+ }
+
+ @Test
+ public void fail_when_no_files_param() throws Exception {
+ setSystemAdminUser();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'file' parameter is missing");
+ wsTester.newRequest().execute();
+ }
+
+ @Test
+ public void input_stream_should_be_closed() throws Exception {
+ setSystemAdminUser();
+
+ InputStream inputStream = newInputStream(plugin.toPath());
+ call(inputStream, PLUGIN_NAME);
+
+ // As the same InputStream is used, it will fail as it should have been called during the first execution of the WS
+ expectedException.expectCause(hasType(ClosedChannelException.class));
+ call(inputStream, PLUGIN_NAME);
+ }
+
+ @Test
+ public void fail_if_not_system_admin() throws Exception {
+ userSession.login().setGlobalPermissions(PROVISIONING);
+
+ expectedException.expect(ForbiddenException.class);
+ call(newInputStream(plugin.toPath()), PLUGIN_NAME);
+ }
+
+ private TestResponse call(InputStream inputStream, String fileName) {
+ return wsTester.newRequest()
+ .setPart("file", inputStream, fileName)
+ .execute();
+ }
+
+ private void setSystemAdminUser() {
+ userSession.login().setGlobalPermissions(SYSTEM_ADMIN);
+ }
+
+ private void assertPluginIsUploaded(String pluginName) {
+ File result = new File(pluginDirectory, pluginName);
+ assertThat(result.exists()).isTrue();
+ }
+
+}
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Throwables;
+import com.google.common.collect.Maps;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.IOUtils;
+import org.sonar.api.server.ws.internal.PartImpl;
import org.sonar.api.server.ws.internal.ValidatingRequest;
public class TestRequest extends ValidatingRequest {
private final Map<String, String> params = new HashMap<>();
+ private final Map<String, Part> parts = Maps.newHashMap();
private String method = "GET";
private String mimeType = "application/octet-stream";
private String path;
return IOUtils.toInputStream(value);
}
+ @Override
+ protected Part readPart(String key) {
+ return parts.get(key);
+ }
+
+ public TestRequest setPart(String key, InputStream input, String fileName) {
+ parts.put(key, new PartImpl(input, fileName));
+ return this;
+ }
+
@Override
public String method() {
return method;
public String getMediaType() {
return dumbResponse.stream().mediaType();
}
+
+ public int getStatus() {
+ return dumbResponse.stream().status();
+ }
}
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import com.google.common.collect.Maps;
import java.io.IOException;
-import java.io.InputStream;
import java.util.Locale;
-import java.util.Map;
-import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
@Test
public void execute_request() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/health");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void execute_request_when_path_does_not_begin_with_slash() {
- ValidatingRequest request = new SimpleRequest("GET", "api/system/health");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void execute_request_with_action_suffix() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/health");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void bad_request_if_action_suffix_is_not_supported() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/health.bat");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/health.bat");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void no_content() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/alive");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/alive");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void bad_controller() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/xxx/health");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/xxx/health");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void bad_action() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/xxx");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/xxx");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void method_get_not_allowed() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/ping");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/ping");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void method_post_required() {
- ValidatingRequest request = new SimpleRequest("POST", "/api/system/ping");
+ ValidatingRequest request = new TestRequest().setMethod("POST").setPath("/api/system/ping");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void unknown_parameter_is_set() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/fail_with_undeclared_parameter").setParam("unknown", "Unknown");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_undeclared_parameter").setParam("unknown", "Unknown");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void required_parameter_is_not_set() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/print");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void optional_parameter_is_not_set() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/print").setParam("message", "Hello World");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print").setParam("message", "Hello World");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void optional_parameter_is_set() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/print")
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print")
.setParam("message", "Hello World")
.setParam("author", "Marcel");
DumbResponse response = new DumbResponse();
@Test
public void param_value_is_in_possible_values() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/print")
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print")
.setParam("message", "Hello World")
.setParam("format", "json");
DumbResponse response = new DumbResponse();
@Test
public void param_value_is_not_in_possible_values() {
- ValidatingRequest request = new SimpleRequest("GET", "api/system/print")
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/print")
.setParam("message", "Hello World")
.setParam("format", "html");
DumbResponse response = new DumbResponse();
@Test
public void internal_error() {
- ValidatingRequest request = new SimpleRequest("GET", "/api/system/fail");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
@Test
public void bad_request_with_i18n_message() {
userSessionRule.setLocale(Locale.ENGLISH);
- ValidatingRequest request = new SimpleRequest("GET", "api/system/fail_with_i18n_message").setParam("count", "3");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_i18n_message").setParam("count", "3");
DumbResponse response = new DumbResponse();
when(i18n.message(Locale.ENGLISH, "bad.request.reason", "bad.request.reason", 0)).thenReturn("reason #0");
@Test
public void bad_request_with_multiple_messages() {
- ValidatingRequest request = new SimpleRequest("GET", "api/system/fail_with_multiple_messages").setParam("count", "3");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_multiple_messages").setParam("count", "3");
DumbResponse response = new DumbResponse();
underTest.execute(request, response);
public void bad_request_with_multiple_i18n_messages() {
userSessionRule.setLocale(Locale.ENGLISH);
- ValidatingRequest request = new SimpleRequest("GET", "api/system/fail_with_multiple_i18n_messages").setParam("count", "3");
+ ValidatingRequest request = new TestRequest().setMethod("GET").setPath("/api/system/fail_with_multiple_i18n_messages").setParam("count", "3");
DumbResponse response = new DumbResponse();
when(i18n.message(Locale.ENGLISH, "bad.request.reason", "bad.request.reason", 0)).thenReturn("reason #0");
when(i18n.message(Locale.ENGLISH, "bad.request.reason", "bad.request.reason", 1)).thenReturn("reason #1");
assertThat(response.getHeader(name)).isEqualTo(value);
}
- private static class SimpleRequest extends ValidatingRequest {
- private final String method;
- private String path;
- private Map<String, String> params = Maps.newHashMap();
-
- private SimpleRequest(String method, String path) {
- this.method = method;
- this.path = path;
- }
-
- @Override
- public String method() {
- return method;
- }
-
- @Override
- public String getMediaType() {
- return MediaTypes.JSON;
- }
-
- @Override
- public boolean hasParam(String key) {
- return params.keySet().contains(key);
- }
-
- @Override
- public String getPath() {
- return path;
- }
-
- @Override
- protected String readParam(String key) {
- return params.get(key);
- }
-
- @Override
- protected InputStream readInputStreamParam(String key) {
- String param = readParam(key);
-
- return param == null ? null : IOUtils.toInputStream(param);
- }
-
- public SimpleRequest setParams(Map<String, String> m) {
- this.params = m;
- return this;
- }
-
- public SimpleRequest setParam(String key, @Nullable String value) {
- if (value != null) {
- params.put(key, value);
- }
- return this;
- }
-
- }
-
static class SystemWs implements WebService {
@Override
public void define(Context context) {
import org.apache.commons.io.IOUtils;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.internal.PartImpl;
import org.sonar.api.server.ws.internal.ValidatingRequest;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.utils.text.XmlWriter;
private String path;
private Map<String, String> params = Maps.newHashMap();
+ private final Map<String, Part> parts = Maps.newHashMap();
private TestRequest(String method) {
this.method = method;
return param == null ? null : IOUtils.toInputStream(param);
}
+ @Override
+ protected Part readPart(String key) {
+ return parts.get(key);
+ }
+
+ public TestRequest setPart(String key, InputStream input, String fileName) {
+ parts.put(key, new PartImpl(input, fileName));
+ return this;
+ }
+
public Result execute() throws Exception {
TestResponse response = new TestResponse();
verifyRequest(action(), this);
*/
package org.sonar.api.server.ws;
+import static com.google.common.base.Preconditions.checkArgument;
+
import com.google.common.annotations.Beta;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
@CheckForNull
public abstract InputStream paramAsInputStream(String key);
+ @CheckForNull
+ public abstract Part paramAsPart(String key);
+
+ public Part mandatoryParamAsPart(String key) {
+ Part part = paramAsPart(key);
+ checkArgument(part != null, "The '%s' parameter is missing", key);
+ return part;
+ }
+
/**
* @deprecated to be dropped in 4.4. Default values are declared in ws metadata
*/
* @since 6.0
*/
public abstract String getPath();
+
+ /**
+ * @since 6.0
+ */
+ public interface Part {
+ InputStream getInputStream();
+
+ String getFileName();
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.api.server.ws.internal;
+
+import java.io.InputStream;
+import org.sonar.api.server.ws.Request;
+
+public class PartImpl implements Request.Part {
+
+ private final InputStream inputStream;
+ private final String fileName;
+
+ public PartImpl(InputStream inputStream, String fileName) {
+ this.inputStream = inputStream;
+ this.fileName = fileName;
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+
+ @Override
+ public String getFileName() {
+ return fileName;
+ }
+}
public class SimpleGetRequest extends Request {
private final Map<String, String> params = Maps.newHashMap();
+ private final Map<String, Part> parts = Maps.newHashMap();
private String mediaType = "application/json";
private String path;
return params;
}
+ @Override
+ public Part paramAsPart(String key) {
+ return parts.get(key);
+ }
+
+ public SimpleGetRequest setPart(String key, InputStream input, String fileName) {
+ parts.put(key, new PartImpl(input, fileName));
+ return this;
+ }
+
@Override
public LocalConnector localConnector() {
throw new UnsupportedOperationException();
this.path = path;
return this;
}
+
}
*/
package org.sonar.api.server.ws.internal;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.log.Loggers;
-import static com.google.common.base.Preconditions.checkNotNull;
-
/**
* @since 4.2
*/
return readInputStreamParam(key);
}
+ @Override
+ @CheckForNull
+ public Part paramAsPart(String key) {
+ return readPart(key);
+ }
+
@CheckForNull
private String param(String key, boolean validateValue) {
WebService.Param definition = action.param(key);
@CheckForNull
protected abstract InputStream readInputStreamParam(String key);
+ @CheckForNull
+ protected abstract Part readPart(String key);
+
private static void validate(String value, WebService.Param definition) {
Set<String> possibleValues = definition.possibleValues();
if (possibleValues != null && !possibleValues.contains(value)) {
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.server.ws.internal.PartImpl;
import org.sonar.api.server.ws.internal.ValidatingRequest;
import org.sonar.api.utils.DateUtils;
assertThat(IOUtils.toString(underTest.setParam("a_string", "foo").paramAsInputStream("a_string"))).isEqualTo("foo");
}
+ @Test
+ public void param_as_part() throws Exception {
+ InputStream inputStream = mock(InputStream.class);
+ underTest.setPart("key", inputStream, "filename");
+
+ Request.Part part = underTest.paramAsPart("key");
+ assertThat(part.getInputStream()).isEqualTo(inputStream);
+ assertThat(part.getFileName()).isEqualTo("filename");
+
+ assertThat(underTest.paramAsPart("unknown")).isNull();
+ }
+
+ @Test
+ public void mandatory_param_as_part() throws Exception {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'required_param' parameter is missing");
+
+ underTest.mandatoryParamAsPart("required_param");
+ }
+
private static class FakeRequest extends ValidatingRequest {
private final Map<String, String> params = Maps.newHashMap();
+ private final Map<String, Part> parts = Maps.newHashMap();
@Override
public String method() {
return param == null ? null : IOUtils.toInputStream(param);
}
+
+ @Override
+ protected Part readPart(String key) {
+ return parts.get(key);
+ }
+
+ public FakeRequest setPart(String key, InputStream input, String fileName) {
+ parts.put(key, new PartImpl(input, fileName));
+ return this;
+ }
}
private static class FakeWs implements WebService {
*/
package org.sonar.api.server.ws.internal;
-import org.junit.Test;
-
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import java.io.InputStream;
+import org.junit.Test;
+import org.sonar.api.server.ws.Request;
public class SimpleGetRequestTest {
+ SimpleGetRequest underTest = new SimpleGetRequest();
+
@Test
public void method() {
- SimpleGetRequest request = new SimpleGetRequest();
- assertThat(request.method()).isEqualTo("GET");
+ assertThat(underTest.method()).isEqualTo("GET");
- request.setParam("foo", "bar");
- assertThat(request.param("foo")).isEqualTo("bar");
- assertThat(request.param("unknown")).isNull();
+ underTest.setParam("foo", "bar");
+ assertThat(underTest.param("foo")).isEqualTo("bar");
+ assertThat(underTest.param("unknown")).isNull();
}
@Test
public void has_param() {
- SimpleGetRequest request = new SimpleGetRequest();
- assertThat(request.method()).isEqualTo("GET");
+ assertThat(underTest.method()).isEqualTo("GET");
+
+ underTest.setParam("foo", "bar");
+ assertThat(underTest.hasParam("foo")).isTrue();
+ assertThat(underTest.hasParam("unknown")).isFalse();
+ }
+
+ @Test
+ public void get_part() throws Exception {
+ InputStream inputStream = mock(InputStream.class);
+ underTest.setPart("key", inputStream, "filename");
+
+ Request.Part part = underTest.paramAsPart("key");
+ assertThat(part.getInputStream()).isEqualTo(inputStream);
+ assertThat(part.getFileName()).isEqualTo("filename");
- request.setParam("foo", "bar");
- assertThat(request.hasParam("foo")).isTrue();
- assertThat(request.hasParam("unknown")).isFalse();
+ assertThat(underTest.paramAsPart("unknown")).isNull();
}
}