diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a66e9c9..9d46b49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,12 +17,11 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK 8 - uses: actions/setup-java@v2 + - name: Setup JDK 11 + uses: actions/setup-java@v4 with: - distribution: 'adopt' - java-version: '8' - cache: 'maven' + distribution: 'temurin' + java-version: '11' - name: Build with Maven run: mvn --batch-mode -T 1C -U install -Dgpg.skip diff --git a/pom.xml b/pom.xml index 42b2ddf..9965ca5 100644 --- a/pom.xml +++ b/pom.xml @@ -131,12 +131,12 @@ com.google.guava guava - 30.1-jre + 33.2.1-jre - com.ning + org.asynchttpclient async-http-client - 1.9.31 + 3.0.0 org.slf4j diff --git a/src/main/java/io/split/fastly/client/FastlyApiClient.java b/src/main/java/io/split/fastly/client/FastlyApiClient.java index 759183f..597d776 100644 --- a/src/main/java/io/split/fastly/client/FastlyApiClient.java +++ b/src/main/java/io/split/fastly/client/FastlyApiClient.java @@ -4,17 +4,19 @@ import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.FluentCaseInsensitiveStringsMap; -import com.ning.http.client.FluentStringsMap; -import com.ning.http.client.Response; +import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Future; +import org.asynchttpclient.AsyncHttpClientConfig; +import org.asynchttpclient.BoundRequestBuilder; +import org.asynchttpclient.DefaultAsyncHttpClient; +import org.asynchttpclient.DefaultAsyncHttpClientConfig; +import org.asynchttpclient.Response; import static io.split.fastly.client.FastlyApiClient.Method.POST; import static io.split.fastly.client.FastlyApiClient.Method.PURGE; @@ -23,299 +25,301 @@ /** * Wrapper client for http://docs.fastly.com/api - * + *

* Created by patricioe on 10/12/15. */ public class FastlyApiClient { - /* package private */ static final String FASTLY_URL = "https://api.fastly.com"; - /* package private */ static final Joiner SURROGATE_KEY_JOINER = Joiner.on(" "); - - private final Map _commonHeaders; - private final AsyncHttpClientConfig _config; - private final AsyncHttpExecutor _asyncHttpExecutor; - private final String _serviceId; - private final String _apiKey; - - public FastlyApiClient(final String apiKey, final String serviceId) { - this(apiKey, serviceId, null); - } - - public FastlyApiClient(final String apiKey, final String serviceId, AsyncHttpClientConfig config) { - this(apiKey, serviceId, config, null); - } - - /* package private */ - @VisibleForTesting - FastlyApiClient(final String apiKey, final String serviceId, AsyncHttpClientConfig config, AsyncHttpExecutor executor) { - _commonHeaders = ImmutableMap.of( - "Fastly-Key", apiKey, - "Accept", "application/json", - "User-Agent", "fastly-api-java-v"+ VersionResolver.instance().getVersion()); - _config = config; - _apiKey = apiKey; - _serviceId = serviceId; - _asyncHttpExecutor = (Objects.isNull(executor)) ? new AsyncHttpExecutorImpl() : executor; - } + /* package private */ static final String FASTLY_URL = "https://api.fastly.com"; + /* package private */ static final Joiner SURROGATE_KEY_JOINER = Joiner.on(" "); + + private final Map _commonHeaders; + private final AsyncHttpClientConfig _config; + private final AsyncHttpExecutor _asyncHttpExecutor; + private final String _serviceId; + private final String _apiKey; + + public FastlyApiClient(final String apiKey, final String serviceId) { + this(apiKey, serviceId, null); + } + + public FastlyApiClient(final String apiKey, final String serviceId, AsyncHttpClientConfig config) { + this(apiKey, serviceId, config, null); + } + + /* package private */ + @VisibleForTesting + FastlyApiClient(final String apiKey, final String serviceId, AsyncHttpClientConfig config, AsyncHttpExecutor executor) { + _commonHeaders = ImmutableMap.of( + "Fastly-Key", apiKey, + "Accept", "application/json", + "User-Agent", "fastly-api-java-v" + VersionResolver.instance().getVersion()); + _config = config; + _apiKey = apiKey; + _serviceId = serviceId; + _asyncHttpExecutor = (Objects.isNull(executor)) ? new AsyncHttpExecutorImpl() : executor; + } + + public Future vclUpload(int version, String vcl, String id, String name) { + return vclUpload(version, vcl, id, name, FASTLY_URL); + } + + public Future vclUpload(int version, String vcl, String id, String name, String fastlyUrl) { + String apiUrl = String.format("%s/service/%s/version/%d/vcl", fastlyUrl, _serviceId, version); + return _asyncHttpExecutor.execute( + apiUrl, + POST, + ImmutableMap.builder() + .putAll(_commonHeaders) + .put("Content-Type", "application/x-www-form-urlencoded") + .build(), + ImmutableMap.builder().put("content", vcl).put("name", name).put("id", id).build()); + } + + public List> vclUpdate(int version, Map vcl) { + return vclUpdate(version, vcl, FASTLY_URL); + } + + public List> vclUpdate(int version, Map vcl, String fastlyUrl) { + return vcl.entrySet().stream().map(e -> { + String apiUrl = String.format("%s/service/%s/version/$d/vcl/%s", fastlyUrl, _serviceId, version, e.getKey()); + return _asyncHttpExecutor.execute( + apiUrl, + PUT, + ImmutableMap.builder() + .putAll(_commonHeaders) + .put("Content-Type", "application/x-www-form-urlencoded") + .build(), + ImmutableMap.builder().put("content", e.getValue()).put("name", e.getKey()).build()); + + }).collect(toList()); + } + + public Future purgeUrl(final String url) { + return purgeUrl(url, Collections.emptyMap()); + } + + public Future softPurgeUrl(final String url) { + return softPurgeUrl(url, Collections.emptyMap()); + } + + public Future softPurgeUrl(final String url, Map extraHeaders) { + return purgeUrl(url, buildHeaderForSoftPurge(extraHeaders)); + } + + public Future purgeUrl(final String url, Map extraHeaders) { + return _asyncHttpExecutor.execute( + url, + PURGE, + ImmutableMap.builder().putAll(_commonHeaders).putAll(extraHeaders).build(), + Collections.emptyMap()); + } + + public Future purgeKey(String key) { + return purgeKey(key, Collections.emptyMap()); + } + + public Future softPurgeKey(String key) { + return softPurgeKey(key, Collections.emptyMap()); + } + + public Future softPurgeKey(String key, Map extraHeaders) { + return purgeKey(key, buildHeaderForSoftPurge(extraHeaders)); + } + + public Future purgeKey(String key, Map extraHeaders) { + return purgeKey(key, extraHeaders, FASTLY_URL); + } + + public Future purgeKey(String key, Map extraHeaders, String fastlyUrl) { + String apiUrl = String.format("%s/service/%s/purge/%s", fastlyUrl, _serviceId, key); + return _asyncHttpExecutor.execute( + apiUrl, + POST, + ImmutableMap.builder() + .putAll(_commonHeaders) + .putAll(extraHeaders) + .build(), + Collections.emptyMap()); + } + + public Future purgeKeys(List keys) { + return purgeKeys(keys, FASTLY_URL); + } + + public Future purgeKeys(List keys, String fastlyUrl) { + return purgeKeys(keys, Collections.emptyMap(), fastlyUrl); + } + + public Future softPurgeKeys(List keys) { + return softPurgeKeys(keys, FASTLY_URL); + } + + public Future softPurgeKeys(List keys, String fastlyUrl) { + return purgeKeys(keys, buildHeaderForSoftPurge(Collections.emptyMap()), fastlyUrl); + } + + public Future purgeKeys(List keys, Map extraHeaders) { + return purgeKeys(keys, extraHeaders, FASTLY_URL); + } + + public Future purgeKeys(List keys, Map extraHeaders, String fastlyUrl) { + Preconditions.checkNotNull(keys, "keys cannot be null!"); + Preconditions.checkArgument(keys.size() <= 256, "Fastly can't purge batches of more than 256 keys"); + + String apiUrl = String.format("%s/service/%s/purge", fastlyUrl, _serviceId); + return _asyncHttpExecutor.execute( + apiUrl, + POST, + ImmutableMap.builder() + .putAll(_commonHeaders) + .putAll(extraHeaders) + .put("Surrogate-Key", SURROGATE_KEY_JOINER.join(keys)) + .build(), + Collections.emptyMap()); + } + + public Future purgeAll() { + return purgeAll(FASTLY_URL); + } + + public Future purgeAll(String fastlyURL) { + String apiUrl = String.format("%s/service/%s/purge_all", fastlyURL, _serviceId); + return _asyncHttpExecutor.execute( + apiUrl, + POST, + _commonHeaders, + Collections.emptyMap()); + } - public Future vclUpload(int version, String vcl, String id, String name) { - return vclUpload(version, vcl, id, name, FASTLY_URL); - } + private Map buildHeaderForSoftPurge(Map extraHeaders) { + return ImmutableMap.builder().put("Fastly-Soft-Purge", "1").putAll(extraHeaders).build(); + } - public Future vclUpload(int version, String vcl, String id, String name, String fastlyUrl) { - String apiUrl = String.format("%s/service/%s/version/%d/vcl", fastlyUrl, _serviceId, version); - return _asyncHttpExecutor.execute( - apiUrl, - POST, - ImmutableMap. builder() - .putAll(_commonHeaders) - .put("Content-Type", "application/x-www-form-urlencoded") - .build(), - ImmutableMap. builder().put("content", vcl).put("name", name).put("id", id).build()); - } + public void closeConnectionPool() throws IOException { + _asyncHttpExecutor.close(); + } - public List> vclUpdate(int version, Map vcl) { - return vclUpdate(version, vcl, FASTLY_URL); - } + /** + * Supports PURGE operations. + */ + static class ExtendedAsyncHttpClient extends DefaultAsyncHttpClient { - public List> vclUpdate(int version, Map vcl, String fastlyUrl) { - return vcl.entrySet().stream().map( e -> { - String apiUrl = String.format("%s/service/%s/version/$d/vcl/%s", fastlyUrl, _serviceId, version, e.getKey()); - return _asyncHttpExecutor.execute( - apiUrl, - PUT, - ImmutableMap. builder() - .putAll(_commonHeaders) - .put("Content-Type", "application/x-www-form-urlencoded") - .build(), - ImmutableMap. builder().put("content", e.getValue()).put("name", e.getKey()).build()); - - }).collect(toList()); + public ExtendedAsyncHttpClient(AsyncHttpClientConfig config) { + super(config); } - public Future purgeUrl(final String url) { - return purgeUrl(url, Collections.emptyMap()); + /** + * Prepare an HTTP client PURGE request. + * + * @param url A well formed URL. + * @return {@link BoundRequestBuilder} + */ + public BoundRequestBuilder preparePurge(String url) { + return requestBuilder("PURGE", url); } + } - public Future softPurgeUrl(final String url) { - return softPurgeUrl(url, Collections.emptyMap()); - } - public Future softPurgeUrl(final String url, Map extraHeaders) { - return purgeUrl(url, buildHeaderForSoftPurge(extraHeaders)); - } + /* package private */ + @VisibleForTesting + interface AsyncHttpExecutor { - public Future purgeUrl(final String url, Map extraHeaders) { - return _asyncHttpExecutor.execute( - url, - PURGE, - ImmutableMap.builder().putAll(_commonHeaders).putAll(extraHeaders).build(), - Collections.emptyMap()); - } + Future execute(String apiUrl, Method method, Map headers, Map parameters); - public Future purgeKey(String key) { - return purgeKey(key, Collections.emptyMap()); - } + public void close() throws IOException; + } - public Future softPurgeKey(String key) { - return softPurgeKey(key, Collections.emptyMap()); - } + /** + * Entity Responsible for executing the requesting against the remote endpoint. + */ + private class AsyncHttpExecutorImpl implements AsyncHttpExecutor { - public Future softPurgeKey(String key, Map extraHeaders) { - return purgeKey(key, buildHeaderForSoftPurge(extraHeaders)); - } + private ExtendedAsyncHttpClient client; - public Future purgeKey(String key, Map extraHeaders) { - return purgeKey(key, extraHeaders, FASTLY_URL); - } + private AsyncHttpClientConfig defaultConfig = new DefaultAsyncHttpClientConfig.Builder() + .setMaxConnections(50) + .setMaxRequestRetry(3) + .setMaxConnections(20000) + .build(); - public Future purgeKey(String key, Map extraHeaders, String fastlyUrl) { - String apiUrl = String.format("%s/service/%s/purge/%s", fastlyUrl, _serviceId, key); - return _asyncHttpExecutor.execute( - apiUrl, - POST, - ImmutableMap. builder() - .putAll(_commonHeaders) - .putAll(extraHeaders) - .build(), - Collections.emptyMap()); + public AsyncHttpExecutorImpl() { + client = _config != null ? new ExtendedAsyncHttpClient(_config) : new ExtendedAsyncHttpClient(defaultConfig); } - public Future purgeKeys(List keys) { - return purgeKeys(keys, FASTLY_URL); - } + public void close() throws IOException { - public Future purgeKeys(List keys, String fastlyUrl) { - return purgeKeys(keys, Collections.emptyMap(), fastlyUrl); + client.close(); } - public Future softPurgeKeys(List keys) { - return softPurgeKeys(keys, FASTLY_URL); - } + public Future execute(String apiUrl, + Method method, + Map headers, + Map parameters) { - public Future softPurgeKeys(List keys, String fastlyUrl) { - return purgeKeys(keys, buildHeaderForSoftPurge(Collections.emptyMap()), fastlyUrl); - } + BoundRequestBuilder request = getRequestForMethod(apiUrl, method); - public Future purgeKeys(List keys, Map extraHeaders) { - return purgeKeys(keys, extraHeaders, FASTLY_URL); - } + build(request, headers, parameters); - public Future purgeKeys(List keys, Map extraHeaders, String fastlyUrl) { - Preconditions.checkNotNull(keys, "keys cannot be null!"); - Preconditions.checkArgument(keys.size() <= 256, "Fastly can't purge batches of more than 256 keys"); - - String apiUrl = String.format("%s/service/%s/purge", fastlyUrl, _serviceId); - return _asyncHttpExecutor.execute( - apiUrl, - POST, - ImmutableMap. builder() - .putAll(_commonHeaders) - .putAll(extraHeaders) - .put("Surrogate-Key", SURROGATE_KEY_JOINER.join(keys)) - .build(), - Collections.emptyMap()); + return request.execute(); } - public Future purgeAll() { - return purgeAll(FASTLY_URL); - } + private BoundRequestBuilder getRequestForMethod(String apiURL, Method method) { - public Future purgeAll(String fastlyURL) { - String apiUrl = String.format("%s/service/%s/purge_all", fastlyURL, _serviceId); - return _asyncHttpExecutor.execute( - apiUrl, - POST, - _commonHeaders, - Collections.emptyMap()); - } + if (method == Method.PURGE) { + return client.preparePurge(apiURL); + } - private Map buildHeaderForSoftPurge(Map extraHeaders) { - return ImmutableMap. builder().put("Fastly-Soft-Purge", "1").putAll(extraHeaders).build(); - } + if (method == Method.POST) { + return client.preparePost(apiURL); + } - public void closeConnectionPool() { - _asyncHttpExecutor.close(); - } + if (method == Method.PUT) { + return client.preparePut(apiURL); + } - /** - * Supports PURGE operations. - */ - static class ExtendedAsyncHttpClient extends AsyncHttpClient { - - public ExtendedAsyncHttpClient(AsyncHttpClientConfig config) { - super(config); - } - - /** - * Prepare an HTTP client PURGE request. - * - * @param url A well formed URL. - * @return {@link BoundRequestBuilder} - */ - public BoundRequestBuilder preparePurge(String url) { - return requestBuilder("PURGE", url); - } - } + if (method == Method.DELETE) { + return client.prepareDelete(apiURL); + } + if (method == Method.GET) { + return client.prepareGet(apiURL); + } - /* package private */ - @VisibleForTesting - interface AsyncHttpExecutor { - Future execute( String apiUrl,Method method, Map headers, Map parameters); - public void close(); + return null; } - /** - * Entity Responsible for executing the requesting against the remote endpoint. - */ - private class AsyncHttpExecutorImpl implements AsyncHttpExecutor { - - private ExtendedAsyncHttpClient client; - - private AsyncHttpClientConfig defaultConfig = new AsyncHttpClientConfig.Builder() - .setAllowPoolingConnections(true) - .setMaxConnections(50) - .setMaxRequestRetry(3) - .setMaxConnections(20000) - .build(); - - public AsyncHttpExecutorImpl() { - client = _config != null ? new ExtendedAsyncHttpClient(_config) : new ExtendedAsyncHttpClient(defaultConfig); - } - - public void close() { - client.close(); - } - - public Future execute( String apiUrl, - Method method, - Map headers, - Map parameters) { - - AsyncHttpClient.BoundRequestBuilder request = getRequestForMethod(apiUrl, method); - - build(request, headers, parameters); + private void build(BoundRequestBuilder request, Map headers, Map parameters) { - return request.execute(); - } + Map> fluentCaseInsensitiveStringsMap = new HashMap<>(); + headers.forEach((k, v) -> fluentCaseInsensitiveStringsMap.put(k, Collections.singletonList(v))); - private AsyncHttpClient.BoundRequestBuilder getRequestForMethod(String apiURL, Method method) { + request.setHeaders(fluentCaseInsensitiveStringsMap); - if (method == Method.PURGE) { - return client.preparePurge(apiURL); - } + Map> fluentStringsMap = new HashMap<>(); + parameters.forEach((k, v) -> fluentStringsMap.put(k, Collections.singletonList(v))); - if (method == Method.POST) { - return client.preparePost(apiURL); - } - - if (method == Method.PUT) { - return client.preparePut(apiURL); - } - - if (method == Method.DELETE) { - return client.prepareDelete(apiURL); - } - - if (method == Method.GET) { - return client.prepareGet(apiURL); - } - - return null; - } - - private void build(AsyncHttpClient.BoundRequestBuilder request, Map headers, Map parameters) { - - FluentCaseInsensitiveStringsMap fluentCaseInsensitiveStringsMap = new FluentCaseInsensitiveStringsMap(); - headers.forEach(fluentCaseInsensitiveStringsMap::add); - - request.setHeaders(fluentCaseInsensitiveStringsMap); - - FluentStringsMap fluentStringsMap = new FluentStringsMap(); - parameters.forEach(fluentStringsMap::add); - - if (request.build().getMethod().equals("GET")) { - request.setQueryParams(fluentStringsMap); - } else { - request.setFormParams(fluentStringsMap); - } - - String host = headers.get("Host"); - if (host != null) { - request.setVirtualHost(host); - } - } + if (request.build().getMethod().equals("GET")) { + request.setQueryParams(fluentStringsMap); + } else { + request.setFormParams(fluentStringsMap); + } + String host = headers.get("Host"); + if (host != null) { + request.setVirtualHost(host); + } } - enum Method { - POST, - PURGE, - PUT, - GET, - DELETE; - } + } + + enum Method { + POST, + PURGE, + PUT, + GET, + DELETE; + } } diff --git a/src/test/java/io/split/fastly/client/FastlyApiClientIntegrationTest.java b/src/test/java/io/split/fastly/client/FastlyApiClientIntegrationTest.java index 17d7107..4b867d1 100644 --- a/src/test/java/io/split/fastly/client/FastlyApiClientIntegrationTest.java +++ b/src/test/java/io/split/fastly/client/FastlyApiClientIntegrationTest.java @@ -1,8 +1,8 @@ package io.split.fastly.client; import com.google.common.collect.Lists; -import com.ning.http.client.Response; import io.split.BaseFastlyTest; +import org.asynchttpclient.Response; import org.junit.Ignore; import org.junit.Test;