of(String collectionName, String uuid,
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPage.java b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPage.java
index 54f45f945..b4deb0bf2 100644
--- a/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPage.java
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPage.java
@@ -40,6 +40,15 @@ public boolean isEmpty() {
return this.currentPage.isEmpty();
}
+ /**
+ * Fetch an {@link AsyncPage} containing the next {@code pageSize} results
+ * and advance the cursor.
+ *
+ *
+ * The returned stage may complete exceptionally in case the underlying
+ * query fails. Callers are advised to use exception-aware
+ * {@link CompletableFuture#handle} to process page results.
+ */
public CompletableFuture> fetchNextPage() {
return fetch.apply(cursor, pageSize)
.thenApply(nextPage -> {
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPaginator.java b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPaginator.java
index 308a0327e..771b6e4b6 100644
--- a/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPaginator.java
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/AsyncPaginator.java
@@ -31,9 +31,16 @@ public AsyncPaginator(Builder builder) {
var rs = new AsyncPage(
cursor,
pageSize,
- (after, limit) -> {
- var fn = ObjectBuilder.partial(queryOptions, q -> q.after(after).limit(limit));
- return this.query.fetchObjects(fn).thenApply(QueryResponse::objects);
+ (cursor, pageSize) -> {
+ var fn = ObjectBuilder.partial(queryOptions, q -> q.after(cursor).limit(pageSize));
+ return this.query.fetchObjects(fn)
+ .handle((response, ex) -> {
+ if (ex != null) {
+ throw PaginationException.after(cursor, pageSize, ex);
+ }
+ return response;
+ })
+ .thenApply(QueryResponse::objects);
});
this.resultSet = builder.prefetch ? rs.fetchNextPage() : CompletableFuture.completedFuture(rs);
@@ -51,17 +58,17 @@ public CompletableFuture forPage(Consumer, CompletableFuture> processEachAndAdvance(
+ private static Function, CompletableFuture> processEachAndAdvance(
Consumer> action) {
return processAndAdvanceFunc(rs -> rs.forEach(action));
}
- public Function, CompletableFuture> processPageAndAdvance(
+ private static Function, CompletableFuture> processPageAndAdvance(
Consumer>> action) {
return processAndAdvanceFunc(rs -> action.accept(rs.items()));
}
- public Function, CompletableFuture> processAndAdvanceFunc(
+ private static Function, CompletableFuture> processAndAdvanceFunc(
Consumer> action) {
return rs -> {
// Empty result set means there were no more objects to fetch.
@@ -105,11 +112,17 @@ public Builder pageSize(int pageSize) {
return this;
}
- public Builder resumeFrom(String uuid) {
+ /** Set a cursor (object UUID) to start pagination from. */
+ public Builder fromCursor(String uuid) {
this.cursor = uuid;
return this;
}
+ /**
+ * When prefetch is enabled, the first page is retrieved before any of the
+ * terminating methods ({@link AsyncPaginator#forEach},
+ * {@link AsyncPaginator#forPage}) are called on the paginator.
+ */
public Builder prefetch(boolean enable) {
this.prefetch = enable;
return this;
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/pagination/PaginationException.java b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/PaginationException.java
new file mode 100644
index 000000000..27a119f59
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/PaginationException.java
@@ -0,0 +1,33 @@
+package io.weaviate.client6.v1.api.collections.pagination;
+
+import io.weaviate.client6.v1.api.WeaviateException;
+
+/**
+ * WeaviatePaginationException is thrown then the client encouters an exception
+ * while fetching the next page. This exception preserves the original exception
+ * (see {@link #getCause} and the information about the last cursor and page
+ * size used (see {@link #cursor()} and {@link #pageSize()} respectively).
+ */
+public class PaginationException extends WeaviateException {
+ private final String cursor;
+ private final int pageSize;
+
+ public static PaginationException after(String cursor, int pageSize, Throwable cause) {
+ return new PaginationException(cursor, pageSize, cause);
+ }
+
+ private PaginationException(String cursor, int pageSize, Throwable cause) {
+ super("fetch next page, page_size=%d cursor=%s".formatted(pageSize, cursor), cause);
+ this.cursor = cursor;
+ this.pageSize = pageSize;
+ }
+
+ /** A null-cursor means the error happened while fetching the first page. */
+ public String cursor() {
+ return cursor;
+ }
+
+ public int pageSize() {
+ return pageSize;
+ }
+}
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/pagination/Paginator.java b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/Paginator.java
index 213448e34..04b10bf11 100644
--- a/src/main/java/io/weaviate/client6/v1/api/collections/pagination/Paginator.java
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/pagination/Paginator.java
@@ -37,7 +37,11 @@ public Spliterator> spliterat
return new CursorSpliterator(cursor, pageSize,
(after, limit) -> {
var fn = ObjectBuilder.partial(queryOptions, q -> q.after(after).limit(limit));
- return query.fetchObjects(fn).objects();
+ try {
+ return query.fetchObjects(fn).objects();
+ } catch (Exception e) {
+ throw PaginationException.after(cursor, pageSize, e);
+ }
});
}
@@ -75,7 +79,8 @@ public Builder pageSize(int pageSize) {
return this;
}
- public Builder resumeFrom(String uuid) {
+ /** Set a cursor (object UUID) to start pagination from. */
+ public Builder fromCursor(String uuid) {
this.cursor = uuid;
return this;
}
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java
index 440ce7a31..60fc5b87b 100644
--- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java
@@ -13,7 +13,6 @@
import io.weaviate.client6.v1.internal.ObjectBuilder;
public record Multi2VecClipVectorizer(
- @SerializedName("vectorizeClassName") boolean vectorizeCollectionName,
@SerializedName("inferenceUrl") String inferenceUrl,
@SerializedName("imageFields") List imageFields,
@SerializedName("textFields") List textFields,
@@ -45,7 +44,6 @@ public static Multi2VecClipVectorizer of(Function {
private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX;
- private boolean vectorizeCollectionName = false;
private String inferenceUrl;
private Map imageFields = new HashMap<>();
private Map textFields = new HashMap<>();
@@ -95,11 +92,6 @@ public Builder textField(String field, float weight) {
return this;
}
- public Builder vectorizeCollectionName(boolean enable) {
- this.vectorizeCollectionName = enable;
- return this;
- }
-
public Builder vectorIndex(VectorIndex vectorIndex) {
this.vectorIndex = vectorIndex;
return this;
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java
index aa2550e30..aa53dc085 100644
--- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java
@@ -9,7 +9,15 @@
import io.weaviate.client6.v1.internal.ObjectBuilder;
public record Text2VecContextionaryVectorizer(
- @SerializedName("vectorizeClassName") boolean vectorizeCollectionName,
+ /**
+ * Weaviate defaults to {@code true} if the value is not provided.
+ * Because text2vec-contextionary cannot handle understores in collection names,
+ * this quickly becomes inconvenient.
+ *
+ * To avoid that we send "vectorizeClassName": false all the time
+ * and make it impossible to enable this feature, as it is deprecated.
+ */
+ @Deprecated @SerializedName("vectorizeClassName") boolean vectorizeCollectionName,
VectorIndex vectorIndex) implements Vectorizer {
@Override
@@ -31,18 +39,22 @@ public static Text2VecContextionaryVectorizer of(
return fn.apply(new Builder()).build();
}
+ /**
+ * Canonical constructor always sets {@link #vectorizeCollectionName} to false.
+ */
+ public Text2VecContextionaryVectorizer(boolean vectorizeCollectionName, VectorIndex vectorIndex) {
+ this.vectorizeCollectionName = false;
+ this.vectorIndex = vectorIndex;
+ }
+
public Text2VecContextionaryVectorizer(Builder builder) {
this(builder.vectorizeCollectionName, builder.vectorIndex);
}
public static class Builder implements ObjectBuilder {
- private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX;
- private boolean vectorizeCollectionName = false;
+ private final boolean vectorizeCollectionName = false;
- public Builder vectorizeCollectionName(boolean enable) {
- this.vectorizeCollectionName = enable;
- return this;
- }
+ private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX;
public Builder vectorIndex(VectorIndex vectorIndex) {
this.vectorIndex = vectorIndex;
diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java
index 5d50ade0b..a8c9e7bd3 100644
--- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java
+++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java
@@ -9,7 +9,6 @@
import io.weaviate.client6.v1.internal.ObjectBuilder;
public record Text2VecWeaviateVectorizer(
- @SerializedName("vectorizeClassName") boolean vectorizeCollectionName,
@SerializedName("baseUrl") String inferenceUrl,
@SerializedName("dimensions") Integer dimensions,
@SerializedName("model") String model,
@@ -34,7 +33,7 @@ public static Text2VecWeaviateVectorizer of(Function {
private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX;
- private boolean vectorizeCollectionName = false;
private String inferenceUrl;
private Integer dimensions;
private String model;
- public Builder vectorizeCollectionName(boolean enable) {
- this.vectorizeCollectionName = enable;
- return this;
- }
-
public Builder inferenceUrl(String inferenceUrl) {
this.inferenceUrl = inferenceUrl;
return this;
diff --git a/src/main/java/io/weaviate/client6/v1/internal/Debug.java b/src/main/java/io/weaviate/client6/v1/internal/Debug.java
new file mode 100644
index 000000000..8d3702d61
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/internal/Debug.java
@@ -0,0 +1,27 @@
+package io.weaviate.client6.v1.internal;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.MessageOrBuilder;
+import com.google.protobuf.util.JsonFormat;
+
+/** Debug utilities. */
+public final class Debug {
+ public static final void printProto(Object proto) {
+ System.out.println(proto2json((MessageOrBuilder) proto));
+ }
+
+ public static final void printProto(Object proto, String message, Object... args) {
+ System.out.println(message.formatted(args) + ": " + proto2json((MessageOrBuilder) proto));
+ }
+
+ private static final String proto2json(MessageOrBuilder proto) {
+ String out;
+ try {
+ out = JsonFormat.printer().print(proto);
+ } catch (InvalidProtocolBufferException e) {
+ out = e.getMessage();
+ }
+
+ return out;
+ }
+}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java b/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java
index 75893b06b..7655db36a 100644
--- a/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java
+++ b/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java
@@ -10,10 +10,12 @@
import com.google.common.util.concurrent.ListenableFuture;
import io.grpc.ManagedChannel;
+import io.grpc.StatusRuntimeException;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.grpc.stub.MetadataUtils;
+import io.weaviate.client6.v1.api.WeaviateApiException;
import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateGrpc;
import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateGrpc.WeaviateBlockingStub;
import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateGrpc.WeaviateFutureStub;
@@ -48,8 +50,12 @@ public ResponseT performRequest(RequestT
Rpc rpc) {
var message = rpc.marshal(request);
var method = rpc.method();
- var reply = method.apply(blockingStub, message);
- return rpc.unmarshal(reply);
+ try {
+ var reply = method.apply(blockingStub, message);
+ return rpc.unmarshal(reply);
+ } catch (io.grpc.StatusRuntimeException e) {
+ throw WeaviateApiException.gRPC(e);
+ }
}
@Override
@@ -76,6 +82,10 @@ public void onSuccess(T result) {
@Override
public void onFailure(Throwable t) {
+ if (t instanceof StatusRuntimeException e) {
+ completable.completeExceptionally(WeaviateApiException.gRPC(e));
+ return;
+ }
completable.completeExceptionally(t);
}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/BooleanEndpoint.java b/src/main/java/io/weaviate/client6/v1/internal/rest/BooleanEndpoint.java
new file mode 100644
index 000000000..b0b20665e
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/BooleanEndpoint.java
@@ -0,0 +1,24 @@
+package io.weaviate.client6.v1.internal.rest;
+
+import java.util.Map;
+import java.util.function.Function;
+
+public class BooleanEndpoint extends EndpointBase {
+
+ public BooleanEndpoint(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters,
+ Function body) {
+ super(method, requestUrl, queryParameters, body);
+ }
+
+ @Override
+ public boolean isError(int statusCode) {
+ return statusCode != 404 && super.isError(statusCode);
+ }
+
+ public boolean getResult(int statusCode) {
+ return statusCode < 400;
+ }
+}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java b/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java
index 396eb3b1f..bcd5a07ab 100644
--- a/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java
@@ -20,22 +20,20 @@
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.io.CloseMode;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import io.weaviate.client6.v1.api.WeaviateApiException;
public class DefaultRestTransport implements RestTransport {
private final CloseableHttpClient httpClient;
private final CloseableHttpAsyncClient httpClientAsync;
private final RestTransportOptions transportOptions;
- // TODO: retire
- private static final Gson gson = new GsonBuilder().create();
-
public DefaultRestTransport(RestTransportOptions transportOptions) {
this.transportOptions = transportOptions;
@@ -78,18 +76,38 @@ public DefaultRestTransport(RestTransportOptions transportOptions) {
}
@Override
- public ResponseT performRequest(RequestT request, Endpoint endpoint)
+ public ResponseT performRequest(RequestT request,
+ Endpoint endpoint)
throws IOException {
var req = prepareClassicRequest(request, endpoint);
- // FIXME: we need to differentiate between "no body" and "soumething's wrong"
- return this.httpClient.execute(req,
- response -> response.getEntity() != null
- ? endpoint.deserializeResponse(gson, EntityUtils.toString(response.getEntity()))
- : null);
+ return this.httpClient.execute(req, r -> this.handleResponse(endpoint, req.getMethod(), req.getRequestUri(), r));
+ }
+
+ private ClassicHttpRequest prepareClassicRequest(RequestT request,
+ Endpoint endpoint) {
+ var method = endpoint.method(request);
+ var uri = transportOptions.baseUrl() + endpoint.requestUrl(request);
+
+ // TODO: apply options;
+ var req = ClassicRequestBuilder.create(method).setUri(uri);
+ var body = endpoint.body(request);
+ if (body != null) {
+ req.setEntity(body, ContentType.APPLICATION_JSON);
+ }
+ return req.build();
+ }
+
+ private ResponseT handleResponse(Endpoint, ResponseT> endpoint, String method, String url,
+ ClassicHttpResponse httpResponse) throws IOException, ParseException {
+ var statusCode = httpResponse.getCode();
+ var body = httpResponse.getEntity() != null
+ ? EntityUtils.toString(httpResponse.getEntity())
+ : "";
+ return _handleResponse(endpoint, method, url, statusCode, body);
}
@Override
- public CompletableFuture performRequestAsync(RequestT request,
+ public CompletableFuture performRequestAsync(RequestT request,
Endpoint endpoint) {
var req = prepareSimpleRequest(request, endpoint);
@@ -112,18 +130,18 @@ public void cancelled() {
}
});
- // FIXME: we need to differentiate between "no body" and "soumething's wrong"
- return completable.thenApply(r -> r.getBody() != null
- ? endpoint.deserializeResponse(gson, r.getBody().getBodyText())
- : null);
+ return completable
+ .thenApply(r -> (ResponseT) handleResponseAsync(endpoint,
+ req.getMethod(), req.getRequestUri(), r));
}
- private SimpleHttpRequest prepareSimpleRequest(RequestT request, Endpoint endpoint) {
+ private SimpleHttpRequest prepareSimpleRequest(RequestT request,
+ Endpoint endpoint) {
var method = endpoint.method(request);
var uri = transportOptions.baseUrl() + endpoint.requestUrl(request);
// TODO: apply options;
- var body = endpoint.body(gson, request);
+ var body = endpoint.body(request);
var req = SimpleHttpRequest.create(method, uri);
if (body != null) {
req.setBody(body.getBytes(), ContentType.APPLICATION_JSON);
@@ -131,17 +149,36 @@ private SimpleHttpRequest prepareSimpleRequest(RequestT request, Endp
return req;
}
- private ClassicHttpRequest prepareClassicRequest(RequestT request, Endpoint endpoint) {
- var method = endpoint.method(request);
- var uri = transportOptions.baseUrl() + endpoint.requestUrl(request);
+ private ResponseT handleResponseAsync(
+ Endpoint, ResponseT> endpoint,
+ String method, String url,
+ SimpleHttpResponse httpResponse) {
+ var statusCode = httpResponse.getCode();
+ var body = httpResponse.getBody() != null
+ ? httpResponse.getBody().getBodyText()
+ : "";
+ return _handleResponse(endpoint, method, url, statusCode, body);
+ }
- // TODO: apply options;
- var req = ClassicRequestBuilder.create(method).setUri(uri);
- var body = endpoint.body(gson, request);
- if (body != null) {
- req.setEntity(body, ContentType.APPLICATION_JSON);
+ private ResponseT _handleResponse(Endpoint, ResponseT> endpoint, String method, String url,
+ int statusCode, String body) {
+ if (endpoint.isError(statusCode)) {
+ var message = endpoint.deserializeError(statusCode, body);
+ throw WeaviateApiException.http(method, url, statusCode, message);
}
- return req.build();
+
+ if (endpoint instanceof JsonEndpoint json) {
+ @SuppressWarnings("unchecked")
+ ResponseT response = (ResponseT) json.deserializeResponse(statusCode, body);
+ return response;
+ } else if (endpoint instanceof BooleanEndpoint bool) {
+ @SuppressWarnings("unchecked")
+ ResponseT response = (ResponseT) ((Boolean) bool.getResult(statusCode));
+ return response;
+ }
+
+ // TODO: make it a WeaviateTransportException
+ throw new RuntimeException("Unhandled endpoint type " + endpoint.getClass().getSimpleName());
}
@Override
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/Endpoint.java b/src/main/java/io/weaviate/client6/v1/internal/rest/Endpoint.java
index 7c8998a61..52cc37c3b 100644
--- a/src/main/java/io/weaviate/client6/v1/internal/rest/Endpoint.java
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/Endpoint.java
@@ -1,10 +1,6 @@
package io.weaviate.client6.v1.internal.rest;
import java.util.Map;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import com.google.gson.Gson;
public interface Endpoint {
@@ -12,54 +8,12 @@ public interface Endpoint {
String requestUrl(RequestT request);
- // Gson is leaking.
- String body(Gson gson, RequestT request);
+ String body(RequestT request);
Map queryParameters(RequestT request);
/** Should this status code be considered an error? */
- boolean isError(int code);
-
- ResponseT deserializeResponse(Gson gson, String response);
-
- public static Endpoint of(
- Function method,
- Function requestUrl,
- BiFunction body,
- Function> queryParameters,
- Function isError,
- BiFunction deserialize) {
- return new Endpoint() {
-
- @Override
- public String method(RequestT request) {
- return method.apply(request);
- }
-
- @Override
- public String requestUrl(RequestT request) {
- return requestUrl.apply(request);
- }
-
- @Override
- public String body(Gson gson, RequestT request) {
- return body.apply(gson, request);
- }
-
- @Override
- public Map queryParameters(RequestT request) {
- return queryParameters.apply(request);
- }
-
- @Override
- public ResponseT deserializeResponse(Gson gson, String response) {
- return deserialize.apply(gson, response);
- }
+ boolean isError(int statusCode);
- @Override
- public boolean isError(int code) {
- return isError.apply(code);
- }
- };
- }
+ String deserializeError(int statusCode, String responseBody);
}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/EndpointBase.java b/src/main/java/io/weaviate/client6/v1/internal/rest/EndpointBase.java
new file mode 100644
index 000000000..2ebe61d6d
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/EndpointBase.java
@@ -0,0 +1,74 @@
+package io.weaviate.client6.v1.internal.rest;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import com.google.gson.annotations.SerializedName;
+
+import io.weaviate.client6.v1.internal.json.JSON;
+
+public abstract class EndpointBase implements Endpoint {
+ private static final Function, String> NULL_BODY = __ -> null;
+
+ protected final Function method;
+ protected final Function requestUrl;
+ protected final Function body;
+ protected final Function> queryParameters;
+
+ @SuppressWarnings("unchecked")
+ protected static Function nullBody() {
+ return (Function) NULL_BODY;
+ }
+
+ public EndpointBase(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters,
+ Function body) {
+ this.method = method;
+ this.requestUrl = requestUrl;
+ this.body = body;
+ this.queryParameters = queryParameters;
+ }
+
+ @Override
+ public String method(RequestT request) {
+ return method.apply(request);
+ }
+
+ @Override
+ public String requestUrl(RequestT request) {
+ return requestUrl.apply(request);
+ }
+
+ @Override
+ public Map queryParameters(RequestT request) {
+ return queryParameters.apply(request);
+ }
+
+ @Override
+ public String body(RequestT request) {
+ return body.apply(request);
+ }
+
+ @Override
+ public boolean isError(int statusCode) {
+ return statusCode >= 400;
+ }
+
+ @Override
+ public String deserializeError(int statusCode, String responseBody) {
+ var response = JSON.deserialize(responseBody, ErrorResponse.class);
+ if (response.errors.isEmpty()) {
+ return "";
+
+ }
+ return response.errors.get(0).text();
+ }
+
+ static record ErrorResponse(@SerializedName("error") List errors) {
+ private static record ErrorMessage(@SerializedName("message") String text) {
+ }
+ }
+}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/JsonEndpoint.java b/src/main/java/io/weaviate/client6/v1/internal/rest/JsonEndpoint.java
new file mode 100644
index 000000000..9e04896c4
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/JsonEndpoint.java
@@ -0,0 +1,7 @@
+package io.weaviate.client6.v1.internal.rest;
+
+/** An Endpoint which expects a JSON response body. */
+public interface JsonEndpoint
+ extends Endpoint {
+ ResponseT deserializeResponse(int statusCode, String responseBody);
+}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/OptionalEndpoint.java b/src/main/java/io/weaviate/client6/v1/internal/rest/OptionalEndpoint.java
new file mode 100644
index 000000000..0b6052573
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/OptionalEndpoint.java
@@ -0,0 +1,38 @@
+package io.weaviate.client6.v1.internal.rest;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class OptionalEndpoint extends SimpleEndpoint> {
+
+ public static OptionalEndpoint noBodyOptional(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters,
+ BiFunction deserializeResponse) {
+ return new OptionalEndpoint<>(method, requestUrl, queryParameters, nullBody(), deserializeResponse);
+ }
+
+ public OptionalEndpoint(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters,
+ Function body,
+ BiFunction deserializeResponse) {
+ super(method, requestUrl, queryParameters, body, optional(deserializeResponse));
+ }
+
+ private static BiFunction> optional(
+ BiFunction deserializeResponse) {
+ return (statusCode, responseBody) -> statusCode == 404
+ ? Optional.empty()
+ : Optional.ofNullable(deserializeResponse.apply(statusCode, responseBody));
+ }
+
+ @Override
+ public boolean isError(int statusCode) {
+ return statusCode != 404 && super.isError(statusCode);
+ }
+}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/RestTransport.java b/src/main/java/io/weaviate/client6/v1/internal/rest/RestTransport.java
index b20c98fbd..1fc25836b 100644
--- a/src/main/java/io/weaviate/client6/v1/internal/rest/RestTransport.java
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/RestTransport.java
@@ -5,9 +5,10 @@
import java.util.concurrent.CompletableFuture;
public interface RestTransport extends Closeable {
- ResponseT performRequest(RequestT request, Endpoint endpoint)
+ ResponseT performRequest(RequestT request,
+ Endpoint endpoint)
throws IOException;
- CompletableFuture performRequestAsync(RequestT request,
+ CompletableFuture performRequestAsync(RequestT request,
Endpoint endpoint);
}
diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/SimpleEndpoint.java b/src/main/java/io/weaviate/client6/v1/internal/rest/SimpleEndpoint.java
new file mode 100644
index 000000000..9f5c6fa9c
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/internal/rest/SimpleEndpoint.java
@@ -0,0 +1,54 @@
+package io.weaviate.client6.v1.internal.rest;
+
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class SimpleEndpoint extends EndpointBase
+ implements JsonEndpoint {
+ private static final BiFunction NULL_RESPONSE = (__code, __body) -> null;
+
+ private final BiFunction deserializeResponse;
+
+ protected static BiFunction nullResponse() {
+ return NULL_RESPONSE;
+ }
+
+ public static SimpleEndpoint noBody(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters,
+ BiFunction deserializeResponse) {
+ return new SimpleEndpoint<>(method, requestUrl, queryParameters, nullBody(), deserializeResponse);
+ }
+
+ public static SimpleEndpoint sideEffect(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters,
+ Function body) {
+ return new SimpleEndpoint<>(method, requestUrl, queryParameters, body, nullResponse());
+ }
+
+ public static SimpleEndpoint sideEffect(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters) {
+ return new SimpleEndpoint<>(method, requestUrl, queryParameters, nullBody(), nullResponse());
+ }
+
+ public SimpleEndpoint(
+ Function method,
+ Function requestUrl,
+ Function> queryParameters,
+ Function body,
+ BiFunction deserializeResponse) {
+ super(method, requestUrl, queryParameters, body);
+ this.deserializeResponse = deserializeResponse;
+ }
+
+ @Override
+ public ResponseT deserializeResponse(int statusCode, String responseBody) {
+ return deserializeResponse.apply(statusCode, responseBody);
+ }
+}
diff --git a/src/test/java/io/weaviate/client6/v1/api/AuthorizationTest.java b/src/test/java/io/weaviate/client6/v1/api/AuthorizationTest.java
index fcd74fe44..8c4d375e0 100644
--- a/src/test/java/io/weaviate/client6/v1/api/AuthorizationTest.java
+++ b/src/test/java/io/weaviate/client6/v1/api/AuthorizationTest.java
@@ -10,7 +10,7 @@
import org.mockserver.model.HttpRequest;
import io.weaviate.client6.v1.internal.rest.DefaultRestTransport;
-import io.weaviate.client6.v1.internal.rest.Endpoint;
+import io.weaviate.client6.v1.internal.rest.OptionalEndpoint;
import io.weaviate.client6.v1.internal.rest.RestTransportOptions;
public class AuthorizationTest {
@@ -36,13 +36,8 @@ public void testAuthorization_apiKey() throws IOException {
Collections.emptyMap(), Authorization.apiKey("my-api-key"), null);
try (final var restClient = new DefaultRestTransport(transportOptions)) {
- restClient.performRequest(null, Endpoint.of(
- request -> "GET",
- request -> "/",
- (gson, request) -> null,
- request -> null,
- code -> code != 200,
- (gson, response) -> null));
+ restClient.performRequest(null, OptionalEndpoint.noBodyOptional(
+ request -> "GET", request -> "/", request -> null, (code, response) -> null));
}
mockServer.verify(
diff --git a/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java b/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java
index a4b7f5621..56e189f2e 100644
--- a/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java
+++ b/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java
@@ -73,8 +73,7 @@ public static Object[][] testCases() {
Multi2VecClipVectorizer.of(m2v -> m2v
.inferenceUrl("http://example.com")
.imageField("img", 1f)
- .textField("txt", 2f)
- .vectorizeCollectionName(true)),
+ .textField("txt", 2f)),
"""
{
"vectorIndexType": "hnsw",
@@ -82,7 +81,6 @@ public static Object[][] testCases() {
"vectorizer": {
"multi2vec-clip": {
"inferenceUrl": "http://example.com",
- "vectorizeClassName": true,
"imageFields": ["img"],
"textFields": ["txt"],
"weights": {
@@ -96,15 +94,14 @@ public static Object[][] testCases() {
},
{
Vectorizer.class,
- Text2VecContextionaryVectorizer.of(t2v -> t2v
- .vectorizeCollectionName(true)),
+ Text2VecContextionaryVectorizer.of(),
"""
{
"vectorIndexType": "hnsw",
"vectorIndexConfig": {},
"vectorizer": {
"text2vec-contextionary": {
- "vectorizeClassName": true
+ "vectorizeClassName": false
}
}
}
@@ -115,8 +112,7 @@ public static Object[][] testCases() {
Text2VecWeaviateVectorizer.of(t2v -> t2v
.inferenceUrl("http://example.com")
.dimensions(4)
- .model("very-good-model")
- .vectorizeCollectionName(true)),
+ .model("very-good-model")),
"""
{
"vectorIndexType": "hnsw",
@@ -124,7 +120,6 @@ public static Object[][] testCases() {
"vectorizer": {
"text2vec-weaviate": {
"baseUrl": "http://example.com",
- "vectorizeClassName": true,
"dimensions": 4,
"model": "very-good-model"
}
diff --git a/src/test/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransportTest.java b/src/test/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransportTest.java
index ab58ac5be..cf6de61a8 100644
--- a/src/test/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransportTest.java
+++ b/src/test/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransportTest.java
@@ -42,13 +42,8 @@ public void setUp() throws IOException {
@Test
public void testCustomTrustStore_sync() throws IOException {
- transport.performRequest(null, Endpoint.of(
- request -> "GET",
- request -> "/",
- (gson, request) -> null,
- request -> null,
- code -> code != 200,
- (gson, response) -> null));
+ transport.performRequest(null, OptionalEndpoint.noBodyOptional(
+ request -> "GET", request -> "/", request -> null, (code, response) -> null));
mockServer.verify(
HttpRequest.request()
@@ -63,13 +58,8 @@ public void testCustomTrustStore_sync() throws IOException {
@Test
public void testCustomTrustStore_async() throws IOException, ExecutionException, InterruptedException {
- transport.performRequestAsync(null, Endpoint.of(
- request -> "GET",
- request -> "/",
- (gson, request) -> null,
- request -> null,
- code -> code != 200,
- (gson, response) -> null)).get();
+ transport.performRequestAsync(null, OptionalEndpoint.noBodyOptional(
+ request -> "GET", request -> "/", request -> null, (code, response) -> null)).get();
mockServer.verify(
HttpRequest.request()