*/
public class SecurityServletFilter implements Filter {
- private static final Set<String> ALLOWED_HTTP_METHODS = Set.of("DELETE", "GET", "HEAD", "POST", "PUT");
+ private static final Set<String> ALLOWED_HTTP_METHODS = Set.of("DELETE", "GET", "HEAD", "POST", "PUT", "PATCH");
@Override
public void init(FilterConfig filterConfig) {
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<SELF>> implements WsRequest {
private final String path;
private final DefaultHeaders headers = new DefaultHeaders();
private OptionalInt timeOutInMs = OptionalInt.empty();
private OptionalInt writeTimeOutInMs = OptionalInt.empty();
-
+
BaseRequest(String path) {
this.path = path;
}
return timeOutInMs;
}
- public SELF setTimeOutInMs(int timeOutInMs) {
+ public <T extends SELF> T setTimeOutInMs(int timeOutInMs) {
this.timeOutInMs = OptionalInt.of(timeOutInMs);
- return (SELF) this;
+ return (T) this;
}
@Override
return writeTimeOutInMs;
}
- public SELF setWriteTimeOutInMs(int writeTimeOutInMs) {
+ public <T extends SELF> T setWriteTimeOutInMs(int writeTimeOutInMs) {
this.writeTimeOutInMs = OptionalInt.of(writeTimeOutInMs);
- return (SELF) this;
+ return (T) this;
}
/**
* Expected media type of response. Default is {@link MediaTypes#JSON}.
*/
@SuppressWarnings("unchecked")
- public SELF setMediaType(String s) {
+ public <T extends SELF> T setMediaType(String s) {
requireNonNull(s, "media type of response cannot be null");
this.mediaType = s;
- return (SELF) this;
+ return (T) this;
}
- public SELF setParam(String key, @Nullable String value) {
- return setSingleValueParam(key, value);
+ public <T extends SELF> T setParam(String key, @Nullable String value) {
+ return (T) setSingleValueParam(key, value);
}
- public SELF setParam(String key, @Nullable Integer value) {
+ public <T extends SELF> T setParam(String key, @Nullable Integer value) {
return setSingleValueParam(key, value);
}
- public SELF setParam(String key, @Nullable Long value) {
+ public <T extends SELF> T setParam(String key, @Nullable Long value) {
return setSingleValueParam(key, value);
}
- public SELF setParam(String key, @Nullable Float value) {
+ public <T extends SELF> T setParam(String key, @Nullable Float value) {
return setSingleValueParam(key, value);
}
- public SELF setParam(String key, @Nullable Boolean value) {
+ public <T extends SELF> T setParam(String key, @Nullable Boolean value) {
return setSingleValueParam(key, value);
}
@SuppressWarnings("unchecked")
- private SELF setSingleValueParam(String key, @Nullable Object value) {
+ private <T extends SELF> T setSingleValueParam(String key, @Nullable Object value) {
checkArgument(!isNullOrEmpty(key), "a WS parameter key cannot be null");
if (value == null) {
- return (SELF) this;
+ return (T) this;
}
parameters.setValue(key, value.toString());
- return (SELF) this;
+ return (T) this;
}
@SuppressWarnings("unchecked")
- public SELF setParam(String key, @Nullable Collection<? extends Object> values) {
+ public <T extends SELF> T setParam(String key, @Nullable Collection<? extends Object> values) {
checkArgument(!isNullOrEmpty(key), "a WS parameter key cannot be null");
if (values == null || values.isEmpty()) {
- return (SELF) this;
+ return (T) this;
}
parameters.setValues(key, values.stream()
.map(Object::toString)
.collect(Collectors.toList()));
- return (SELF) this;
+ return (T) this;
}
@Override
}
@SuppressWarnings("unchecked")
- public SELF setHeader(String name, @Nullable String value) {
+ public <T extends SELF> T setHeader(String name, @Nullable String value) {
requireNonNull(name, "Header name can't be null");
headers.setValue(name, value);
- return (SELF) this;
+ return (T) this;
}
private static class DefaultParameters implements Parameters {
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
+import org.sonarqube.ws.client.RequestWithPayload.Part;
import static java.lang.String.format;
import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
if (httpRequest instanceof GetRequest) {
return get((GetRequest) httpRequest);
}
- if (httpRequest instanceof PostRequest) {
- return post((PostRequest) httpRequest);
+ if (httpRequest instanceof RequestWithPayload) {
+ return executeRequest((RequestWithPayload) httpRequest);
}
throw new IllegalArgumentException(format("Unsupported implementation: %s", httpRequest.getClass()));
}
return new OkHttpResponse(doCall(prepareOkHttpClient(okHttpClient, getRequest), okRequestBuilder.build()));
}
- private WsResponse post(PostRequest postRequest) {
- HttpUrl.Builder urlBuilder = prepareUrlBuilder(postRequest);
+ private WsResponse executeRequest(RequestWithPayload<?> request) {
+ HttpUrl.Builder urlBuilder = prepareUrlBuilder(request);
RequestBody body;
- Map<String, PostRequest.Part> parts = postRequest.getParts();
- if (postRequest.hasBody()) {
- body = RequestBody.create(JSON, postRequest.getBody());
+ Map<String, Part> parts = request.getParts();
+ if (request.hasBody()) {
+ body = RequestBody.create(JSON, request.getBody());
} else if (parts.isEmpty()) {
// parameters are defined in the body (application/x-www-form-urlencoded)
FormBody.Builder formBody = new FormBody.Builder();
- postRequest.getParameters().getKeys()
- .forEach(key -> postRequest.getParameters().getValues(key)
+ request.getParameters().getKeys()
+ .forEach(key -> request.getParameters().getValues(key)
.forEach(value -> formBody.add(key, value)));
body = formBody.build();
} else {
// parameters are defined in the URL (as GET)
- completeUrlQueryParameters(postRequest, urlBuilder);
+ completeUrlQueryParameters(request, urlBuilder);
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
parts.entrySet().forEach(param -> {
- PostRequest.Part part = param.getValue();
+ Part part = param.getValue();
bodyBuilder.addFormDataPart(
param.getKey(),
part.getFile().getName(),
});
body = bodyBuilder.build();
}
- Request.Builder okRequestBuilder = prepareOkRequestBuilder(postRequest, urlBuilder).post(body);
- Response response = doCall(prepareOkHttpClient(noRedirectOkHttpClient, postRequest), okRequestBuilder.build());
- response = checkRedirect(response, postRequest);
+ Request.Builder okRequestBuilder = prepareOkRequestBuilder(request, urlBuilder);
+ okRequestBuilder = request.addVerbToBuilder(body).apply(okRequestBuilder);
+ Response response = doCall(prepareOkHttpClient(noRedirectOkHttpClient, request), okRequestBuilder.build());
+ response = checkRedirect(response, request);
return new OkHttpResponse(response);
}
}
}
- private Response checkRedirect(Response response, PostRequest postRequest) {
+ private Response checkRedirect(Response response, RequestWithPayload<?> postRequest) {
switch (response.code()) {
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
}
}
- private Response followPostRedirect(Response response, PostRequest postRequest) {
+ private Response followPostRedirect(Response response, RequestWithPayload<?> postRequest) {
String location = response.header("Location");
if (location == null) {
throw new IllegalStateException(format("Missing HTTP header 'Location' in redirect of %s", response.request().url()));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.client;
+
+import java.util.function.Function;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+
+/**
+ * @since 10.0
+ */
+public class PatchRequest extends RequestWithPayload<PatchRequest> {
+
+ public PatchRequest(String path) {
+ super(path);
+ }
+
+ @Override
+ Function<Request.Builder, Request.Builder> addVerbToBuilder(RequestBody body) {
+ return builder -> builder.patch(body);
+ }
+
+ @Override
+ public Method getMethod() {
+ return Method.PATCH;
+ }
+
+}
*/
package org.sonarqube.ws.client;
-import java.io.File;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.function.Function;
+import okhttp3.Request;
+import okhttp3.RequestBody;
/**
* @since 5.3
*/
-public class PostRequest extends BaseRequest<PostRequest> {
-
- private String body;
- private final Map<String, Part> parts = new LinkedHashMap<>();
+public class PostRequest extends RequestWithPayload<PostRequest> {
public PostRequest(String path) {
super(path);
}
@Override
- public Method getMethod() {
- return Method.POST;
- }
-
- public PostRequest setBody(String body) {
- this.body = body;
- return this;
+ Function<Request.Builder, Request.Builder> addVerbToBuilder(RequestBody body) {
+ return builder -> builder.post(body);
}
- public String getBody() {
- return body;
- }
-
- public boolean hasBody() {
- return this.body != null;
- }
-
- public PostRequest setPart(String name, Part part) {
- this.parts.put(name, part);
- return this;
- }
-
- public Map<String, Part> getParts() {
- return parts;
- }
-
- public static class Part {
- private final String mediaType;
- private final File file;
-
- public Part(String mediaType, File file) {
- this.mediaType = mediaType;
- this.file = file;
- }
-
- public String getMediaType() {
- return mediaType;
- }
-
- public File getFile() {
- return file;
- }
+ @Override
+ public Method getMethod() {
+ return Method.POST;
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.client;
+
+import java.io.File;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Function;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+
+/**
+ * @since 5.3
+ */
+public abstract class RequestWithPayload<T extends RequestWithPayload<T>> extends BaseRequest<RequestWithPayload<T>> {
+
+ private String body;
+ private final Map<String, Part> parts = new LinkedHashMap<>();
+
+ protected RequestWithPayload(String path) {
+ super(path);
+ }
+
+ public T setBody(String body) {
+ this.body = body;
+ return (T) this;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public boolean hasBody() {
+ return this.body != null;
+ }
+
+ public T setPart(String name, Part part) {
+ this.parts.put(name, part);
+ return (T) this;
+ }
+
+ abstract Function<Request.Builder, Request.Builder> addVerbToBuilder(RequestBody body);
+
+ public Map<String, Part> getParts() {
+ return parts;
+ }
+
+ public static class Part {
+ private final String mediaType;
+ private final File file;
+
+ public Part(String mediaType, File file) {
+ this.mediaType = mediaType;
+ this.file = file;
+ }
+
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ public File getFile() {
+ return file;
+ }
+ }
+
+}
Headers getHeaders();
enum Method {
- GET, POST
+ GET, POST, PATCH
}
}
private String p;
private String ps;
private String q;
+ private Boolean deactivated;
/**
* Example value: "42"
public String getQ() {
return q;
}
+
+ public Boolean getDeactivated() {
+ return deactivated;
+ }
+
+ public SearchRequest setDeactivated(Boolean deactivated) {
+ this.deactivated = deactivated;
+ return this;
+ }
}
new GetRequest(path("search"))
.setParam("p", request.getP())
.setParam("ps", request.getPs())
- .setParam("q", request.getQ()),
+ .setParam("q", request.getQ())
+ .setParam("deactivated", request.getDeactivated()),
SearchWsResponse.parser());
}
return new RequestAssert<>(getRequest);
}
- public RequestAssert<PostRequest> assertThat(PostRequest postRequest) {
+ public RequestAssert<?> assertThat(PostRequest postRequest) {
return new RequestAssert<>(postRequest);
}
public PostRequest getPostRequest() {
assertSinglePostCall();
- return postCalls.iterator().next().getRequest();
+ return (PostRequest) postCalls.iterator().next().getRequest();
}
@CheckForNull
}
@Immutable
- public static final class PostCall extends CallWithParser<PostRequest> {
+ public static final class PostCall extends CallWithParser<RequestWithPayload<PostRequest>> {
public PostCall(PostRequest postRequest, @Nullable Parser<?> parser) {
super(postRequest, parser);