From f9db410bee1f6296ff07f19a4b131276e6993312 Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Mon, 18 Sep 2023 17:48:55 -0400 Subject: [PATCH 1/7] update http client --- build.gradle | 4 +- .../PineconeIndexOperationClient.java | 76 ++++--- .../io/pinecone/model/CreateIndexRequest.java | 2 +- .../java/io/pinecone/utils/Constants.java | 9 + .../PineconeIndexOperationClientTest.java | 200 ++++-------------- 5 files changed, 105 insertions(+), 186 deletions(-) create mode 100644 src/main/java/io/pinecone/utils/Constants.java diff --git a/build.gradle b/build.gradle index ad2d6956..81f4b139 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,8 @@ dependencies { runtime 'io.netty:netty-tcnative-boringssl-static:2.0.59.Final' implementation 'org.slf4j:slf4j-api:2.0.5' implementation 'com.google.api.grpc:proto-google-common-protos:2.14.3' + implementation 'com.squareup.okhttp3:okhttp:4.10.0' + testImplementation("com.squareup.okhttp3:mockwebserver:4.10.0") implementation 'org.asynchttpclient:async-http-client:2.12.1' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.14.2' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.14.2' @@ -36,7 +38,7 @@ dependencies { testImplementation "io.grpc:grpc-testing:${grpcVersion}" testImplementation 'junit:junit:4.13.2' testImplementation "org.hamcrest:hamcrest:2.2" - testImplementation 'org.mockito:mockito-core:4.8.0' + testImplementation 'org.mockito:mockito-inline:4.8.0' testImplementation 'org.slf4j:slf4j-simple:2.0.5' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.0' diff --git a/src/main/java/io/pinecone/PineconeIndexOperationClient.java b/src/main/java/io/pinecone/PineconeIndexOperationClient.java index b0a09d52..ecdedf50 100644 --- a/src/main/java/io/pinecone/PineconeIndexOperationClient.java +++ b/src/main/java/io/pinecone/PineconeIndexOperationClient.java @@ -1,17 +1,17 @@ package io.pinecone; import io.pinecone.model.CreateIndexRequest; -import org.asynchttpclient.AsyncHttpClient; -import org.asynchttpclient.DefaultAsyncHttpClient; - +import okhttp3.*; import java.io.IOException; +import static io.pinecone.utils.Constants.*; + public class PineconeIndexOperationClient { - private AsyncHttpClient client; - private PineconeClientConfig clientConfig; - private String url; + private final OkHttpClient client; + private final PineconeClientConfig clientConfig; + private final String url; - PineconeIndexOperationClient(PineconeClientConfig clientConfig, AsyncHttpClient client) { + PineconeIndexOperationClient(PineconeClientConfig clientConfig, OkHttpClient client) { this.clientConfig = clientConfig; this.client = client; this.url = "https://controller." + clientConfig.getEnvironment() + ".pinecone.io/databases/"; @@ -19,34 +19,52 @@ public class PineconeIndexOperationClient { public PineconeIndexOperationClient(PineconeClientConfig clientConfig) { this.clientConfig = clientConfig; - client = new DefaultAsyncHttpClient(); + this.client = new OkHttpClient(); this.url = "https://controller." + clientConfig.getEnvironment() + ".pinecone.io/databases/"; } public void deleteIndex(String indexName) throws IOException { - System.out.println("Sending delete index request:"); - client.prepare("DELETE", url + indexName) - .setHeader("accept", "text/plain") - .setHeader("Api-Key", clientConfig.getApiKey()) - .execute() - .toCompletableFuture() - .join(); - - client.close(); + Request request = new Request.Builder() + .url(url + indexName) + .delete() + .addHeader(ACCEPT_HEADER, TEXT_PLAIN) + .addHeader(API_KEY, clientConfig.getApiKey()) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException(response.message()); + } + } + finally { + close(client); + } } public void createIndex(CreateIndexRequest createIndexRequest) throws IOException { - client.prepare("POST", url) - .setHeader("accept", "text/plain") - .setHeader("content-type", "application/json") - .setHeader("Api-Key", clientConfig.getApiKey()) - .setBody(createIndexRequest.toJson()) - .execute() - .toCompletableFuture() - .join(); - - client.close(); - } -} + MediaType mediaType = MediaType.parse("application/json; charset=utf-8"); + RequestBody requestBody = RequestBody.create(mediaType, createIndexRequest.toJson()); + Request request = new Request.Builder() + .url(url) + .post(requestBody) + .addHeader(ACCEPT_HEADER, TEXT_PLAIN) + .addHeader(CONTENT_TYPE, CONTENT_TYPE_JSON) + .addHeader(API_KEY, clientConfig.getApiKey()) + .build(); + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException(response.message()); + } + } + finally { + close(client); + } + } + + public void close(OkHttpClient client) { + client.dispatcher().executorService().shutdown(); + client.connectionPool().evictAll(); + } +} diff --git a/src/main/java/io/pinecone/model/CreateIndexRequest.java b/src/main/java/io/pinecone/model/CreateIndexRequest.java index d56a7c86..a7f68769 100644 --- a/src/main/java/io/pinecone/model/CreateIndexRequest.java +++ b/src/main/java/io/pinecone/model/CreateIndexRequest.java @@ -186,7 +186,7 @@ public static void validateJsonObject(JsonNode jsonNode) { if(jsonNode.get("name").isNull()) { throw new PineconeValidationException("Index name cannot be empty"); } - System.out.println("jsonString: " + jsonNode.get("dimension")); + if(jsonNode.get("dimension").isNull()) { throw new PineconeValidationException("Dimension cannot be empty"); } diff --git a/src/main/java/io/pinecone/utils/Constants.java b/src/main/java/io/pinecone/utils/Constants.java new file mode 100644 index 00000000..fc229c37 --- /dev/null +++ b/src/main/java/io/pinecone/utils/Constants.java @@ -0,0 +1,9 @@ +package io.pinecone.utils; + +public class Constants { + public static final String ACCEPT_HEADER = "accept"; + public static final String API_KEY = "Api-Key"; + public static final String CONTENT_TYPE = "content-type"; + public static final String CONTENT_TYPE_JSON = "application/json"; + public static final String TEXT_PLAIN = "text/plain"; +} \ No newline at end of file diff --git a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java index 29f377f6..098805f8 100644 --- a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java +++ b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java @@ -1,169 +1,59 @@ package io.pinecone; -import io.pinecone.model.CreateIndexRequest; -import io.pinecone.model.IndexMetadataConfig; -import org.asynchttpclient.*; +import okhttp3.*; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.fail; import static org.mockito.Mockito.*; -public class PineconeIndexOperationClientTest { - - @SuppressWarnings("unchecked") - @Test - public void testIndexDeletion() throws IOException { - AsyncHttpClient mockClient = mock(DefaultAsyncHttpClient.class); - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey").withEnvironment("testEnvironment"); - Response mockResponse = mock(Response.class); - BoundRequestBuilder mockBoundRequestBuilder = mock(BoundRequestBuilder.class); - ListenableFuture mockResponseFuture = mock(ListenableFuture.class); - CompletableFuture mockCompletableFuture = mock(CompletableFuture.class); - - when(mockResponse.getStatusCode()).thenReturn(202); - when(mockClient.prepare(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setHeader(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.execute()).thenReturn(mockResponseFuture); - when(mockResponseFuture.toCompletableFuture()).thenReturn(mockCompletableFuture); - - PineconeIndexOperationClient indexService = new PineconeIndexOperationClient(clientConfig, mockClient); - indexService.deleteIndex("testIndex"); - - verify(mockClient, times(1)).prepare(eq("DELETE"), anyString()); - verify(mockBoundRequestBuilder).setHeader(eq("Api-Key"), eq("testApiKey")); - verify(mockBoundRequestBuilder).execute(); - verify(mockClient).close(); - } - - @SuppressWarnings("unchecked") - @Test - public void testIndexCreation() throws IOException { - AsyncHttpClient mockClient = mock(DefaultAsyncHttpClient.class); - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey").withEnvironment("testEnvironment"); - Response mockResponse = mock(Response.class); - BoundRequestBuilder mockBoundRequestBuilder = mock(BoundRequestBuilder.class); - ListenableFuture mockResponseFuture = mock(ListenableFuture.class); - CompletableFuture mockCompletableFuture = mock(CompletableFuture.class); - - when(mockResponse.getStatusCode()).thenReturn(201); - when(mockClient.prepare(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setHeader(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setBody(anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.execute()).thenReturn(mockResponseFuture); - when(mockResponseFuture.toCompletableFuture()).thenReturn(mockCompletableFuture); - - PineconeIndexOperationClient indexService = new PineconeIndexOperationClient(clientConfig, mockClient); - CreateIndexRequest createIndexRequest = new CreateIndexRequest().withIndexName("test_name").withDimension(3); - indexService.createIndex(createIndexRequest); - - verify(mockClient, times(1)).prepare(eq("POST"), anyString()); - verify(mockBoundRequestBuilder).setHeader(eq("accept"), eq("text/plain")); - verify(mockBoundRequestBuilder).setHeader(eq("content-type"), eq("application/json")); - verify(mockBoundRequestBuilder).execute(); - verify(mockClient).close(); - } - - @Test - public void testIndexCreationThrowsExceptionWithNullIndex() { - AsyncHttpClient mockClient = mock(DefaultAsyncHttpClient.class); - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey").withEnvironment("testEnvironment"); - Response mockResponse = mock(Response.class); - BoundRequestBuilder mockBoundRequestBuilder = mock(BoundRequestBuilder.class); - ListenableFuture mockResponseFuture = mock(ListenableFuture.class); - CompletableFuture mockCompletableFuture = mock(CompletableFuture.class); - - when(mockResponse.getStatusCode()).thenReturn(201); - when(mockClient.prepare(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setHeader(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setBody(anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.execute()).thenReturn(mockResponseFuture); - when(mockResponseFuture.toCompletableFuture()).thenReturn(mockCompletableFuture); +import okhttp3.mockwebserver.MockWebServer; +import java.util.concurrent.ExecutorService; - PineconeIndexOperationClient indexService = new PineconeIndexOperationClient(clientConfig, mockClient); - CreateIndexRequest createIndexRequest = new CreateIndexRequest(); - - try{ - indexService.createIndex(createIndexRequest); - fail("Expected validation exception not occurred"); - } - catch (PineconeValidationException validationException) { - System.out.println("Expected PineconeValidationException with index not null occurred!"); - } - catch (IOException e) { - throw new RuntimeException(e); - } +public class PineconeIndexOperationClientTest { + private static MockWebServer mockWebServer; + @BeforeAll + public static void setUp() throws IOException { + mockWebServer = new MockWebServer(); + mockWebServer.start(); } - @Test - public void testIndexCreationThrowsExceptionWithNullDimension() { - AsyncHttpClient mockClient = mock(DefaultAsyncHttpClient.class); - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey").withEnvironment("testEnvironment"); - Response mockResponse = mock(Response.class); - BoundRequestBuilder mockBoundRequestBuilder = mock(BoundRequestBuilder.class); - ListenableFuture mockResponseFuture = mock(ListenableFuture.class); - CompletableFuture mockCompletableFuture = mock(CompletableFuture.class); - - when(mockResponse.getStatusCode()).thenReturn(201); - when(mockClient.prepare(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setHeader(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setBody(anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.execute()).thenReturn(mockResponseFuture); - when(mockResponseFuture.toCompletableFuture()).thenReturn(mockCompletableFuture); - - PineconeIndexOperationClient indexService = new PineconeIndexOperationClient(clientConfig, mockClient); - CreateIndexRequest createIndexRequest = new CreateIndexRequest().withIndexName("test_name"); - - try{ - indexService.createIndex(createIndexRequest); - fail("Expected validation exception not occurred"); - } - catch (PineconeValidationException validationException) { - System.out.println("Expected PineconeValidationException with dimension not null occurred!"); - } catch (IOException e) { - throw new RuntimeException(e); - } + @AfterAll + public static void tearDown() throws IOException { + mockWebServer.shutdown(); } @Test - public void testIndexCreationWithAllFields() { - AsyncHttpClient mockClient = mock(DefaultAsyncHttpClient.class); - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey").withEnvironment("testEnvironment"); - Response mockResponse = mock(Response.class); - BoundRequestBuilder mockBoundRequestBuilder = mock(BoundRequestBuilder.class); - ListenableFuture mockResponseFuture = mock(ListenableFuture.class); - CompletableFuture mockCompletableFuture = mock(CompletableFuture.class); - - when(mockResponse.getStatusCode()).thenReturn(201); - when(mockClient.prepare(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setHeader(anyString(), anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.setBody(anyString())).thenReturn(mockBoundRequestBuilder); - when(mockBoundRequestBuilder.execute()).thenReturn(mockResponseFuture); - when(mockResponseFuture.toCompletableFuture()).thenReturn(mockCompletableFuture); - - IndexMetadataConfig metadataConfig = new IndexMetadataConfig(); - List indexedItems = Arrays.asList("A", "B", "C", "D"); - metadataConfig.setIndexed(indexedItems); - PineconeIndexOperationClient indexService = new PineconeIndexOperationClient(clientConfig, mockClient); - CreateIndexRequest createIndexRequest = new CreateIndexRequest() - .withIndexName("test_name") - .withDimension(3) - .withMetric("euclidean") - .withPods(2) - .withPodType("p1.x2") - .withReplicas(2) - .withMetadataConfig(metadataConfig) - .withSourceCollection("step"); - - try { - indexService.createIndex(createIndexRequest); - } - catch (IOException e) { - throw new RuntimeException(e); - } + public void testDeleteIndex() throws IOException { + String indexName = "testIndex"; + PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + + Call mockCall = mock(Call.class); + when(mockCall.execute()).thenReturn(new Response.Builder() + .request(new Request.Builder().url("http://localhost").build()) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .body(ResponseBody.create(MediaType.parse("text/plain"), "Response body")) + .build()); + + OkHttpClient mockClient = mock(OkHttpClient.class); + Dispatcher mockDispatcher = mock(Dispatcher.class); + ExecutorService mockExecutorService = mock(ExecutorService.class); + ConnectionPool mockConnectionPool = mock(ConnectionPool.class); + when(mockClient.newCall(any(Request.class))).thenReturn(mockCall); + when(mockClient.dispatcher()).thenReturn(mockDispatcher); + when(mockClient.dispatcher().executorService()).thenReturn(mockExecutorService); + when(mockClient.connectionPool()).thenReturn(mockConnectionPool); + + PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); + indexOperationClient.deleteIndex(indexName); + + verify(mockClient, times(1)).newCall(any(Request.class)); + verify(mockCall, times(1)).execute(); + verify(mockDispatcher, times(1)).executorService(); + verify(mockExecutorService, times(1)).shutdown(); + verify(mockConnectionPool, times(1)).evictAll(); } -} \ No newline at end of file +} From 338f33c64e6aa62d6be19998e3e296d1ba3c2d75 Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Tue, 19 Sep 2023 11:13:48 -0400 Subject: [PATCH 2/7] Add unit test for create index --- build.gradle | 1 - .../PineconeIndexOperationClient.java | 6 +- .../java/io/pinecone/utils/Constants.java | 2 + .../PineconeIndexOperationClientTest.java | 111 +++++++++++++++++- 4 files changed, 114 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 81f4b139..775dc98a 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,6 @@ dependencies { compileOnly "org.apache.tomcat:annotations-api:6.0.53" // necessary for Java 9+ testImplementation "io.grpc:grpc-testing:${grpcVersion}" - testImplementation 'junit:junit:4.13.2' testImplementation "org.hamcrest:hamcrest:2.2" testImplementation 'org.mockito:mockito-inline:4.8.0' testImplementation 'org.slf4j:slf4j-simple:2.0.5' diff --git a/src/main/java/io/pinecone/PineconeIndexOperationClient.java b/src/main/java/io/pinecone/PineconeIndexOperationClient.java index ecdedf50..d3aa2009 100644 --- a/src/main/java/io/pinecone/PineconeIndexOperationClient.java +++ b/src/main/java/io/pinecone/PineconeIndexOperationClient.java @@ -14,13 +14,13 @@ public class PineconeIndexOperationClient { PineconeIndexOperationClient(PineconeClientConfig clientConfig, OkHttpClient client) { this.clientConfig = clientConfig; this.client = client; - this.url = "https://controller." + clientConfig.getEnvironment() + ".pinecone.io/databases/"; + this.url = BASE_URL_PREFIX + clientConfig.getEnvironment() + BASE_URL_SUFFIX; } public PineconeIndexOperationClient(PineconeClientConfig clientConfig) { this.clientConfig = clientConfig; this.client = new OkHttpClient(); - this.url = "https://controller." + clientConfig.getEnvironment() + ".pinecone.io/databases/"; + this.url = BASE_URL_PREFIX + clientConfig.getEnvironment() + BASE_URL_SUFFIX; } public void deleteIndex(String indexName) throws IOException { @@ -43,7 +43,7 @@ public void deleteIndex(String indexName) throws IOException { public void createIndex(CreateIndexRequest createIndexRequest) throws IOException { MediaType mediaType = MediaType.parse("application/json; charset=utf-8"); - RequestBody requestBody = RequestBody.create(mediaType, createIndexRequest.toJson()); + RequestBody requestBody = RequestBody.create(createIndexRequest.toJson(), mediaType); Request request = new Request.Builder() .url(url) diff --git a/src/main/java/io/pinecone/utils/Constants.java b/src/main/java/io/pinecone/utils/Constants.java index fc229c37..fc455016 100644 --- a/src/main/java/io/pinecone/utils/Constants.java +++ b/src/main/java/io/pinecone/utils/Constants.java @@ -3,6 +3,8 @@ public class Constants { public static final String ACCEPT_HEADER = "accept"; public static final String API_KEY = "Api-Key"; + public static final String BASE_URL_PREFIX = "https://controller."; + public static final String BASE_URL_SUFFIX = ".pinecone.io/databases/"; public static final String CONTENT_TYPE = "content-type"; public static final String CONTENT_TYPE_JSON = "application/json"; public static final String TEXT_PLAIN = "text/plain"; diff --git a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java index 098805f8..9a775a09 100644 --- a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java +++ b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java @@ -1,14 +1,21 @@ package io.pinecone; +import io.pinecone.model.CreateIndexRequest; +import io.pinecone.model.IndexMetadataConfig; import okhttp3.*; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; import okhttp3.mockwebserver.MockWebServer; + +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ExecutorService; public class PineconeIndexOperationClientTest { @@ -34,7 +41,7 @@ public void testDeleteIndex() throws IOException { .request(new Request.Builder().url("http://localhost").build()) .protocol(Protocol.HTTP_1_1) .code(200) - .message("OK") + .message("The index has been successfully deleted.") .body(ResponseBody.create(MediaType.parse("text/plain"), "Response body")) .build()); @@ -56,4 +63,104 @@ public void testDeleteIndex() throws IOException { verify(mockExecutorService, times(1)).shutdown(); verify(mockConnectionPool, times(1)).evictAll(); } -} + + @Test + public void testCreateIndex() throws IOException { + PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + CreateIndexRequest createIndexRequest = new CreateIndexRequest() + .withIndexName("test_name").withDimension(3); + + Call mockCall = mock(Call.class); + when(mockCall.execute()).thenReturn(new Response.Builder() + .request(new Request.Builder().url("http://localhost").build()) + .protocol(Protocol.HTTP_1_1) + .code(201) + .message("The index has been successfully created") + .body(ResponseBody.create(MediaType.parse("text/plain"), "Response body")) + .build()); + + OkHttpClient mockClient = mock(OkHttpClient.class); + Dispatcher mockDispatcher = mock(Dispatcher.class); + ExecutorService mockExecutorService = mock(ExecutorService.class); + ConnectionPool mockConnectionPool = mock(ConnectionPool.class); + when(mockClient.newCall(any(Request.class))).thenReturn(mockCall); + when(mockClient.dispatcher()).thenReturn(mockDispatcher); + when(mockClient.dispatcher().executorService()).thenReturn(mockExecutorService); + when(mockClient.connectionPool()).thenReturn(mockConnectionPool); + + PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); + indexOperationClient.createIndex(createIndexRequest); + + verify(mockClient, times(1)).newCall(any(Request.class)); + verify(mockCall, times(1)).execute(); + verify(mockDispatcher, times(1)).executorService(); + verify(mockExecutorService, times(1)).shutdown(); + verify(mockConnectionPool, times(1)).evictAll(); + } + + @Test + public void testCreateIndexWithNullIndex() { + PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + CreateIndexRequest createIndexRequest = new CreateIndexRequest().withDimension(3); + + OkHttpClient mockClient = mock(OkHttpClient.class); + PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); + assertThrows(PineconeValidationException.class, () -> indexOperationClient.createIndex(createIndexRequest)); + } + + @Test + public void testCreateIndexWithNullDimensions() { + PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + CreateIndexRequest createIndexRequest = new CreateIndexRequest().withIndexName("testIndexName"); + + OkHttpClient mockClient = mock(OkHttpClient.class); + PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); + assertThrows(PineconeValidationException.class, () -> indexOperationClient.createIndex(createIndexRequest)); + } + + @Test + public void testCreateIndexWithAllFields() throws IOException { + PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + + IndexMetadataConfig metadataConfig = new IndexMetadataConfig(); + List indexedItems = Arrays.asList("A", "B", "C", "D"); + metadataConfig.setIndexed(indexedItems); + + CreateIndexRequest createIndexRequest = new CreateIndexRequest() + .withIndexName("test_name") + .withDimension(3) + .withMetric("euclidean") + .withPods(2) + .withPodType("p1.x2") + .withReplicas(2) + .withMetadataConfig(metadataConfig) + .withSourceCollection("step"); + + Call mockCall = mock(Call.class); + when(mockCall.execute()).thenReturn(new Response.Builder() + .request(new Request.Builder().url("http://localhost").build()) + .protocol(Protocol.HTTP_1_1) + .code(201) + .message("The index has been successfully created") + .body(ResponseBody.create(MediaType.parse("text/plain"), "Response body")) + .build()); + + OkHttpClient mockClient = mock(OkHttpClient.class); + Dispatcher mockDispatcher = mock(Dispatcher.class); + ExecutorService mockExecutorService = mock(ExecutorService.class); + ConnectionPool mockConnectionPool = mock(ConnectionPool.class); + when(mockClient.newCall(any(Request.class))).thenReturn(mockCall); + when(mockClient.dispatcher()).thenReturn(mockDispatcher); + when(mockClient.dispatcher().executorService()).thenReturn(mockExecutorService); + when(mockClient.connectionPool()).thenReturn(mockConnectionPool); + + PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); + indexOperationClient.createIndex(createIndexRequest); + + verify(mockClient, times(1)).newCall(any(Request.class)); + verify(mockCall, times(1)).execute(); + verify(mockDispatcher, times(1)).executorService(); + verify(mockExecutorService, times(1)).shutdown(); + verify(mockConnectionPool, times(1)).evictAll(); + } +} \ No newline at end of file From b5ac2d8dd910b5eb9283861da374952715701c26 Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Tue, 19 Sep 2023 11:19:06 -0400 Subject: [PATCH 3/7] indent files --- .../io/pinecone/PineconeIndexOperationClient.java | 9 +++------ .../io/pinecone/PineconeIndexOperationClientTest.java | 11 +++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/pinecone/PineconeIndexOperationClient.java b/src/main/java/io/pinecone/PineconeIndexOperationClient.java index d3aa2009..1d16a4fd 100644 --- a/src/main/java/io/pinecone/PineconeIndexOperationClient.java +++ b/src/main/java/io/pinecone/PineconeIndexOperationClient.java @@ -3,7 +3,6 @@ import io.pinecone.model.CreateIndexRequest; import okhttp3.*; import java.io.IOException; - import static io.pinecone.utils.Constants.*; public class PineconeIndexOperationClient { @@ -35,8 +34,7 @@ public void deleteIndex(String indexName) throws IOException { if (!response.isSuccessful()) { throw new IOException(response.message()); } - } - finally { + } finally { close(client); } } @@ -57,8 +55,7 @@ public void createIndex(CreateIndexRequest createIndexRequest) throws IOExceptio if (!response.isSuccessful()) { throw new IOException(response.message()); } - } - finally { + } finally { close(client); } } @@ -67,4 +64,4 @@ public void close(OkHttpClient client) { client.dispatcher().executorService().shutdown(); client.connectionPool().evictAll(); } -} +} \ No newline at end of file diff --git a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java index 9a775a09..c3500c97 100644 --- a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java +++ b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java @@ -3,23 +3,22 @@ import io.pinecone.model.CreateIndexRequest; import io.pinecone.model.IndexMetadataConfig; import okhttp3.*; +import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.*; - -import okhttp3.mockwebserver.MockWebServer; - import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + public class PineconeIndexOperationClientTest { private static MockWebServer mockWebServer; + @BeforeAll public static void setUp() throws IOException { mockWebServer = new MockWebServer(); From b080e18c0257d20644ebbcc54d59f393879c0435 Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Tue, 19 Sep 2023 18:20:36 -0400 Subject: [PATCH 4/7] Add validations to prevent exceptions --- .../io/pinecone/PineconeClientConfig.java | 3 -- .../PineconeIndexOperationClient.java | 22 ++++++--- .../PineconeIndexOperationClientTest.java | 46 +++++++++++++++++-- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/pinecone/PineconeClientConfig.java b/src/main/java/io/pinecone/PineconeClientConfig.java index 8a386808..7b9535d4 100644 --- a/src/main/java/io/pinecone/PineconeClientConfig.java +++ b/src/main/java/io/pinecone/PineconeClientConfig.java @@ -17,9 +17,6 @@ public class PineconeClientConfig { */ private String apiKey; - /** - * Required project name - */ private String projectName; private String environment; diff --git a/src/main/java/io/pinecone/PineconeIndexOperationClient.java b/src/main/java/io/pinecone/PineconeIndexOperationClient.java index 1d16a4fd..2cbb2ae6 100644 --- a/src/main/java/io/pinecone/PineconeIndexOperationClient.java +++ b/src/main/java/io/pinecone/PineconeIndexOperationClient.java @@ -10,16 +10,26 @@ public class PineconeIndexOperationClient { private final PineconeClientConfig clientConfig; private final String url; - PineconeIndexOperationClient(PineconeClientConfig clientConfig, OkHttpClient client) { - this.clientConfig = clientConfig; + private PineconeIndexOperationClient(PineconeClientConfig clientConfig, OkHttpClient client, String url) { this.client = client; - this.url = BASE_URL_PREFIX + clientConfig.getEnvironment() + BASE_URL_SUFFIX; + this.clientConfig = clientConfig; + this.url = url; + } + + public PineconeIndexOperationClient(PineconeClientConfig clientConfig, OkHttpClient client) { + this(clientConfig, client, createUrl(clientConfig)); } public PineconeIndexOperationClient(PineconeClientConfig clientConfig) { - this.clientConfig = clientConfig; - this.client = new OkHttpClient(); - this.url = BASE_URL_PREFIX + clientConfig.getEnvironment() + BASE_URL_SUFFIX; + this(clientConfig, new OkHttpClient()); + } + + private static String createUrl(PineconeClientConfig clientConfig) { + if (clientConfig.getApiKey() == null || clientConfig.getEnvironment() == null) { + throw new PineconeValidationException("Both API key and environment name are required for index operations."); + } + + return BASE_URL_PREFIX + clientConfig.getEnvironment() + BASE_URL_SUFFIX; } public void deleteIndex(String indexName) throws IOException { diff --git a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java index c3500c97..dcbc1a2b 100644 --- a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java +++ b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java @@ -30,10 +30,38 @@ public static void tearDown() throws IOException { mockWebServer.shutdown(); } + @Test + public void IndexOpsWithoutApiKey() throws IOException { + PineconeClientConfig clientConfig = new PineconeClientConfig() + .withEnvironment("testEnvironment"); + OkHttpClient mockClient = mock(OkHttpClient.class); + + assertThrows(PineconeValidationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); + } + + @Test + public void IndexOpsWithoutEnvironment() throws IOException { + PineconeClientConfig clientConfig = new PineconeClientConfig() + .withApiKey("testApiKey"); + OkHttpClient mockClient = mock(OkHttpClient.class); + + assertThrows(PineconeValidationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); + } + + @Test + public void IndexOpsWithoutApiKeyAndEnvironment() throws IOException { + PineconeClientConfig clientConfig = new PineconeClientConfig(); + OkHttpClient mockClient = mock(OkHttpClient.class); + + assertThrows(PineconeValidationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); + } + @Test public void testDeleteIndex() throws IOException { String indexName = "testIndex"; - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + PineconeClientConfig clientConfig = new PineconeClientConfig() + .withApiKey("testApiKey") + .withEnvironment("testEnvironment"); Call mockCall = mock(Call.class); when(mockCall.execute()).thenReturn(new Response.Builder() @@ -65,7 +93,9 @@ public void testDeleteIndex() throws IOException { @Test public void testCreateIndex() throws IOException { - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + PineconeClientConfig clientConfig = new PineconeClientConfig() + .withApiKey("testApiKey") + .withEnvironment("testEnvironment"); CreateIndexRequest createIndexRequest = new CreateIndexRequest() .withIndexName("test_name").withDimension(3); @@ -99,7 +129,9 @@ public void testCreateIndex() throws IOException { @Test public void testCreateIndexWithNullIndex() { - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + PineconeClientConfig clientConfig = new PineconeClientConfig() + .withApiKey("testApiKey") + .withEnvironment("testEnvironment"); CreateIndexRequest createIndexRequest = new CreateIndexRequest().withDimension(3); OkHttpClient mockClient = mock(OkHttpClient.class); @@ -109,7 +141,9 @@ public void testCreateIndexWithNullIndex() { @Test public void testCreateIndexWithNullDimensions() { - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + PineconeClientConfig clientConfig = new PineconeClientConfig() + .withApiKey("testApiKey") + .withEnvironment("testEnvironment"); CreateIndexRequest createIndexRequest = new CreateIndexRequest().withIndexName("testIndexName"); OkHttpClient mockClient = mock(OkHttpClient.class); @@ -119,7 +153,9 @@ public void testCreateIndexWithNullDimensions() { @Test public void testCreateIndexWithAllFields() throws IOException { - PineconeClientConfig clientConfig = new PineconeClientConfig().withApiKey("testApiKey"); + PineconeClientConfig clientConfig = new PineconeClientConfig() + .withApiKey("testApiKey") + .withEnvironment("testEnvironment"); IndexMetadataConfig metadataConfig = new IndexMetadataConfig(); List indexedItems = Arrays.asList("A", "B", "C", "D"); From 3187111ca76c2f6a47ee2191bcfedbdd04ec3e97 Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Wed, 20 Sep 2023 16:04:39 -0400 Subject: [PATCH 5/7] Improve client error --- src/main/java/io/pinecone/PineconeClient.java | 2 + .../io/pinecone/PineconeClientConfig.java | 2 + .../java/io/pinecone/PineconeConnection.java | 2 + .../io/pinecone/PineconeConnectionConfig.java | 1 + .../PineconeIndexOperationClient.java | 41 ++++++++++++------- .../exceptions/FailedRequestInfo.java | 20 +++++++++ .../pinecone/exceptions/HttpErrorMapper.java | 22 ++++++++++ .../PineconeAlreadyIndexExistsException.java | 12 ++++++ .../PineconeAuthorizationException.java | 12 ++++++ .../PineconeBadRequestException.java | 12 ++++++ .../PineconeConfigurationException.java | 12 ++++++ .../{ => exceptions}/PineconeException.java | 2 +- .../PineconeInternalServerException.java | 12 ++++++ .../exceptions/PineconeNotFoundException.java | 12 ++++++ .../PineconeUnmappedHttpException.java | 12 ++++++ .../PineconeValidationException.java | 2 +- .../io/pinecone/model/CreateIndexRequest.java | 2 +- .../java/io/pinecone/utils/Constants.java | 11 ----- .../PineconeIndexOperationClientTest.java | 31 +------------- 19 files changed, 163 insertions(+), 59 deletions(-) create mode 100644 src/main/java/io/pinecone/exceptions/FailedRequestInfo.java create mode 100644 src/main/java/io/pinecone/exceptions/HttpErrorMapper.java create mode 100644 src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java create mode 100644 src/main/java/io/pinecone/exceptions/PineconeAuthorizationException.java create mode 100644 src/main/java/io/pinecone/exceptions/PineconeBadRequestException.java create mode 100644 src/main/java/io/pinecone/exceptions/PineconeConfigurationException.java rename src/main/java/io/pinecone/{ => exceptions}/PineconeException.java (88%) create mode 100644 src/main/java/io/pinecone/exceptions/PineconeInternalServerException.java create mode 100644 src/main/java/io/pinecone/exceptions/PineconeNotFoundException.java create mode 100644 src/main/java/io/pinecone/exceptions/PineconeUnmappedHttpException.java rename src/main/java/io/pinecone/{ => exceptions}/PineconeValidationException.java (89%) delete mode 100644 src/main/java/io/pinecone/utils/Constants.java diff --git a/src/main/java/io/pinecone/PineconeClient.java b/src/main/java/io/pinecone/PineconeClient.java index a149a846..c7536ad2 100644 --- a/src/main/java/io/pinecone/PineconeClient.java +++ b/src/main/java/io/pinecone/PineconeClient.java @@ -1,5 +1,7 @@ package io.pinecone; +import io.pinecone.exceptions.PineconeValidationException; + /** * Top-level client for connecting and making calls to Pinecone services. One instance can * be used to connect to multiple services and shared across threads. diff --git a/src/main/java/io/pinecone/PineconeClientConfig.java b/src/main/java/io/pinecone/PineconeClientConfig.java index 7b9535d4..cf1eba11 100644 --- a/src/main/java/io/pinecone/PineconeClientConfig.java +++ b/src/main/java/io/pinecone/PineconeClientConfig.java @@ -1,5 +1,7 @@ package io.pinecone; +import io.pinecone.exceptions.PineconeValidationException; + import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/src/main/java/io/pinecone/PineconeConnection.java b/src/main/java/io/pinecone/PineconeConnection.java index 65579428..00e5f41b 100644 --- a/src/main/java/io/pinecone/PineconeConnection.java +++ b/src/main/java/io/pinecone/PineconeConnection.java @@ -6,6 +6,8 @@ import io.grpc.netty.NegotiationType; import io.grpc.netty.NettyChannelBuilder; import io.grpc.stub.MetadataUtils; +import io.pinecone.exceptions.PineconeException; +import io.pinecone.exceptions.PineconeValidationException; import io.pinecone.proto.VectorServiceGrpc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/pinecone/PineconeConnectionConfig.java b/src/main/java/io/pinecone/PineconeConnectionConfig.java index 8590079c..de09c6ae 100644 --- a/src/main/java/io/pinecone/PineconeConnectionConfig.java +++ b/src/main/java/io/pinecone/PineconeConnectionConfig.java @@ -1,6 +1,7 @@ package io.pinecone; import io.grpc.ManagedChannel; +import io.pinecone.exceptions.PineconeValidationException; import java.util.function.BiFunction; diff --git a/src/main/java/io/pinecone/PineconeIndexOperationClient.java b/src/main/java/io/pinecone/PineconeIndexOperationClient.java index 2cbb2ae6..25f2c600 100644 --- a/src/main/java/io/pinecone/PineconeIndexOperationClient.java +++ b/src/main/java/io/pinecone/PineconeIndexOperationClient.java @@ -1,14 +1,24 @@ package io.pinecone; +import io.pinecone.exceptions.FailedRequestInfo; +import io.pinecone.exceptions.HttpErrorMapper; +import io.pinecone.exceptions.PineconeConfigurationException; import io.pinecone.model.CreateIndexRequest; import okhttp3.*; + import java.io.IOException; -import static io.pinecone.utils.Constants.*; public class PineconeIndexOperationClient { private final OkHttpClient client; private final PineconeClientConfig clientConfig; private final String url; + public static final String ACCEPT_HEADER = "accept"; + public static final String API_KEY_HEADER_NAME = "Api-Key"; + public static final String BASE_URL_PREFIX = "https://controller."; + public static final String BASE_URL_SUFFIX = ".pinecone.io/databases/"; + public static final String CONTENT_TYPE = "content-type"; + public static final String CONTENT_TYPE_JSON = "application/json"; + public static final String TEXT_PLAIN = "text/plain"; private PineconeIndexOperationClient(PineconeClientConfig clientConfig, OkHttpClient client, String url) { this.client = client; @@ -26,7 +36,7 @@ public PineconeIndexOperationClient(PineconeClientConfig clientConfig) { private static String createUrl(PineconeClientConfig clientConfig) { if (clientConfig.getApiKey() == null || clientConfig.getEnvironment() == null) { - throw new PineconeValidationException("Both API key and environment name are required for index operations."); + throw new PineconeConfigurationException("Both API key and environment name are required for index operations."); } return BASE_URL_PREFIX + clientConfig.getEnvironment() + BASE_URL_SUFFIX; @@ -37,15 +47,11 @@ public void deleteIndex(String indexName) throws IOException { .url(url + indexName) .delete() .addHeader(ACCEPT_HEADER, TEXT_PLAIN) - .addHeader(API_KEY, clientConfig.getApiKey()) + .addHeader(API_KEY_HEADER_NAME, clientConfig.getApiKey()) .build(); try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) { - throw new IOException(response.message()); - } - } finally { - close(client); + handleResponse(response); } } @@ -58,19 +64,24 @@ public void createIndex(CreateIndexRequest createIndexRequest) throws IOExceptio .post(requestBody) .addHeader(ACCEPT_HEADER, TEXT_PLAIN) .addHeader(CONTENT_TYPE, CONTENT_TYPE_JSON) - .addHeader(API_KEY, clientConfig.getApiKey()) + .addHeader(API_KEY_HEADER_NAME, clientConfig.getApiKey()) .build(); try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) { - throw new IOException(response.message()); - } - } finally { - close(client); + handleResponse(response); + } + } + + private void handleResponse(Response response) throws IOException { + if (!response.isSuccessful()) { + int statusCode = response.code(); + String responseBodyString = (response.body() != null) ? response.body().string() : null; + FailedRequestInfo failedRequestInfo = new FailedRequestInfo(statusCode, responseBodyString); + HttpErrorMapper.mapHttpStatusError(failedRequestInfo); } } - public void close(OkHttpClient client) { + public void close() { client.dispatcher().executorService().shutdown(); client.connectionPool().evictAll(); } diff --git a/src/main/java/io/pinecone/exceptions/FailedRequestInfo.java b/src/main/java/io/pinecone/exceptions/FailedRequestInfo.java new file mode 100644 index 00000000..2de2367a --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/FailedRequestInfo.java @@ -0,0 +1,20 @@ +package io.pinecone.exceptions; + +public class FailedRequestInfo { + private final int status; + private final String message; + + public FailedRequestInfo(int status, String message) { + this.status = status; + this.message = message; + } + + public int getStatus() { + return status; + } + + public String getMessage() { + return message; + } +} + diff --git a/src/main/java/io/pinecone/exceptions/HttpErrorMapper.java b/src/main/java/io/pinecone/exceptions/HttpErrorMapper.java new file mode 100644 index 00000000..97213936 --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/HttpErrorMapper.java @@ -0,0 +1,22 @@ +package io.pinecone.exceptions; + +public class HttpErrorMapper { + + public static void mapHttpStatusError(FailedRequestInfo failedRequestInfo) { + int statusCode = failedRequestInfo.getStatus(); + switch (statusCode) { + case 400: + throw new PineconeBadRequestException(failedRequestInfo.getMessage()); + case 401: + throw new PineconeAuthorizationException(failedRequestInfo.getMessage()); + case 404: + throw new PineconeNotFoundException(failedRequestInfo.getMessage()); + case 409: + throw new PineconeAlreadyIndexExistsException(failedRequestInfo.getMessage()); + case 500: + throw new PineconeInternalServerException(failedRequestInfo.getMessage()); + default: + throw new PineconeUnmappedHttpException(failedRequestInfo.getMessage()); + } + } +} diff --git a/src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java b/src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java new file mode 100644 index 00000000..4eb59ec1 --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeAlreadyIndexExistsException extends PineconeException { + + public PineconeAlreadyIndexExistsException(String message) { + super(message); + } + + public PineconeAlreadyIndexExistsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/pinecone/exceptions/PineconeAuthorizationException.java b/src/main/java/io/pinecone/exceptions/PineconeAuthorizationException.java new file mode 100644 index 00000000..6b5e56cd --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeAuthorizationException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeAuthorizationException extends PineconeException { + + public PineconeAuthorizationException(String message) { + super(message); + } + + public PineconeAuthorizationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/pinecone/exceptions/PineconeBadRequestException.java b/src/main/java/io/pinecone/exceptions/PineconeBadRequestException.java new file mode 100644 index 00000000..0eb3a7fe --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeBadRequestException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeBadRequestException extends PineconeException { + + public PineconeBadRequestException(String message) { + super(message); + } + + public PineconeBadRequestException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/src/main/java/io/pinecone/exceptions/PineconeConfigurationException.java b/src/main/java/io/pinecone/exceptions/PineconeConfigurationException.java new file mode 100644 index 00000000..7f5b8972 --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeConfigurationException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeConfigurationException extends PineconeException { + + public PineconeConfigurationException(String message) { + super(message); + } + + public PineconeConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/pinecone/PineconeException.java b/src/main/java/io/pinecone/exceptions/PineconeException.java similarity index 88% rename from src/main/java/io/pinecone/PineconeException.java rename to src/main/java/io/pinecone/exceptions/PineconeException.java index c3b841b5..631f1318 100644 --- a/src/main/java/io/pinecone/PineconeException.java +++ b/src/main/java/io/pinecone/exceptions/PineconeException.java @@ -1,4 +1,4 @@ -package io.pinecone; +package io.pinecone.exceptions; public class PineconeException extends RuntimeException { diff --git a/src/main/java/io/pinecone/exceptions/PineconeInternalServerException.java b/src/main/java/io/pinecone/exceptions/PineconeInternalServerException.java new file mode 100644 index 00000000..bdd1ae20 --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeInternalServerException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeInternalServerException extends PineconeException { + + public PineconeInternalServerException(String message) { + super(message); + } + + public PineconeInternalServerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/pinecone/exceptions/PineconeNotFoundException.java b/src/main/java/io/pinecone/exceptions/PineconeNotFoundException.java new file mode 100644 index 00000000..a28cd36e --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeNotFoundException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeNotFoundException extends PineconeException { + + public PineconeNotFoundException(String message) { + super(message); + } + + public PineconeNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/pinecone/exceptions/PineconeUnmappedHttpException.java b/src/main/java/io/pinecone/exceptions/PineconeUnmappedHttpException.java new file mode 100644 index 00000000..aa65d341 --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeUnmappedHttpException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeUnmappedHttpException extends PineconeException { + + public PineconeUnmappedHttpException(String message) { + super(message); + } + + public PineconeUnmappedHttpException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/pinecone/PineconeValidationException.java b/src/main/java/io/pinecone/exceptions/PineconeValidationException.java similarity index 89% rename from src/main/java/io/pinecone/PineconeValidationException.java rename to src/main/java/io/pinecone/exceptions/PineconeValidationException.java index 6f4ae7f7..d3b776f4 100644 --- a/src/main/java/io/pinecone/PineconeValidationException.java +++ b/src/main/java/io/pinecone/exceptions/PineconeValidationException.java @@ -1,4 +1,4 @@ -package io.pinecone; +package io.pinecone.exceptions; public class PineconeValidationException extends PineconeException { diff --git a/src/main/java/io/pinecone/model/CreateIndexRequest.java b/src/main/java/io/pinecone/model/CreateIndexRequest.java index a7f68769..a92c9eb3 100644 --- a/src/main/java/io/pinecone/model/CreateIndexRequest.java +++ b/src/main/java/io/pinecone/model/CreateIndexRequest.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import io.pinecone.PineconeValidationException; +import io.pinecone.exceptions.PineconeValidationException; public class CreateIndexRequest { private String indexName; diff --git a/src/main/java/io/pinecone/utils/Constants.java b/src/main/java/io/pinecone/utils/Constants.java deleted file mode 100644 index fc455016..00000000 --- a/src/main/java/io/pinecone/utils/Constants.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.pinecone.utils; - -public class Constants { - public static final String ACCEPT_HEADER = "accept"; - public static final String API_KEY = "Api-Key"; - public static final String BASE_URL_PREFIX = "https://controller."; - public static final String BASE_URL_SUFFIX = ".pinecone.io/databases/"; - public static final String CONTENT_TYPE = "content-type"; - public static final String CONTENT_TYPE_JSON = "application/json"; - public static final String TEXT_PLAIN = "text/plain"; -} \ No newline at end of file diff --git a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java index dcbc1a2b..79c28b56 100644 --- a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java +++ b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java @@ -1,5 +1,6 @@ package io.pinecone; +import io.pinecone.exceptions.PineconeValidationException; import io.pinecone.model.CreateIndexRequest; import io.pinecone.model.IndexMetadataConfig; import okhttp3.*; @@ -11,7 +12,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; -import java.util.concurrent.ExecutorService; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @@ -73,22 +73,12 @@ public void testDeleteIndex() throws IOException { .build()); OkHttpClient mockClient = mock(OkHttpClient.class); - Dispatcher mockDispatcher = mock(Dispatcher.class); - ExecutorService mockExecutorService = mock(ExecutorService.class); - ConnectionPool mockConnectionPool = mock(ConnectionPool.class); when(mockClient.newCall(any(Request.class))).thenReturn(mockCall); - when(mockClient.dispatcher()).thenReturn(mockDispatcher); - when(mockClient.dispatcher().executorService()).thenReturn(mockExecutorService); - when(mockClient.connectionPool()).thenReturn(mockConnectionPool); - PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); indexOperationClient.deleteIndex(indexName); verify(mockClient, times(1)).newCall(any(Request.class)); verify(mockCall, times(1)).execute(); - verify(mockDispatcher, times(1)).executorService(); - verify(mockExecutorService, times(1)).shutdown(); - verify(mockConnectionPool, times(1)).evictAll(); } @Test @@ -109,22 +99,12 @@ public void testCreateIndex() throws IOException { .build()); OkHttpClient mockClient = mock(OkHttpClient.class); - Dispatcher mockDispatcher = mock(Dispatcher.class); - ExecutorService mockExecutorService = mock(ExecutorService.class); - ConnectionPool mockConnectionPool = mock(ConnectionPool.class); when(mockClient.newCall(any(Request.class))).thenReturn(mockCall); - when(mockClient.dispatcher()).thenReturn(mockDispatcher); - when(mockClient.dispatcher().executorService()).thenReturn(mockExecutorService); - when(mockClient.connectionPool()).thenReturn(mockConnectionPool); - PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); indexOperationClient.createIndex(createIndexRequest); verify(mockClient, times(1)).newCall(any(Request.class)); verify(mockCall, times(1)).execute(); - verify(mockDispatcher, times(1)).executorService(); - verify(mockExecutorService, times(1)).shutdown(); - verify(mockConnectionPool, times(1)).evictAll(); } @Test @@ -181,21 +161,12 @@ public void testCreateIndexWithAllFields() throws IOException { .build()); OkHttpClient mockClient = mock(OkHttpClient.class); - Dispatcher mockDispatcher = mock(Dispatcher.class); - ExecutorService mockExecutorService = mock(ExecutorService.class); - ConnectionPool mockConnectionPool = mock(ConnectionPool.class); when(mockClient.newCall(any(Request.class))).thenReturn(mockCall); - when(mockClient.dispatcher()).thenReturn(mockDispatcher); - when(mockClient.dispatcher().executorService()).thenReturn(mockExecutorService); - when(mockClient.connectionPool()).thenReturn(mockConnectionPool); PineconeIndexOperationClient indexOperationClient = new PineconeIndexOperationClient(clientConfig, mockClient); indexOperationClient.createIndex(createIndexRequest); verify(mockClient, times(1)).newCall(any(Request.class)); verify(mockCall, times(1)).execute(); - verify(mockDispatcher, times(1)).executorService(); - verify(mockExecutorService, times(1)).shutdown(); - verify(mockConnectionPool, times(1)).evictAll(); } } \ No newline at end of file From 9048bc0e22c17705cf7a4994e60df86229620ec9 Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Wed, 20 Sep 2023 16:37:01 -0400 Subject: [PATCH 6/7] Update unit test --- .../java/io/pinecone/PineconeIndexOperationClientTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java index 79c28b56..b06c4256 100644 --- a/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java +++ b/src/test/java/io/pinecone/PineconeIndexOperationClientTest.java @@ -1,5 +1,6 @@ package io.pinecone; +import io.pinecone.exceptions.PineconeConfigurationException; import io.pinecone.exceptions.PineconeValidationException; import io.pinecone.model.CreateIndexRequest; import io.pinecone.model.IndexMetadataConfig; @@ -36,7 +37,7 @@ public void IndexOpsWithoutApiKey() throws IOException { .withEnvironment("testEnvironment"); OkHttpClient mockClient = mock(OkHttpClient.class); - assertThrows(PineconeValidationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); + assertThrows(PineconeConfigurationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); } @Test @@ -45,7 +46,7 @@ public void IndexOpsWithoutEnvironment() throws IOException { .withApiKey("testApiKey"); OkHttpClient mockClient = mock(OkHttpClient.class); - assertThrows(PineconeValidationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); + assertThrows(PineconeConfigurationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); } @Test @@ -53,7 +54,7 @@ public void IndexOpsWithoutApiKeyAndEnvironment() throws IOException { PineconeClientConfig clientConfig = new PineconeClientConfig(); OkHttpClient mockClient = mock(OkHttpClient.class); - assertThrows(PineconeValidationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); + assertThrows(PineconeConfigurationException.class, () -> new PineconeIndexOperationClient(clientConfig, mockClient)); } @Test From e7638a8e5e9fb3586b31ba7e314f500a074b3f21 Mon Sep 17 00:00:00 2001 From: rohanshah18 Date: Fri, 22 Sep 2023 16:15:54 -0400 Subject: [PATCH 7/7] address code review comments --- .../PineconeIndexOperationClient.java | 38 ++++++++++--------- .../pinecone/exceptions/HttpErrorMapper.java | 2 +- .../PineconeAlreadyExistsException.java | 12 ++++++ .../PineconeAlreadyIndexExistsException.java | 12 ------ 4 files changed, 34 insertions(+), 30 deletions(-) create mode 100644 src/main/java/io/pinecone/exceptions/PineconeAlreadyExistsException.java delete mode 100644 src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java diff --git a/src/main/java/io/pinecone/PineconeIndexOperationClient.java b/src/main/java/io/pinecone/PineconeIndexOperationClient.java index 25f2c600..3d934229 100644 --- a/src/main/java/io/pinecone/PineconeIndexOperationClient.java +++ b/src/main/java/io/pinecone/PineconeIndexOperationClient.java @@ -18,6 +18,9 @@ public class PineconeIndexOperationClient { public static final String BASE_URL_SUFFIX = ".pinecone.io/databases/"; public static final String CONTENT_TYPE = "content-type"; public static final String CONTENT_TYPE_JSON = "application/json"; + public static final String DELETE = "DELETE"; + public static final String EMPTY_RESOURCE_PATH = ""; + public static final String POST = "POST"; public static final String TEXT_PLAIN = "text/plain"; private PineconeIndexOperationClient(PineconeClientConfig clientConfig, OkHttpClient client, String url) { @@ -43,14 +46,7 @@ private static String createUrl(PineconeClientConfig clientConfig) { } public void deleteIndex(String indexName) throws IOException { - Request request = new Request.Builder() - .url(url + indexName) - .delete() - .addHeader(ACCEPT_HEADER, TEXT_PLAIN) - .addHeader(API_KEY_HEADER_NAME, clientConfig.getApiKey()) - .build(); - - try (Response response = client.newCall(request).execute()) { + try (Response response = client.newCall(buildRequest(DELETE, indexName, TEXT_PLAIN,null)).execute()) { handleResponse(response); } } @@ -59,19 +55,27 @@ public void createIndex(CreateIndexRequest createIndexRequest) throws IOExceptio MediaType mediaType = MediaType.parse("application/json; charset=utf-8"); RequestBody requestBody = RequestBody.create(createIndexRequest.toJson(), mediaType); - Request request = new Request.Builder() - .url(url) - .post(requestBody) - .addHeader(ACCEPT_HEADER, TEXT_PLAIN) - .addHeader(CONTENT_TYPE, CONTENT_TYPE_JSON) - .addHeader(API_KEY_HEADER_NAME, clientConfig.getApiKey()) - .build(); - - try (Response response = client.newCall(request).execute()) { + try (Response response = client.newCall(buildRequest(POST, EMPTY_RESOURCE_PATH, TEXT_PLAIN, requestBody)).execute()) { handleResponse(response); } } + private Request buildRequest(String method, String path, String header, RequestBody requestBody) { + Request.Builder builder = new Request.Builder() + .url(url + path) + .addHeader(ACCEPT_HEADER, header) + .addHeader(API_KEY_HEADER_NAME, clientConfig.getApiKey()); + + if (POST.equals(method)) { + builder.post(requestBody); + builder.addHeader(CONTENT_TYPE, CONTENT_TYPE_JSON); + } else if (DELETE.equals(method)) { + builder.delete(); + } + + return builder.build(); + } + private void handleResponse(Response response) throws IOException { if (!response.isSuccessful()) { int statusCode = response.code(); diff --git a/src/main/java/io/pinecone/exceptions/HttpErrorMapper.java b/src/main/java/io/pinecone/exceptions/HttpErrorMapper.java index 97213936..6d702785 100644 --- a/src/main/java/io/pinecone/exceptions/HttpErrorMapper.java +++ b/src/main/java/io/pinecone/exceptions/HttpErrorMapper.java @@ -12,7 +12,7 @@ public static void mapHttpStatusError(FailedRequestInfo failedRequestInfo) { case 404: throw new PineconeNotFoundException(failedRequestInfo.getMessage()); case 409: - throw new PineconeAlreadyIndexExistsException(failedRequestInfo.getMessage()); + throw new PineconeAlreadyExistsException(failedRequestInfo.getMessage()); case 500: throw new PineconeInternalServerException(failedRequestInfo.getMessage()); default: diff --git a/src/main/java/io/pinecone/exceptions/PineconeAlreadyExistsException.java b/src/main/java/io/pinecone/exceptions/PineconeAlreadyExistsException.java new file mode 100644 index 00000000..3b5aa94d --- /dev/null +++ b/src/main/java/io/pinecone/exceptions/PineconeAlreadyExistsException.java @@ -0,0 +1,12 @@ +package io.pinecone.exceptions; + +public class PineconeAlreadyExistsException extends PineconeException { + + public PineconeAlreadyExistsException(String message) { + super(message); + } + + public PineconeAlreadyExistsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java b/src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java deleted file mode 100644 index 4eb59ec1..00000000 --- a/src/main/java/io/pinecone/exceptions/PineconeAlreadyIndexExistsException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.pinecone.exceptions; - -public class PineconeAlreadyIndexExistsException extends PineconeException { - - public PineconeAlreadyIndexExistsException(String message) { - super(message); - } - - public PineconeAlreadyIndexExistsException(String message, Throwable cause) { - super(message, cause); - } -}