package org.kohsuke.github; import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.commons.lang3.StringUtils; import org.kohsuke.github.internal.Previews; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.WillClose; import static java.util.Arrays.asList; /** * Class {@link GitHubRequest} represents an immutable instance used by the client to determine what information to * retrieve from a GitHub server. Use the {@link Builder} construct a {@link GitHubRequest}. *
* NOTE: {@link GitHubRequest} should include the data type to be returned. Any use cases where the same request should * be used to return different types of data could be handled in some other way. However, the return type is currently * not specified until late in the building process, so this is still untyped. *
*/ class GitHubRequest { private static final List
* If a header of the same name is already set, this method overrides it.
*
* @param name
* the name
* @param value
* the value
* @return the request builder
*/
public B setHeader(String name, String value) {
headers.put(name, value);
return (B) this;
}
/**
* With header requester.
*
* @param name
* the name
* @param value
* the value
* @return the request builder
*/
public B withHeader(String name, String value) {
String oldValue = headers.get(name);
if (!StringUtils.isBlank(oldValue)) {
value = oldValue + ", " + value;
}
return setHeader(name, value);
}
/**
* Object to inject into binding.
*
* @param value
* the value
* @return the request builder
*/
public B injectMappingValue(@NonNull Object value) {
return injectMappingValue(value.getClass().getName(), value);
}
/**
* Object to inject into binding.
*
* @param name
* the name
* @param value
* the value
* @return the request builder
*/
public B injectMappingValue(@NonNull String name, Object value) {
this.injectedMappingValues.put(name, value);
return (B) this;
}
public B withPreview(String name) {
return withHeader("Accept", name);
}
public B withPreview(Previews preview) {
return withPreview(preview.mediaType());
}
/**
* With requester.
*
* @param Map
* map of key value pairs to add
* @return the request builder
*/
public B with(Map
* Sets the path component of api URL without URI encoding.
*
* Should only be used when passing a literal URL field from a GHObject, such as {@link GHContent#refresh()} or
* when needing to set query parameters on requests methods that don't usually have them, such as
* {@link GHRelease#uploadAsset(String, InputStream, String)}.
*
* @param rawUrlPath
* the content type
* @return the request builder
*/
B setRawUrlPath(@Nonnull String rawUrlPath) {
Objects.requireNonNull(rawUrlPath);
// This method should only work for full urls, which must start with "http"
if (!rawUrlPath.startsWith("http")) {
throw new GHException("Raw URL must start with 'http'");
}
this.urlPath = rawUrlPath;
return (B) this;
}
/**
* Path component of api URL. Appended to api url.
*
* If urlPath starts with a slash, it will be URI encoded as a path. If it starts with anything else, it will be
* used as is.
*
* @param urlPathItems
* the content type
* @return the request builder
*/
public B withUrlPath(@Nonnull String urlPath, @Nonnull String... urlPathItems) {
// full url may be set and reset as needed
if (urlPathItems.length == 0 && !urlPath.startsWith("/")) {
return setRawUrlPath(urlPath);
}
// Once full url is set, do not allow path setting
if (!this.urlPath.startsWith("/")) {
throw new GHException("Cannot append to url path after setting a full url");
}
String tailUrlPath = urlPath;
if (urlPathItems.length != 0) {
tailUrlPath += "/" + String.join("/", urlPathItems);
}
tailUrlPath = StringUtils.prependIfMissing(tailUrlPath, "/");
this.urlPath = urlPathEncode(tailUrlPath);
return (B) this;
}
/**
* Small number of GitHub APIs use HTTP methods somewhat inconsistently, and use a body where it's not expected.
* Normally whether parameters go as query parameters or a body depends on the HTTP verb in use, but this method
* forces the parameters to be sent as a body.
*
* @return the request builder
*/
public B inBody() {
forceBody = true;
return (B) this;
}
}
protected static class Entry {
final String key;
final Object value;
protected Entry(String key, Object value) {
this.key = key;
this.value = value;
}
}
/**
* Encode the path to url safe string.
*
* @param value
* string to be path encoded.
* @return The encoded string.
*/
private static String urlPathEncode(String value) {
try {
return new URI(null, null, value, null, null).toASCIIString();
} catch (URISyntaxException ex) {
throw new AssertionError(ex);
}
}
}