diff --git a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/KiotaClientFactory.java b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/KiotaClientFactory.java index 5f86565e4..992bb0ca6 100644 --- a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/KiotaClientFactory.java +++ b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/KiotaClientFactory.java @@ -1,15 +1,22 @@ package com.microsoft.kiota.http; +import com.microsoft.kiota.RequestOption; import com.microsoft.kiota.authentication.BaseBearerTokenAuthenticationProvider; import com.microsoft.kiota.http.middleware.AuthorizationHandler; import com.microsoft.kiota.http.middleware.HeadersInspectionHandler; import com.microsoft.kiota.http.middleware.ParametersNameDecodingHandler; import com.microsoft.kiota.http.middleware.RedirectHandler; import com.microsoft.kiota.http.middleware.RetryHandler; +import com.microsoft.kiota.http.middleware.UrlReplaceHandler; import com.microsoft.kiota.http.middleware.UserAgentHandler; +import com.microsoft.kiota.http.middleware.options.HeadersInspectionOption; +import com.microsoft.kiota.http.middleware.options.ParametersNameDecodingOption; +import com.microsoft.kiota.http.middleware.options.RedirectHandlerOption; +import com.microsoft.kiota.http.middleware.options.RetryHandlerOption; +import com.microsoft.kiota.http.middleware.options.UrlReplaceHandlerOption; +import com.microsoft.kiota.http.middleware.options.UserAgentHandlerOption; import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import okhttp3.Interceptor; import okhttp3.OkHttpClient; @@ -18,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** This class is used to build the HttpClient instance used by the core service. */ public class KiotaClientFactory { @@ -31,12 +39,23 @@ private KiotaClientFactory() {} return create(createDefaultInterceptors()); } + /** + * Creates an OkHttpClient Builder with the default configuration and middleware options. + * @param requestOptions The request options to use for the interceptors. + * @return an OkHttpClient Builder instance. + */ + @Nonnull public static OkHttpClient.Builder create(@Nonnull final RequestOption[] requestOptions) { + Objects.requireNonNull(requestOptions, "parameter requestOptions cannot be null"); + return create(createDefaultInterceptors(requestOptions)); + } + /** * Creates an OkHttpClient Builder with the default configuration and middleware. * @param interceptors The interceptors to add to the client. Will default to createDefaultInterceptors() if null. * @return an OkHttpClient Builder instance. */ - @Nonnull public static OkHttpClient.Builder create(@Nullable final Interceptor[] interceptors) { + @Nonnull public static OkHttpClient.Builder create(@Nonnull final Interceptor[] interceptors) { + Objects.requireNonNull(interceptors, "parameter interceptors cannot be null"); final OkHttpClient.Builder builder = new OkHttpClient.Builder() .connectTimeout(Duration.ofSeconds(100)) @@ -45,9 +64,7 @@ private KiotaClientFactory() {} Duration.ofSeconds( 100)); // TODO configure the default client options. - final Interceptor[] interceptorsOrDefault = - interceptors != null ? interceptors : createDefaultInterceptors(); - for (final Interceptor interceptor : interceptorsOrDefault) { + for (final Interceptor interceptor : interceptors) { builder.addInterceptor(interceptor); } return builder; @@ -58,12 +75,9 @@ private KiotaClientFactory() {} * @param interceptors The interceptors to add to the client. Will default to createDefaultInterceptors() if null. * @return an OkHttpClient Builder instance. */ - @Nonnull public static OkHttpClient.Builder create(@Nullable final List interceptors) { - if (interceptors == null) { - return create(); - } - return create( - (new ArrayList<>(interceptors)).toArray(new Interceptor[interceptors.size()])); + @Nonnull public static OkHttpClient.Builder create(@Nonnull final List interceptors) { + Objects.requireNonNull(interceptors, "parameter interceptors cannot be null"); + return create((new ArrayList<>(interceptors)).toArray(new Interceptor[0])); } /** @@ -73,7 +87,8 @@ private KiotaClientFactory() {} */ @Nonnull public static OkHttpClient.Builder create( @Nonnull final BaseBearerTokenAuthenticationProvider authenticationProvider) { - ArrayList interceptors = new ArrayList<>(createDefaultInterceptorsAsList()); + ArrayList interceptors = + new ArrayList<>(Arrays.asList(createDefaultInterceptors())); interceptors.add(new AuthorizationHandler(authenticationProvider)); return create(interceptors); } @@ -83,19 +98,81 @@ private KiotaClientFactory() {} * @return an array of interceptors. */ @Nonnull public static Interceptor[] createDefaultInterceptors() { - return new Interceptor[] { - new RedirectHandler(), - new RetryHandler(), - new ParametersNameDecodingHandler(), - new UserAgentHandler(), - new HeadersInspectionHandler() - }; + return createDefaultInterceptors(new RequestOption[0]); + } + + /** + * Creates the default interceptors for the client. + * @param requestOptions The request options to use for the interceptors. + * @return an array of interceptors. + */ + @Nonnull public static Interceptor[] createDefaultInterceptors( + @Nonnull final RequestOption[] requestOptions) { + Objects.requireNonNull(requestOptions, "parameter requestOptions cannot be null"); + + UrlReplaceHandlerOption uriReplacementOption = null; + UserAgentHandlerOption userAgentHandlerOption = null; + RetryHandlerOption retryHandlerOption = null; + RedirectHandlerOption redirectHandlerOption = null; + ParametersNameDecodingOption parametersNameDecodingOption = null; + HeadersInspectionOption headersInspectionHandlerOption = null; + + for (final RequestOption option : requestOptions) { + if (uriReplacementOption == null && option instanceof UrlReplaceHandlerOption) { + uriReplacementOption = (UrlReplaceHandlerOption) option; + } else if (retryHandlerOption == null && option instanceof RetryHandlerOption) { + retryHandlerOption = (RetryHandlerOption) option; + } else if (redirectHandlerOption == null && option instanceof RedirectHandlerOption) { + redirectHandlerOption = (RedirectHandlerOption) option; + } else if (parametersNameDecodingOption == null + && option instanceof ParametersNameDecodingOption) { + parametersNameDecodingOption = (ParametersNameDecodingOption) option; + } else if (userAgentHandlerOption == null && option instanceof UserAgentHandlerOption) { + userAgentHandlerOption = (UserAgentHandlerOption) option; + } else if (headersInspectionHandlerOption == null + && option instanceof HeadersInspectionOption) { + headersInspectionHandlerOption = (HeadersInspectionOption) option; + } + } + + final List handlers = new ArrayList<>(); + // orders matter as they are executed in a chain + // interceptors that only modify the request should be added first + // interceptors that read the response should be added last + handlers.add( + userAgentHandlerOption != null + ? new UserAgentHandler(userAgentHandlerOption) + : new UserAgentHandler()); + handlers.add( + parametersNameDecodingOption != null + ? new ParametersNameDecodingHandler(parametersNameDecodingOption) + : new ParametersNameDecodingHandler()); + handlers.add( + uriReplacementOption != null + ? new UrlReplaceHandler(uriReplacementOption) + : new UrlReplaceHandler()); + handlers.add( + headersInspectionHandlerOption != null + ? new HeadersInspectionHandler(headersInspectionHandlerOption) + : new HeadersInspectionHandler()); + handlers.add( + redirectHandlerOption != null + ? new RedirectHandler(redirectHandlerOption) + : new RedirectHandler()); + handlers.add( + retryHandlerOption != null + ? new RetryHandler(retryHandlerOption) + : new RetryHandler()); + + return handlers.toArray(new Interceptor[0]); } /** * Creates the default interceptors for the client. * @return an array of interceptors. + * @deprecated Use {@link #createDefaultInterceptors()} instead. */ + @Deprecated @Nonnull public static List createDefaultInterceptorsAsList() { return new ArrayList<>(Arrays.asList(createDefaultInterceptors())); } diff --git a/components/http/okHttp/src/test/java/com/microsoft/kiota/http/KiotaClientFactoryTest.java b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/KiotaClientFactoryTest.java new file mode 100644 index 000000000..7dd3eda0f --- /dev/null +++ b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/KiotaClientFactoryTest.java @@ -0,0 +1,108 @@ +package com.microsoft.kiota.http; + +import static org.junit.jupiter.api.Assertions.*; + +import com.microsoft.kiota.RequestOption; +import com.microsoft.kiota.http.middleware.ChaosHandler; +import com.microsoft.kiota.http.middleware.HeadersInspectionHandler; +import com.microsoft.kiota.http.middleware.ParametersNameDecodingHandler; +import com.microsoft.kiota.http.middleware.RedirectHandler; +import com.microsoft.kiota.http.middleware.RetryHandler; +import com.microsoft.kiota.http.middleware.UrlReplaceHandler; +import com.microsoft.kiota.http.middleware.UserAgentHandler; +import com.microsoft.kiota.http.middleware.options.RetryHandlerOption; +import com.microsoft.kiota.http.middleware.options.UrlReplaceHandlerOption; + +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class KiotaClientFactoryTest { + + @Test + void testCreatesDefaultInterceptors() throws IOException { + OkHttpClient client = KiotaClientFactory.create().build(); + assertNotNull(client.interceptors()); + assertEquals(6, client.interceptors().size()); + } + + @Test + void testDefaultInterceptorsWhenPassedIn() throws IOException { + OkHttpClient client = + KiotaClientFactory.create( + new Interceptor[] {getDisabledRetryHandler(), new ChaosHandler()}) + .build(); + List interceptors = client.interceptors(); + assertNotNull(interceptors); + assertEquals(2, interceptors.size()); + for (Interceptor interceptor : interceptors) { + if (interceptor instanceof RetryHandler) { + RetryHandlerOption handlerOption = ((RetryHandler) interceptor).getRetryOptions(); + assertEquals(0, handlerOption.delay()); + assertEquals(0, handlerOption.maxRetries()); + } + + assertTrue( + interceptor instanceof RetryHandler || interceptor instanceof ChaosHandler, + "Array should contain instances of RetryHandler and ChaosHandler"); + } + } + + @Test + void testDefaultInterceptorsWhenRequestOptionsPassedIn() throws IOException { + RetryHandlerOption retryHandlerOption = + new RetryHandlerOption((delay, executionCount, request, response) -> false, 0, 0); + UrlReplaceHandlerOption urlReplaceHandlerOption = + new UrlReplaceHandlerOption(new HashMap<>(), false); + + final ArrayList options = new ArrayList<>(); + options.add(urlReplaceHandlerOption); + options.add(retryHandlerOption); + + Interceptor[] interceptors = + KiotaClientFactory.createDefaultInterceptors(options.toArray(new RequestOption[0])); + OkHttpClient client = KiotaClientFactory.create(interceptors).build(); + List clientInterceptors = client.interceptors(); + assertNotNull(interceptors); + assertEquals(6, clientInterceptors.size()); + for (Interceptor interceptor : clientInterceptors) { + if (interceptor instanceof RetryHandler) { + RetryHandlerOption handlerOption = ((RetryHandler) interceptor).getRetryOptions(); + assertEquals(0, handlerOption.delay()); + assertEquals(0, handlerOption.maxRetries()); + } + + if (interceptor instanceof UrlReplaceHandler) { + UrlReplaceHandlerOption handlerOption = + ((UrlReplaceHandler) interceptor).getUrlReplaceHandlerOption(); + assertTrue(handlerOption.getReplacementPairs().isEmpty()); + assertFalse(handlerOption.isEnabled()); + } + + assertTrue( + interceptor instanceof UrlReplaceHandler + || interceptor instanceof RedirectHandler + || interceptor instanceof RetryHandler + || interceptor instanceof ParametersNameDecodingHandler + || interceptor instanceof UserAgentHandler + || interceptor instanceof HeadersInspectionHandler + || interceptor instanceof ChaosHandler, + "Array should contain instances of" + + " UrlReplaceHandler,RedirectHandler,RetryHandler,ParametersNameDecodingHandler,UserAgentHandler," + + " HeadersInspectionHandler, and ChaosHandler"); + } + } + + private static RetryHandler getDisabledRetryHandler() { + RetryHandlerOption retryHandlerOption = + new RetryHandlerOption((delay, executionCount, request, response) -> false, 0, 0); + RetryHandler retryHandler = new RetryHandler(retryHandlerOption); + return retryHandler; + } +}