Skip to content
6 changes: 6 additions & 0 deletions src/it/java/io/weaviate/integration/CollectionsITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.junit.Test;

import io.weaviate.ConcurrentTest;
import io.weaviate.client6.v1.api.WeaviateApiException;
import io.weaviate.client6.v1.api.WeaviateClient;
import io.weaviate.client6.v1.api.collections.CollectionConfig;
import io.weaviate.client6.v1.api.collections.InvertedIndex;
Expand Down Expand Up @@ -185,4 +186,9 @@ public void testShards() throws IOException {
.extracting(Shard::status)
.containsOnly(wantStatus.name());
}

@Test(expected = WeaviateApiException.class)
public void testInvalidCollectionName() throws IOException {
client.collections.create("^collection@weaviate.io$");
}
}
14 changes: 14 additions & 0 deletions src/it/java/io/weaviate/integration/DataITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.junit.Test;

import io.weaviate.ConcurrentTest;
import io.weaviate.client6.v1.api.WeaviateApiException;
import io.weaviate.client6.v1.api.WeaviateClient;
import io.weaviate.client6.v1.api.collections.Property;
import io.weaviate.client6.v1.api.collections.Vectorizers;
Expand Down Expand Up @@ -393,4 +394,17 @@ public void testReferenceAddMany() throws IOException {
.extracting(WeaviateObject::uuid)
.contains(alpha, bravo, charlie);
}

@Test(expected = WeaviateApiException.class)
public void testDuplicateUuid() throws IOException {
// Arrange
var nsThings = ns("Things");

client.collections.create(nsThings);
var things = client.collections.use(nsThings);
var thing_1 = things.data.insert(Map.of());

// Act
things.data.insert(Map.of(), thing -> thing.uuid(thing_1.uuid()));
}
}
35 changes: 34 additions & 1 deletion src/it/java/io/weaviate/integration/PaginationITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;

Expand All @@ -15,9 +16,11 @@

import io.weaviate.ConcurrentTest;
import io.weaviate.client6.v1.api.WeaviateClient;
import io.weaviate.client6.v1.api.WeaviateException;
import io.weaviate.client6.v1.api.collections.Property;
import io.weaviate.client6.v1.api.collections.WeaviateMetadata;
import io.weaviate.client6.v1.api.collections.WeaviateObject;
import io.weaviate.client6.v1.api.collections.pagination.PaginationException;
import io.weaviate.containers.Container;

public class PaginationITest extends ConcurrentTest {
Expand Down Expand Up @@ -89,7 +92,7 @@ public void testResumePagination() throws IOException {
.reduce((prev, next) -> next).get();

// Act
var remaining = things.paginate(p -> p.resumeFrom(lastId)).stream().count();
var remaining = things.paginate(p -> p.fromCursor(lastId)).stream().count();

// Assert
Assertions.assertThat(remaining).isEqualTo(5);
Expand Down Expand Up @@ -157,4 +160,34 @@ public void testAsyncPaginator() throws IOException, InterruptedException, Execu
.isEqualTo(count);
}
}

@Test(expected = PaginationException.class)
public void testFailedPagination() throws IOException {
var things = client.collections.use("Unknown");
things.paginate().forEach(System.out::println);
}

@Test(expected = PaginationException.class)
public void testFailedAsyncPagination_forEach() throws Throwable {
try (final var async = client.async()) {
var things = async.collections.use("Unknown");
try {
things.paginate().forEach(__ -> System.out.println("called once")).join();
} catch (CompletionException e) {
throw e.getCause(); // CompletableFuture exceptions are always wrapped
}
}
}

@Test(expected = WeaviateException.class)
public void testFailedAsyncPagination_forPage() throws Throwable {
try (final var async = client.async()) {
var things = async.collections.use("Unknown");
try {
things.paginate().forPage(__ -> System.out.println("called once")).join();
} catch (CompletionException e) {
throw e.getCause(); // CompletableFuture exceptions are always wrapped
}
}
}
}
41 changes: 41 additions & 0 deletions src/it/java/io/weaviate/integration/SearchITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;

import org.assertj.core.api.Assertions;
Expand All @@ -16,6 +17,7 @@
import org.junit.rules.TestRule;

import io.weaviate.ConcurrentTest;
import io.weaviate.client6.v1.api.WeaviateApiException;
import io.weaviate.client6.v1.api.WeaviateClient;
import io.weaviate.client6.v1.api.collections.Property;
import io.weaviate.client6.v1.api.collections.Vectorizers;
Expand Down Expand Up @@ -374,4 +376,43 @@ public void testHybrid() throws IOException {
Assertions.assertThat(first.metadata().explainScore())
.as("metadata::explainScore").isNotNull();
}

@Test(expected = WeaviateApiException.class)
public void testBadRequest() throws IOException {
// Arrange
var nsThings = ns("Things");

client.collections.create(nsThings,
collection -> collection
.properties(Property.text("name"))
.vectors(Vectorizers.text2vecContextionary()));

var things = client.collections.use(nsThings);
var balloon = things.data.insert(Map.of("name", "balloon"));

things.query.nearObject(balloon.uuid(), q -> q.limit(-1));
}

@Test(expected = WeaviateApiException.class)
public void testBadRequest_async() throws Throwable {
// Arrange
var nsThings = ns("Things");

try (final var async = client.async()) {
async.collections.create(nsThings,
collection -> collection
.properties(Property.text("name"))
.vectors(Vectorizers.text2vecContextionary()))
.join();

var things = async.collections.use(nsThings);
var balloon = things.data.insert(Map.of("name", "balloon")).join();

try {
things.query.nearObject(balloon.uuid(), q -> q.limit(-1)).join();
} catch (CompletionException e) {
throw e.getCause(); // CompletableFuture exceptions are always wrapped
}
}
}
}
69 changes: 69 additions & 0 deletions src/main/java/io/weaviate/client6/v1/api/WeaviateApiException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.weaviate.client6.v1.api;

/**
* Exception class thrown by client API message when the request's reached the
* server, but the operation did not complete successfully either either due to
* a bad request or a server error.
*/
public class WeaviateApiException extends WeaviateException {
private final String errorMessage;
private final Source source;
private final String endpoint;
private final Integer httpStatusCode;
private final io.grpc.Status.Code grpcStatusCode;

private enum Source {
HTTP, GRPC;
};

public static WeaviateApiException http(String method, String endpoint, int statusCode, String errorMessage) {
return new WeaviateApiException(method, endpoint, statusCode, errorMessage);
}

public static WeaviateApiException gRPC(io.grpc.StatusRuntimeException ex) {
var status = ex.getStatus();
return new WeaviateApiException(status.getCode(), status.getDescription());
}

private WeaviateApiException(io.grpc.Status.Code code, String errorMessage) {
super("%s: %s".formatted(code, errorMessage));
this.source = Source.GRPC;
this.errorMessage = errorMessage;
this.grpcStatusCode = code;
this.endpoint = null;
this.httpStatusCode = null;
}

private WeaviateApiException(String method, String endpoint, int statusCode, String errorMessage) {
super("HTTP %d: %s %s: %s".formatted(statusCode, method, endpoint, errorMessage));
this.source = Source.HTTP;
this.errorMessage = errorMessage;
this.endpoint = endpoint;
this.httpStatusCode = statusCode;
this.grpcStatusCode = null;
}

public boolean isGPRC() {
return source == Source.GRPC;
}

public String grpcStatusCode() {
return grpcStatusCode.toString();
}

public boolean isHTTP() {
return source == Source.HTTP;
}

public String endpoint() {
return endpoint;
}

public Integer httpStatusCode() {
return httpStatusCode;
}

public String getError() {
return errorMessage;
}
}
38 changes: 38 additions & 0 deletions src/main/java/io/weaviate/client6/v1/api/WeaviateException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.weaviate.client6.v1.api;

/**
* WeaviateException is the base class for other library exceptions
* to provide an ergonomic way of handling any Weaviate-related exceptions.
*
* <p>
* Some parts of the API may still throw other standard exceptions, like
* {@link java.io.IOException} or {@link java.lang.IllegalArgumentException},
* which will not be wrapped into a WeaviateException.
*
* <p>
* Usage:
*
* <pre>{@code
* var thigns = client.collections.use("Things");
* try {
* things.paginate(...)
* things.query.bm25(...);
* things.aggregate.overAll(...);
* } catch (WeaviateException e) {
* System.out.println(e);
* }
* }</pre>
*/
public abstract class WeaviateException extends RuntimeException {
public WeaviateException(String message) {
super(message);
}

public WeaviateException(Throwable cause) {
super(cause);
}

public WeaviateException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

import java.util.Collections;

import org.apache.hc.core5.http.HttpStatus;

import io.weaviate.client6.v1.internal.json.JSON;
import io.weaviate.client6.v1.internal.rest.Endpoint;
import io.weaviate.client6.v1.internal.rest.SimpleEndpoint;

public record CreateCollectionRequest(CollectionConfig collection) {
public static final Endpoint<CreateCollectionRequest, CollectionConfig> _ENDPOINT = Endpoint.of(
public static final Endpoint<CreateCollectionRequest, CollectionConfig> _ENDPOINT = new SimpleEndpoint<>(
request -> "POST",
request -> "/schema/",
(gson, request) -> JSON.serialize(request.collection),
request -> Collections.emptyMap(),
code -> code != HttpStatus.SC_SUCCESS,
(gson, response) -> JSON.deserialize(response, CollectionConfig.class));
request -> JSON.serialize(request.collection),
(statusCode, response) -> JSON.deserialize(response, CollectionConfig.class));
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@

import java.util.Collections;

import org.apache.hc.core5.http.HttpStatus;

import io.weaviate.client6.v1.internal.rest.Endpoint;
import io.weaviate.client6.v1.internal.rest.SimpleEndpoint;

public record DeleteCollectionRequest(String collectionName) {
public static final Endpoint<DeleteCollectionRequest, Void> _ENDPOINT = Endpoint.of(
public static final Endpoint<DeleteCollectionRequest, Void> _ENDPOINT = SimpleEndpoint.sideEffect(
request -> "DELETE",
request -> "/schema/" + request.collectionName,
(gson, request) -> null,
request -> Collections.emptyMap(),
status -> status != HttpStatus.SC_SUCCESS,
(gson, resopnse) -> null);
request -> Collections.emptyMap());
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
import java.util.Collections;
import java.util.Optional;

import org.apache.hc.core5.http.HttpStatus;

import io.weaviate.client6.v1.internal.json.JSON;
import io.weaviate.client6.v1.internal.rest.Endpoint;
import io.weaviate.client6.v1.internal.rest.OptionalEndpoint;

public record GetConfigRequest(String collectionName) {
public static final Endpoint<GetConfigRequest, Optional<CollectionConfig>> _ENDPOINT = Endpoint.of(
request -> "GET",
request -> "/schema/" + request.collectionName,
(gson, request) -> null,
request -> Collections.emptyMap(),
code -> code != HttpStatus.SC_SUCCESS,
(gson, response) -> Optional.ofNullable(JSON.deserialize(response, CollectionConfig.class)));
public static final Endpoint<GetConfigRequest, Optional<CollectionConfig>> _ENDPOINT = OptionalEndpoint
.noBodyOptional(
request -> "GET",
request -> "/schema/" + request.collectionName,
request -> Collections.emptyMap(),
(statusCode, response) -> JSON.deserialize(response, CollectionConfig.class));
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@
import java.util.Collections;
import java.util.List;

import org.apache.hc.core5.http.HttpStatus;

import io.weaviate.client6.v1.internal.json.JSON;
import io.weaviate.client6.v1.internal.rest.Endpoint;
import io.weaviate.client6.v1.internal.rest.SimpleEndpoint;

public record ListCollectionRequest() {
public static final Endpoint<ListCollectionRequest, List<CollectionConfig>> _ENDPOINT = Endpoint.of(
public static final Endpoint<ListCollectionRequest, List<CollectionConfig>> _ENDPOINT = SimpleEndpoint.noBody(
request -> "GET",
request -> "/schema",
(gson, request) -> null,
request -> Collections.emptyMap(),
code -> code != HttpStatus.SC_SUCCESS,
(gson, response) -> JSON.deserialize(response, ListCollectionResponse.class).collections());
}
Loading