dependencies { | dependencies { | ||||
// please keep list ordered | // please keep list ordered | ||||
compile 'com.google.guava:guava' | |||||
compile 'com.google.protobuf:protobuf-java' | compile 'com.google.protobuf:protobuf-java' | ||||
compile 'com.squareup.okhttp3:okhttp' | compile 'com.squareup.okhttp3:okhttp' | ||||
compile 'commons-io:commons-io' | compile 'commons-io:commons-io' |
*/ | */ | ||||
package org.sonarqube.ws; | package org.sonarqube.ws; | ||||
import com.google.common.base.Strings; | |||||
import com.google.common.collect.ImmutableMap; | |||||
import java.util.HashMap; | |||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.commons.io.FilenameUtils; | import org.apache.commons.io.FilenameUtils; | ||||
import static org.sonarqube.ws.WsUtils.isNullOrEmpty; | |||||
/** | /** | ||||
* @since 5.3 | * @since 5.3 | ||||
*/ | */ | ||||
public static final String DEFAULT = "application/octet-stream"; | public static final String DEFAULT = "application/octet-stream"; | ||||
public static final String SVG = "image/svg+xml"; | public static final String SVG = "image/svg+xml"; | ||||
private static final Map<String, String> MAP = new ImmutableMap.Builder<String, String>() | |||||
.put("js", JAVASCRIPT) | |||||
.put("json", JSON) | |||||
.put("zip", ZIP) | |||||
.put("tgz", "application/tgz") | |||||
.put("ps", "application/postscript") | |||||
.put("jnlp", "application/jnlp") | |||||
.put("jar", "application/java-archive") | |||||
.put("xls", "application/vnd.ms-excel") | |||||
.put("ppt", "application/vnd.ms-powerpoint") | |||||
.put("tar", "application/x-tar") | |||||
.put("xml", XML) | |||||
.put("dtd", "application/xml-dtd") | |||||
.put("xslt", "application/xslt+xml") | |||||
.put("bmp", "image/bmp") | |||||
.put("gif", "image/gif") | |||||
.put("jpg", "image/jpeg") | |||||
.put("jpeg", "image/jpeg") | |||||
.put("tiff", "image/tiff") | |||||
.put("png", "image/png") | |||||
.put("svg", SVG) | |||||
.put("ico", "image/x-icon") | |||||
.put("txt", TXT) | |||||
.put("csv", "text/csv") | |||||
.put("properties", TXT) | |||||
.put("rtf", "text/rtf") | |||||
.put("html", HTML) | |||||
.put("css", "text/css") | |||||
.put("tsv", "text/tab-separated-values") | |||||
.build(); | |||||
private static final Map<String, String> MAP = new HashMap<>(); | |||||
static { | |||||
MAP.put("js", JAVASCRIPT); | |||||
MAP.put("json", JSON); | |||||
MAP.put("zip", ZIP); | |||||
MAP.put("tgz", "application/tgz"); | |||||
MAP.put("ps", "application/postscript"); | |||||
MAP.put("jnlp", "application/jnlp"); | |||||
MAP.put("jar", "application/java-archive"); | |||||
MAP.put("xls", "application/vnd.ms-excel"); | |||||
MAP.put("ppt", "application/vnd.ms-powerpoint"); | |||||
MAP.put("tar", "application/x-tar"); | |||||
MAP.put("xml", XML); | |||||
MAP.put("dtd", "application/xml-dtd"); | |||||
MAP.put("xslt", "application/xslt+xml"); | |||||
MAP.put("bmp", "image/bmp"); | |||||
MAP.put("gif", "image/gif"); | |||||
MAP.put("jpg", "image/jpeg"); | |||||
MAP.put("jpeg", "image/jpeg"); | |||||
MAP.put("tiff", "image/tiff"); | |||||
MAP.put("png", "image/png"); | |||||
MAP.put("svg", SVG); | |||||
MAP.put("ico", "image/x-icon"); | |||||
MAP.put("txt", TXT); | |||||
MAP.put("csv", "text/csv"); | |||||
MAP.put("properties", TXT); | |||||
MAP.put("rtf", "text/rtf"); | |||||
MAP.put("html", HTML); | |||||
MAP.put("css", "text/css"); | |||||
MAP.put("tsv", "text/tab-separated-values"); | |||||
} | |||||
private MediaTypes() { | private MediaTypes() { | ||||
// only static methods | // only static methods | ||||
public static String getByFilename(String filename) { | public static String getByFilename(String filename) { | ||||
String extension = FilenameUtils.getExtension(filename); | String extension = FilenameUtils.getExtension(filename); | ||||
String mime = null; | String mime = null; | ||||
if (!Strings.isNullOrEmpty(extension)) { | |||||
if (!isNullOrEmpty(extension)) { | |||||
mime = MAP.get(extension.toLowerCase(Locale.ENGLISH)); | mime = MAP.get(extension.toLowerCase(Locale.ENGLISH)); | ||||
} | } | ||||
return mime != null ? mime : DEFAULT; | return mime != null ? mime : DEFAULT; |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 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.sonarqube.ws; | |||||
import javax.annotation.Nullable; | |||||
import static java.lang.String.format; | |||||
public class WsUtils { | |||||
private WsUtils() { | |||||
// Only static methods here | |||||
} | |||||
public static void checkArgument(boolean expression) { | |||||
if (!expression) { | |||||
throw new IllegalArgumentException(); | |||||
} | |||||
} | |||||
public static void checkArgument(boolean expression, String message, Object... args) { | |||||
if (!expression) { | |||||
throw new IllegalArgumentException(format(message, args)); | |||||
} | |||||
} | |||||
public static boolean isNullOrEmpty(@Nullable String string) { | |||||
return string == null || string.length() == 0; | |||||
} | |||||
public static String nullToEmpty(@Nullable String string) { | |||||
return string == null ? "" : string; | |||||
} | |||||
} |
*/ | */ | ||||
package org.sonarqube.ws.client; | package org.sonarqube.ws.client; | ||||
import com.google.common.collect.LinkedListMultimap; | |||||
import com.google.common.collect.ListMultimap; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonarqube.ws.MediaTypes; | import org.sonarqube.ws.MediaTypes; | ||||
import static com.google.common.base.Preconditions.checkArgument; | |||||
import static com.google.common.base.Strings.isNullOrEmpty; | |||||
import static java.util.Collections.singletonList; | |||||
import static java.util.Collections.emptyList; | |||||
import static java.util.Collections.unmodifiableSet; | import static java.util.Collections.unmodifiableSet; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
import static org.sonarqube.ws.WsUtils.checkArgument; | |||||
import static org.sonarqube.ws.WsUtils.isNullOrEmpty; | |||||
abstract class BaseRequest<SELF extends BaseRequest> implements WsRequest { | abstract class BaseRequest<SELF extends BaseRequest> implements WsRequest { | ||||
private static class DefaultParameters implements Parameters { | private static class DefaultParameters implements Parameters { | ||||
// preserve insertion order | // preserve insertion order | ||||
private final ListMultimap<String, String> keyValues = LinkedListMultimap.create(); | |||||
private final Map<String, List<String>> keyValues = new LinkedHashMap<>(); | |||||
@Override | @Override | ||||
@CheckForNull | @CheckForNull | ||||
@Override | @Override | ||||
public List<String> getValues(String key) { | public List<String> getValues(String key) { | ||||
return keyValues.get(key); | |||||
return keyValues.containsKey(key) ? keyValues.get(key) : emptyList(); | |||||
} | } | ||||
@Override | @Override | ||||
checkArgument(!isNullOrEmpty(key)); | checkArgument(!isNullOrEmpty(key)); | ||||
checkArgument(value != null); | checkArgument(value != null); | ||||
keyValues.putAll(key, singletonList(value)); | |||||
keyValues.computeIfAbsent(key, k -> new ArrayList<>()).add(value); | |||||
return this; | return this; | ||||
} | } | ||||
checkArgument(!isNullOrEmpty(key)); | checkArgument(!isNullOrEmpty(key)); | ||||
checkArgument(values != null && !values.isEmpty()); | checkArgument(values != null && !values.isEmpty()); | ||||
this.keyValues.putAll(key, values.stream().map(Object::toString).filter(Objects::nonNull).collect(Collectors.toList())); | |||||
keyValues.computeIfAbsent(key, k -> new ArrayList<>()).addAll(values.stream().map(Object::toString).filter(Objects::nonNull).collect(Collectors.toList())); | |||||
return this; | return this; | ||||
} | } | ||||
} | } |
import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||
import org.sonarqube.ws.MediaTypes; | import org.sonarqube.ws.MediaTypes; | ||||
import static com.google.common.base.Preconditions.checkArgument; | |||||
import static com.google.common.base.Strings.isNullOrEmpty; | |||||
import static org.sonarqube.ws.WsUtils.checkArgument; | |||||
import static org.sonarqube.ws.WsUtils.isNullOrEmpty; | |||||
public abstract class BaseService { | public abstract class BaseService { | ||||
protected final String controller; | protected final String controller; | ||||
public BaseService(WsConnector wsConnector, String controllerPath) { | public BaseService(WsConnector wsConnector, String controllerPath) { | ||||
checkArgument(!isNullOrEmpty(controllerPath)); | |||||
checkArgument(!isNullOrEmpty(controllerPath), "Controller path cannot be empty"); | |||||
this.wsConnector = wsConnector; | this.wsConnector = wsConnector; | ||||
this.controller = controllerPath; | this.controller = controllerPath; | ||||
} | } |
import okhttp3.RequestBody; | import okhttp3.RequestBody; | ||||
import okhttp3.Response; | import okhttp3.Response; | ||||
import static com.google.common.base.Preconditions.checkArgument; | |||||
import static com.google.common.base.Strings.isNullOrEmpty; | |||||
import static com.google.common.base.Strings.nullToEmpty; | |||||
import static java.lang.String.format; | import static java.lang.String.format; | ||||
import static java.net.HttpURLConnection.HTTP_MOVED_PERM; | import static java.net.HttpURLConnection.HTTP_MOVED_PERM; | ||||
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; | import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; | ||||
import static java.nio.charset.StandardCharsets.UTF_8; | import static java.nio.charset.StandardCharsets.UTF_8; | ||||
import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT; | import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT; | ||||
import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT; | import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT; | ||||
import static org.sonarqube.ws.WsUtils.checkArgument; | |||||
import static org.sonarqube.ws.WsUtils.isNullOrEmpty; | |||||
import static org.sonarqube.ws.WsUtils.nullToEmpty; | |||||
/** | /** | ||||
* Connect to any SonarQube server available through HTTP or HTTPS. | * Connect to any SonarQube server available through HTTP or HTTPS. |
*/ | */ | ||||
package org.sonarqube.ws.client; | package org.sonarqube.ws.client; | ||||
import com.google.common.annotations.VisibleForTesting; | |||||
import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||
this.localConnector = localConnector; | this.localConnector = localConnector; | ||||
} | } | ||||
@VisibleForTesting | |||||
LocalConnector localConnector() { | LocalConnector localConnector() { | ||||
return localConnector; | return localConnector; | ||||
} | } |
*/ | */ | ||||
package org.sonarqube.ws.client; | package org.sonarqube.ws.client; | ||||
import com.google.common.base.Throwables; | |||||
import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
try { | try { | ||||
return setContent(IOUtils.toByteArray(is)); | return setContent(IOUtils.toByteArray(is)); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw Throwables.propagate(e); | |||||
throw new IllegalArgumentException(e); | |||||
} | } | ||||
} | } | ||||
import okhttp3.Request; | import okhttp3.Request; | ||||
import okhttp3.Response; | import okhttp3.Response; | ||||
import static com.google.common.base.Strings.nullToEmpty; | |||||
import static java.nio.charset.StandardCharsets.UTF_8; | import static java.nio.charset.StandardCharsets.UTF_8; | ||||
import static java.util.Arrays.asList; | import static java.util.Arrays.asList; | ||||
import static org.sonarqube.ws.WsUtils.nullToEmpty; | |||||
/** | /** | ||||
* Helper to build an instance of {@link okhttp3.OkHttpClient} that | * Helper to build an instance of {@link okhttp3.OkHttpClient} that |
*/ | */ | ||||
package org.sonarqube.ws.client; | package org.sonarqube.ws.client; | ||||
import com.google.common.collect.ImmutableList; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.List; | import java.util.List; | ||||
import org.assertj.core.data.MapEntry; | import org.assertj.core.data.MapEntry; | ||||
import org.junit.rules.ExpectedException; | import org.junit.rules.ExpectedException; | ||||
import org.sonarqube.ws.MediaTypes; | import org.sonarqube.ws.MediaTypes; | ||||
import static com.google.common.collect.Lists.newArrayList; | |||||
import static java.util.Arrays.asList; | import static java.util.Arrays.asList; | ||||
import static java.util.Collections.singletonList; | import static java.util.Collections.singletonList; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
assertParameters(entry("keyB", "b"), entry("keyA", "a")); | assertParameters(entry("keyB", "b"), entry("keyA", "a")); | ||||
assertMultiValueParameters(entry("keyB", singletonList("b")), entry("keyA", singletonList("a"))); | assertMultiValueParameters(entry("keyB", singletonList("b")), entry("keyA", singletonList("a"))); | ||||
underTest.setParam("keyC", ImmutableList.of("c1", "c2", "c3")); | |||||
underTest.setParam("keyC", asList("c1", "c2", "c3")); | |||||
assertParameters(entry("keyB", "b"), entry("keyA", "a"), entry("keyC", "c1")); | assertParameters(entry("keyB", "b"), entry("keyA", "a"), entry("keyC", "c1")); | ||||
assertMultiValueParameters( | assertMultiValueParameters( | ||||
entry("keyB", singletonList("b")), | entry("keyB", singletonList("b")), | ||||
entry("keyA", singletonList("a")), | entry("keyA", singletonList("a")), | ||||
entry("keyC", ImmutableList.of("c1", "c2", "c3"))); | |||||
entry("keyC", asList("c1", "c2", "c3"))); | |||||
} | } | ||||
@Test | @Test | ||||
public void skip_null_value_in_multi_param() { | public void skip_null_value_in_multi_param() { | ||||
underTest.setParam("key", newArrayList("v1", null, "", "v3")); | |||||
underTest.setParam("key", asList("v1", null, "", "v3")); | |||||
assertMultiValueParameters(entry("key", asList("v1", "", "v3"))); | assertMultiValueParameters(entry("key", asList("v1", "", "v3"))); | ||||
} | } |
*/ | */ | ||||
package org.sonarqube.ws.client; | package org.sonarqube.ws.client; | ||||
import com.google.common.collect.ImmutableMap; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.HashMap; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||
WsResponse wsResponse = underTest.call(wsRequest); | WsResponse wsResponse = underTest.call(wsRequest); | ||||
verifyRequested("POST", "api/issues/search", MediaTypes.JSON, ImmutableMap.of("foo", "bar")); | |||||
Map<String, String> expectedParams = new HashMap<>(); | |||||
expectedParams.put("foo", "bar"); | |||||
verifyRequested("POST", "api/issues/search", MediaTypes.JSON, expectedParams); | |||||
assertThat(wsResponse.code()).isEqualTo(400); | assertThat(wsResponse.code()).isEqualTo(400); | ||||
assertThat(wsResponse.content()).isEqualTo("{}"); | assertThat(wsResponse.content()).isEqualTo("{}"); | ||||
assertThat(IOUtils.toString(wsResponse.contentReader())).isEqualTo("{}"); | assertThat(IOUtils.toString(wsResponse.contentReader())).isEqualTo("{}"); |
*/ | */ | ||||
package org.sonarqube.ws.client; | package org.sonarqube.ws.client; | ||||
import com.google.common.base.Joiner; | |||||
import com.google.protobuf.Parser; | import com.google.protobuf.Parser; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
* | * | ||||
*/ | */ | ||||
public class ServiceTester<T extends BaseService> extends ExternalResource { | public class ServiceTester<T extends BaseService> extends ExternalResource { | ||||
private static final Joiner COMMA_JOINER = Joiner.on(","); | |||||
private final T underTest; | private final T underTest; | ||||
private final List<GetCall> getCalls = new ArrayList<>(); | private final List<GetCall> getCalls = new ArrayList<>(); |