diff --git a/README.md b/README.md index e28eef6..1fb0294 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ public interface PostService { // Use CleverClient to call the API var cleverClient = CleverClient.builder() - .urlBase("https://jsonplaceholder.typicode.com/") + .baseUrl("https://jsonplaceholder.typicode.com/") .build(); var postService = cleverClient.create(PostService.class); @@ -97,7 +97,7 @@ We have the following attributes to create a CleverClient object: | Attribute | Description | Required | | ----------- |----------------------------------------|-----------| -| urlBase | Api's url | mandatory | +| baseUrl | Api's url | mandatory | | headers | Pairs of headers name/value | optional | | httpClient | Java HttpClient object | optional | | endOfStream | Text used to mark the final of streams | optional | @@ -107,7 +107,7 @@ The attribute ```endOfStream``` is required when you have endpoints sending back Example: ```java -final var URL_BASE = "https://api.example.com"; +final var BASE_URL = "https://api.example.com"; final var HEADER_NAME = "Authorization"; final var HEADER_VALUE = "Bearer qwertyasdfghzxcvb"; final var END_OF_STREAM = "[DONE]"; @@ -121,7 +121,7 @@ var httpClient = HttpClient.newBuilder() .build(); var cleverClient = CleverClient.builder() - .urlBase(URL_BASE) + .baseUrl(BASE_URL) .headers(Arrays.asList(HEADER_NAME, HEADER_VALUE)) .httpClient(httpClient) .endOfStream(END_OF_STREAM) diff --git a/pom.xml b/pom.xml index c1ed707..0e12b28 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.sashirestela cleverclient - 0.12.0 + 0.13.0 jar cleverclient diff --git a/src/example/java/io/github/sashirestela/cleverclient/example/BasicExample.java b/src/example/java/io/github/sashirestela/cleverclient/example/BasicExample.java index 758372c..6407feb 100644 --- a/src/example/java/io/github/sashirestela/cleverclient/example/BasicExample.java +++ b/src/example/java/io/github/sashirestela/cleverclient/example/BasicExample.java @@ -7,10 +7,10 @@ public class BasicExample { public static void main(String[] args) { - final var URL_BASE = "https://jsonplaceholder.typicode.com"; + final var BASE_URL = "https://jsonplaceholder.typicode.com"; var cleverClient = CleverClient.builder() - .urlBase(URL_BASE) + .baseUrl(BASE_URL) .build(); var postService = cleverClient.create(PostService.class); diff --git a/src/example/java/io/github/sashirestela/cleverclient/example/FileDownloadExample.java b/src/example/java/io/github/sashirestela/cleverclient/example/FileDownloadExample.java index a07bb71..42ded38 100644 --- a/src/example/java/io/github/sashirestela/cleverclient/example/FileDownloadExample.java +++ b/src/example/java/io/github/sashirestela/cleverclient/example/FileDownloadExample.java @@ -17,10 +17,10 @@ static interface ImageService { } public static void main(String[] args) throws IOException { - final var URL_BASE = "https://via.placeholder.com"; + final var BASE_URL = "https://via.placeholder.com"; var cleverClient = CleverClient.builder() - .urlBase(URL_BASE) + .baseUrl(BASE_URL) .build(); var imageService = cleverClient.create(ImageService.class); diff --git a/src/example/java/io/github/sashirestela/cleverclient/example/HeaderExample.java b/src/example/java/io/github/sashirestela/cleverclient/example/HeaderExample.java index 9d4a39e..3960a01 100644 --- a/src/example/java/io/github/sashirestela/cleverclient/example/HeaderExample.java +++ b/src/example/java/io/github/sashirestela/cleverclient/example/HeaderExample.java @@ -27,10 +27,10 @@ static interface MethodHeaderService { } public static void main(String[] args) { - final var URL_BASE = "https://httpbin.org"; + final var BASE_URL = "https://httpbin.org"; var cleverClient = CleverClient.builder() - .urlBase(URL_BASE) + .baseUrl(BASE_URL) .build(); var classHeaderService = cleverClient.create(ClassHeaderService.class); diff --git a/src/example/java/io/github/sashirestela/cleverclient/example/MultiServiceExample.java b/src/example/java/io/github/sashirestela/cleverclient/example/MultiServiceExample.java index 91de837..e16bbc4 100644 --- a/src/example/java/io/github/sashirestela/cleverclient/example/MultiServiceExample.java +++ b/src/example/java/io/github/sashirestela/cleverclient/example/MultiServiceExample.java @@ -7,10 +7,10 @@ public class MultiServiceExample { public static void main(String[] args) { - final var URL_BASE = "https://jsonplaceholder.typicode.com"; + final var BASE_URL = "https://jsonplaceholder.typicode.com"; var cleverClient = CleverClient.builder() - .urlBase(URL_BASE) + .baseUrl(BASE_URL) .build(); var postService = cleverClient.create(PostService.class); var albumService = cleverClient.create(AlbumService.class); diff --git a/src/example/java/io/github/sashirestela/cleverclient/example/StreamExample.java b/src/example/java/io/github/sashirestela/cleverclient/example/StreamExample.java index 139ea83..594a62c 100644 --- a/src/example/java/io/github/sashirestela/cleverclient/example/StreamExample.java +++ b/src/example/java/io/github/sashirestela/cleverclient/example/StreamExample.java @@ -17,13 +17,13 @@ public class StreamExample { public static void main(String[] args) { - final var URL_BASE = "https://api.openai.com"; + final var BASE_URL = "https://api.openai.com"; final var AUTHORIZATION_HEADER = "Authorization"; final var BEARER_AUTHORIZATION = "Bearer " + System.getenv("OPENAI_API_KEY"); final var END_OF_STREAM = "[DONE]"; var cleverClient = CleverClient.builder() - .urlBase(URL_BASE) + .baseUrl(BASE_URL) .headers(Arrays.asList(AUTHORIZATION_HEADER, BEARER_AUTHORIZATION)) .endOfStream(END_OF_STREAM) .build(); diff --git a/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java b/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java index e1b9ad3..5f31164 100644 --- a/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java +++ b/src/main/java/io/github/sashirestela/cleverclient/CleverClient.java @@ -1,5 +1,7 @@ package io.github.sashirestela.cleverclient; +import static io.github.sashirestela.cleverclient.util.CommonUtil.isNullOrEmpty; + import java.net.http.HttpClient; import java.util.List; import java.util.Optional; @@ -12,7 +14,6 @@ import io.github.sashirestela.cleverclient.support.CleverClientSSE; import lombok.Builder; import lombok.Getter; -import lombok.NonNull; import lombok.Singular; /** @@ -22,17 +23,25 @@ */ @Getter public class CleverClient { - private static Logger logger = LoggerFactory.getLogger(CleverClient.class); + private static final Logger logger = LoggerFactory.getLogger(CleverClient.class); - private String urlBase; - private List headers; - private HttpClient httpClient; - private HttpProcessor httpProcessor; + private final String baseUrl; + @Deprecated + private final String urlBase = null; + private final List headers; + private final HttpClient httpClient; + private final HttpProcessor httpProcessor; /** * Constructor to create an instance of CleverClient. * - * @param urlBase Root of the url of the API service to call. Mandatory. + * @param baseUrl Root of the url of the API service to call. + * at least one of baseUrl and the deprecated urlBase is mandatory. + * in case both are specified and different baseUrl takes precedence + * + * @param urlBase [[ Deprecated ]] Root of the url of the API service to call. + * it is here for backward compatibility only. It will be removed in + * a future version. use `baseUrl()` instead. * @param headers Http headers for all the API service. Header's name and * value must be individual entries in the list. Optional. * @param httpClient Custom Java's HttpClient component. One is created by @@ -41,16 +50,19 @@ public class CleverClient { * server sent events (SSE). Optional. */ @Builder - public CleverClient(@NonNull String urlBase, @Singular List headers, HttpClient httpClient, + public CleverClient(String baseUrl, String urlBase, @Singular List headers, HttpClient httpClient, String endOfStream) { - this.urlBase = urlBase; + if (isNullOrEmpty(baseUrl) && isNullOrEmpty(urlBase)) { + throw new CleverClientException("At least one of baseUrl and urlBase is mandatory.", null, null); + } + this.baseUrl = isNullOrEmpty(baseUrl) ? urlBase : baseUrl; this.headers = Optional.ofNullable(headers).orElse(List.of()); if (this.headers.size() % 2 > 0) { throw new CleverClientException("Headers must be entered as pair of values in the list.", null, null); } this.httpClient = Optional.ofNullable(httpClient).orElse(HttpClient.newHttpClient()); CleverClientSSE.setEndOfStream(endOfStream); - this.httpProcessor = new HttpProcessor(this.urlBase, this.headers, this.httpClient); + this.httpProcessor = new HttpProcessor(this.baseUrl, this.headers, this.httpClient); logger.debug("CleverClient has been created."); } diff --git a/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java b/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java index d5baa46..01ba735 100644 --- a/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java +++ b/src/main/java/io/github/sashirestela/cleverclient/http/HttpProcessor.java @@ -22,19 +22,19 @@ public class HttpProcessor implements InvocationHandler { private static final Logger logger = LoggerFactory.getLogger(HttpProcessor.class); - private HttpClient httpClient; - private String urlBase; - private List headers; + private final HttpClient httpClient; + private final String baseUrl; + private final List headers; /** * Constructor to create an instance of HttpProcessor. * * @param httpClient Java's HttpClient component that solves the http calling. - * @param urlBase Root of the url of the API service to call. + * @param baseUrl Root of the url of the API service to call. * @param headers Http headers for all the API service. */ - public HttpProcessor(String urlBase, List headers, HttpClient httpClient) { - this.urlBase = urlBase; + public HttpProcessor(String baseUrl, List headers, HttpClient httpClient) { + this.baseUrl = baseUrl; this.headers = headers; this.httpClient = httpClient; } @@ -93,7 +93,7 @@ public Object invoke(Object proxy, Method method, Object[] arguments) throws Thr /** * Reads the interface method metadata from memory and uses them to prepare an * HttpConnector object that will resend the request to the Java's HttpClient - * and will receive the response. This method is called from the invoke mehod. + * and will receive the response. This method is called from the invoke method. * * @param method The Method instance corresponding to the interface method * invoked on the proxy instance. @@ -106,7 +106,7 @@ private Object resolve(Method method, Object[] arguments) { var interfaceMetadata = InterfaceMetadataStore.one().get(method.getDeclaringClass()); var methodMetadata = interfaceMetadata.getMethodBySignature().get(method.toString()); var urlMethod = interfaceMetadata.getFullUrlByMethod(methodMetadata); - var url = urlBase + URLBuilder.one().build(urlMethod, methodMetadata, arguments); + var url = baseUrl + URLBuilder.one().build(urlMethod, methodMetadata, arguments); var httpMethod = methodMetadata.getHttpAnnotationName(); var returnType = methodMetadata.getReturnType(); var isMultipart = methodMetadata.isMultipart(); diff --git a/src/main/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStore.java b/src/main/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStore.java index f6c221f..36d00a0 100644 --- a/src/main/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStore.java +++ b/src/main/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStore.java @@ -68,7 +68,7 @@ public InterfaceMetadata get(Class interfaceClass) { if (interfacesByFullName.containsKey(interfaceClass.getName())) { return interfacesByFullName.get(interfaceClass.getName()); } else { - throw new CleverClientException("The interaface {0} has not been saved yet.", interfaceClass.getSimpleName(), + throw new CleverClientException("The interface {0} has not been saved yet.", interfaceClass.getSimpleName(), null); } } @@ -125,7 +125,7 @@ private void validate(InterfaceMetadata interfaceMetadata) { interfaceMetadata.getMethodBySignature().forEach((methodSignature, methodMetadata) -> { if (!methodMetadata.isDefault()) { if (!methodMetadata.hasHttpAnnotation()) { - throw new CleverClientException("Missing HTTP anotation for the method {0}.", + throw new CleverClientException("Missing HTTP annotation for the method {0}.", methodMetadata.getName(), null); } } diff --git a/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java b/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java index ad53c7c..f3b6566 100644 --- a/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java +++ b/src/test/java/io/github/sashirestela/cleverclient/CleverClientTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.http.HttpClient; import java.util.List; @@ -16,53 +15,69 @@ class CleverClientTest { - @Test - void shouldSetPropertiesToDefaultValuesWhenBuilderIsCalledWithoutThoseProperties() { - var cleverClient = CleverClient.builder() - .urlBase("https://test") - .build(); - assertEquals(List.of(), cleverClient.getHeaders()); - assertEquals(HttpClient.Version.HTTP_2, cleverClient.getHttpClient().version()); - assertNotNull(cleverClient.getUrlBase()); - assertNotNull(cleverClient.getHttpProcessor()); - } - @Test - void shouldImplementInterfaceWhenCallingCreate() { - var cleverClient = CleverClient.builder() - .urlBase("https://test") - .header("headerName") - .header("headerValue") - .httpClient(HttpClient.newHttpClient()) - .endOfStream("[DONE]") - .build(); - var test = cleverClient.create(TestCleverClient.class); - assertNotNull(test); - } + @Test + void shouldSetPropertiesToDefaultValuesWhenBuilderIsCalledWithoutThoseProperties() { + var cleverClient = CleverClient.builder() + .baseUrl("https://test") + .build(); + assertEquals(List.of(), cleverClient.getHeaders()); + assertEquals(HttpClient.Version.HTTP_2, cleverClient.getHttpClient().version()); + assertNotNull(cleverClient.getBaseUrl()); + assertNotNull(cleverClient.getHttpProcessor()); + } - @Test - void shouldThrownExceptionWhenTryingToPassAnEmptyUrlBase() { - var cleverClientBuilder = CleverClient.builder() - .header("headerName") - .header("headerValue") - .httpClient(HttpClient.newHttpClient()) - .endOfStream("[DONE]"); - assertThrows(NullPointerException.class, - () -> cleverClientBuilder.build()); - } + @Test + void shouldBuildSuccessfullyUsingDeprecatedUrlBase() { + var baseUrl = "https://test"; + var cleverClient = CleverClient.builder() + .urlBase(baseUrl) + .build(); + assertEquals(List.of(), cleverClient.getHeaders()); + assertEquals(HttpClient.Version.HTTP_2, cleverClient.getHttpClient().version()); + // verify that baseUrl is set when building with the deprecated urlBase() method + assertEquals(cleverClient.getBaseUrl(), baseUrl); + assertNotNull(cleverClient.getHttpProcessor()); + } - @Test - void shouldThrownExceptionWhenTryingToPassAnOddNumbersOfHeaders() { - var cleverClientBuilder = CleverClient.builder() - .urlBase("http://test") - .header("oneHeader"); - Exception exception = assertThrows(CleverClientException.class, - () -> cleverClientBuilder.build()); - assertTrue(exception.getMessage().equals("Headers must be entered as pair of values in the list.")); - } + @Test + void shouldImplementInterfaceWhenCallingCreate() { + var cleverClient = CleverClient.builder() + .baseUrl("https://test") + .header("headerName") + .header("headerValue") + .httpClient(HttpClient.newHttpClient()) + .endOfStream("[DONE]") + .build(); + var test = cleverClient.create(TestCleverClient.class); + assertNotNull(test); + } - interface TestCleverClient { - @GET("/api/") - CompletableFuture getText(); - } + @Test + void shouldThrownExceptionWhenTryingToPassAnEmptyBaseUrlAndUrlBase() { + var cleverClientBuilder = CleverClient.builder() + .header("headerName") + .header("headerValue") + .httpClient(HttpClient.newHttpClient()) + .endOfStream("[DONE]"); + assertThrows(CleverClientException.class, + cleverClientBuilder::build); + } + + @Test + void shouldThrownExceptionWhenTryingToPassAnOddNumbersOfHeaders() { + var cleverClientBuilder = CleverClient.builder() + .baseUrl("http://test") + .header("oneHeader"); + Exception exception = assertThrows(CleverClientException.class, + cleverClientBuilder::build); + assertEquals("Headers must be entered as pair of values in the list.", + exception.getMessage()); + } + + interface TestCleverClient { + + @GET("/api/") + CompletableFuture getText(); + } } diff --git a/src/test/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStoreTest.java b/src/test/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStoreTest.java index 5a03b79..e1c834b 100644 --- a/src/test/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStoreTest.java +++ b/src/test/java/io/github/sashirestela/cleverclient/metadata/InterfaceMetadataStoreTest.java @@ -135,23 +135,22 @@ void shouldSaveInterfaceMetadataWhenAccomplishValidtion() { void shouldThrownExceptionWhenTryingToGetNotPreviouslySavedInterface() { Exception exception = assertThrows(CleverClientException.class, () -> store.get(ITest.NotSavedService.class)); - assertTrue(exception.getMessage().equals( - "The interaface NotSavedService has not been saved yet.")); + assertEquals("The interface NotSavedService has not been saved yet.", exception.getMessage()); } @Test void shouldThrownExceptionWhenMethodHasNotHttpAnnotation() { Exception exception = assertThrows(CleverClientException.class, () -> store.save(ITest.NotAnnotatedService.class)); - assertTrue(exception.getMessage().equals( - "Missing HTTP anotation for the method unannotatedMethod.")); + assertEquals("Missing HTTP annotation for the method unannotatedMethod.", + exception.getMessage()); } @Test void shouldThrownExceptionWhenUrlPathParamAtMethodUnmatchesAnnotatedArguments() { Exception exception = assertThrows(CleverClientException.class, - () -> store.save(ITest.BadPathParamService.class)); - assertTrue(exception.getMessage().equals( - "Path param demoId in the url cannot find an annotated argument in the method unmatchedPathParamMethod.")); + () -> store.save(ITest.BadPathParamService.class)); + assertEquals("Path param demoId in the url cannot find an annotated argument in the method unmatchedPathParamMethod.", + exception.getMessage()); } -} \ No newline at end of file +}