diff --git a/src/main/java/io/weaviate/client/WeaviateClient.java b/src/main/java/io/weaviate/client/WeaviateClient.java index bb6a71c4..aea5b1bf 100644 --- a/src/main/java/io/weaviate/client/WeaviateClient.java +++ b/src/main/java/io/weaviate/client/WeaviateClient.java @@ -6,6 +6,7 @@ import io.weaviate.client.base.util.DbVersionProvider; import io.weaviate.client.base.util.DbVersionSupport; import io.weaviate.client.base.util.GrpcVersionSupport; +import io.weaviate.client.v1.async.WeaviateAsyncClient; import io.weaviate.client.v1.auth.provider.AccessTokenProvider; import io.weaviate.client.v1.backup.Backup; import io.weaviate.client.v1.batch.Batch; @@ -44,6 +45,10 @@ public WeaviateClient(Config config, HttpClient httpClient, AccessTokenProvider this.tokenProvider = tokenProvider; } + public WeaviateAsyncClient async() { + return new WeaviateAsyncClient(config); + } + public Misc misc() { return new Misc(httpClient, config, dbVersionProvider); } diff --git a/src/main/java/io/weaviate/client/base/AsyncBaseClient.java b/src/main/java/io/weaviate/client/base/AsyncBaseClient.java new file mode 100644 index 00000000..c82dbc13 --- /dev/null +++ b/src/main/java/io/weaviate/client/base/AsyncBaseClient.java @@ -0,0 +1,50 @@ +package io.weaviate.client.base; + +import io.weaviate.client.Config; +import io.weaviate.client.base.http.async.ResponseParser; +import io.weaviate.client.base.http.async.WeaviateResponseConsumer; +import java.util.concurrent.Future; +import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; +import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpHeaders; + +public abstract class AsyncBaseClient { + private final CloseableHttpAsyncClient client; + private final Config config; + private final Serializer serializer; + + public AsyncBaseClient(CloseableHttpAsyncClient client, Config config) { + this.client = client; + this.config = config; + this.serializer = new Serializer(); + } + + protected Future> sendGetRequest(String endpoint, Class classOfT, FutureCallback> callback) { + return sendRequest(endpoint, null, "GET", classOfT, callback, null); + } + + protected Future> sendGetRequest(String endpoint, Class classOfT, FutureCallback> callback, ResponseParser parser) { + return sendRequest(endpoint, null, "GET", classOfT, callback, parser); + } + + protected Future> sendPostRequest(String endpoint, Object payload, Class classOfT, FutureCallback> callback) { + return sendRequest(endpoint, payload, "POST", classOfT, callback, null); + } + + protected Future> sendPostRequest(String endpoint, Object payload, Class classOfT, FutureCallback> callback, ResponseParser parser) { + return sendRequest(endpoint, payload, "POST", classOfT, callback, parser); + } + + private Future> sendRequest(String endpoint, Object payload, String method, Class classOfT, FutureCallback> callback, ResponseParser parser) { + SimpleHttpRequest req = new SimpleHttpRequest(method, String.format("%s%s", config.getBaseURL(), endpoint)); + req.addHeader(HttpHeaders.ACCEPT, "*/*"); + req.addHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + if (payload != null) { + req.setBody(serializer.toJsonString(payload), ContentType.APPLICATION_JSON); + } + return client.execute(SimpleRequestProducer.create(req), new WeaviateResponseConsumer<>(classOfT, parser), callback); + } +} diff --git a/src/main/java/io/weaviate/client/base/AsyncClientResult.java b/src/main/java/io/weaviate/client/base/AsyncClientResult.java new file mode 100644 index 00000000..89a54d9e --- /dev/null +++ b/src/main/java/io/weaviate/client/base/AsyncClientResult.java @@ -0,0 +1,9 @@ +package io.weaviate.client.base; + +import java.util.concurrent.Future; +import org.apache.hc.core5.concurrent.FutureCallback; + +public interface AsyncClientResult { + Future> run(); + Future> run(FutureCallback> callback); +} diff --git a/src/main/java/io/weaviate/client/base/BaseClient.java b/src/main/java/io/weaviate/client/base/BaseClient.java index 2d26793d..cabc57c1 100644 --- a/src/main/java/io/weaviate/client/base/BaseClient.java +++ b/src/main/java/io/weaviate/client/base/BaseClient.java @@ -3,10 +3,7 @@ import io.weaviate.client.Config; import io.weaviate.client.base.http.HttpClient; import io.weaviate.client.base.http.HttpResponse; -import io.weaviate.client.v1.graphql.GraphQL; -import io.weaviate.client.v1.graphql.model.GraphQLResponse; import java.util.Collections; -import java.util.List; public abstract class BaseClient { private final HttpClient client; @@ -53,12 +50,7 @@ private Response sendRequest(String endpoint, Object payload, String method, if (statusCode < 399) { T body = toResponse(responseBody, classOfT); - WeaviateErrorResponse errors = null; - - if (body != null && classOfT.equals(GraphQL.class)) { - errors = getWeaviateGraphQLErrorResponse((GraphQLResponse) body, statusCode); - } - return new Response<>(statusCode, body, errors); + return new Response<>(statusCode, body, null); } WeaviateErrorResponse error = toResponse(responseBody, WeaviateErrorResponse.class); @@ -89,7 +81,7 @@ private HttpResponse sendHttpRequest(String address, String json, String method) } private C toResponse(String response, Class classOfT) { - return serializer.toResponse(response, classOfT); + return serializer.toObject(response, classOfT); } private String toJsonString(Object object) { @@ -100,19 +92,4 @@ private WeaviateErrorResponse getWeaviateErrorResponse(Exception e) { WeaviateErrorMessage error = WeaviateErrorMessage.builder().message(e.getMessage()).throwable(e).build(); return WeaviateErrorResponse.builder().error(Collections.singletonList(error)).build(); } - - /** - * Extract errors from {@link WeaviateErrorResponse} from a GraphQL response body. - * - * @param gql GraphQL response body. - * @param code HTTP status code to pass in the {@link WeaviateErrorResponse}. - * @return Error response to be returned to the caller. - */ - private WeaviateErrorResponse getWeaviateGraphQLErrorResponse(GraphQLResponse gql, int code) { - List messages = gql.errorMessages(); - if (messages == null || messages.isEmpty()) { - return null; - } - return WeaviateErrorResponse.builder().code(code).error(gql.errorMessages()).build(); - } } diff --git a/src/main/java/io/weaviate/client/base/Response.java b/src/main/java/io/weaviate/client/base/Response.java index 43d8f8b6..84171e36 100644 --- a/src/main/java/io/weaviate/client/base/Response.java +++ b/src/main/java/io/weaviate/client/base/Response.java @@ -1,15 +1,40 @@ package io.weaviate.client.base; +import io.weaviate.client.v1.graphql.model.GraphQLResponse; +import java.util.List; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.experimental.FieldDefaults; @Getter -@AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) public class Response { int statusCode; T body; WeaviateErrorResponse errors; + + public Response(int statusCode, T body, WeaviateErrorResponse errors) { + this.statusCode = statusCode; + this.body = body; + if (body instanceof GraphQLResponse) { + this.errors = getWeaviateGraphQLErrorResponse((GraphQLResponse) body, statusCode);; + } else { + this.errors = errors; + } + } + + /** + * Extract errors from {@link WeaviateErrorResponse} from a GraphQL response body. + * + * @param gql GraphQL response body. + * @param code HTTP status code to pass in the {@link WeaviateErrorResponse}. + * @return Error response to be returned to the caller. + */ + private WeaviateErrorResponse getWeaviateGraphQLErrorResponse(GraphQLResponse gql, int code) { + List messages = gql.errorMessages(); + if (messages == null || messages.isEmpty()) { + return null; + } + return WeaviateErrorResponse.builder().code(code).error(gql.errorMessages()).build(); + } } diff --git a/src/main/java/io/weaviate/client/base/Serializer.java b/src/main/java/io/weaviate/client/base/Serializer.java index c27731f9..d228857f 100644 --- a/src/main/java/io/weaviate/client/base/Serializer.java +++ b/src/main/java/io/weaviate/client/base/Serializer.java @@ -10,11 +10,27 @@ public Serializer() { this.gson = new GsonBuilder().disableHtmlEscaping().create(); } - public C toResponse(String response, Class classOfT) { + public T toObject(String response, Class classOfT) { return gson.fromJson(response, classOfT); } public String toJsonString(Object object) { return (object != null) ? gson.toJson(object) : null; } + + public Result toResult(int statusCode, String body, Class classOfT) { + if (statusCode < 399) { + return new Result<>(toRsponse(statusCode, body, classOfT)); + } + return new Result<>(statusCode, null, this.toWeaviateError(body)); + } + + public Response toRsponse(int statusCode, String body, Class classOfT) { + T obj = toObject(body, classOfT); + return new Response<>(statusCode, obj, null); + } + + public WeaviateErrorResponse toWeaviateError(String body) { + return toObject(body, WeaviateErrorResponse.class); + } } diff --git a/src/main/java/io/weaviate/client/base/http/async/AsyncHttpClient.java b/src/main/java/io/weaviate/client/base/http/async/AsyncHttpClient.java new file mode 100644 index 00000000..01392b47 --- /dev/null +++ b/src/main/java/io/weaviate/client/base/http/async/AsyncHttpClient.java @@ -0,0 +1,20 @@ +package io.weaviate.client.base.http.async; + +import io.weaviate.client.Config; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.client5.http.impl.async.HttpAsyncClients; +import org.apache.hc.core5.reactor.IOReactorConfig; +import org.apache.hc.core5.util.Timeout; + +public class AsyncHttpClient { + + public static CloseableHttpAsyncClient create(Config config) { + IOReactorConfig ioReactorConfig = IOReactorConfig.custom() + .setSoTimeout(Timeout.ofSeconds(config.getSocketTimeout())) + .build(); + + return HttpAsyncClients.custom() + .setIOReactorConfig(ioReactorConfig) + .build(); + } +} diff --git a/src/main/java/io/weaviate/client/base/http/async/ResponseParser.java b/src/main/java/io/weaviate/client/base/http/async/ResponseParser.java new file mode 100644 index 00000000..878da54c --- /dev/null +++ b/src/main/java/io/weaviate/client/base/http/async/ResponseParser.java @@ -0,0 +1,16 @@ +package io.weaviate.client.base.http.async; + +import io.weaviate.client.base.Result; +import io.weaviate.client.base.Serializer; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpResponse; + +public abstract class ResponseParser { + protected final Serializer serializer; + + public ResponseParser() { + this.serializer = new Serializer(); + } + + public abstract Result parse(HttpResponse response, String body, ContentType contentType); +} diff --git a/src/main/java/io/weaviate/client/base/http/async/WeaviateResponseConsumer.java b/src/main/java/io/weaviate/client/base/http/async/WeaviateResponseConsumer.java new file mode 100644 index 00000000..e4602692 --- /dev/null +++ b/src/main/java/io/weaviate/client/base/http/async/WeaviateResponseConsumer.java @@ -0,0 +1,38 @@ +package io.weaviate.client.base.http.async; + +import io.weaviate.client.base.Result; +import io.weaviate.client.base.Serializer; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer; +import org.apache.hc.core5.http.nio.support.AbstractAsyncResponseConsumer; +import org.apache.hc.core5.http.protocol.HttpContext; + +public class WeaviateResponseConsumer extends AbstractAsyncResponseConsumer, byte[]> { + private final Serializer serializer; + private final Class classOfT; + private final ResponseParser parser; + + public WeaviateResponseConsumer(Class classOfT, ResponseParser parser) { + super(new BasicAsyncEntityConsumer()); + this.serializer = new Serializer(); + this.classOfT = classOfT; + this.parser = parser; + } + + @Override + protected Result buildResult(HttpResponse response, byte[] entity, ContentType contentType) { + String body = new String(entity, StandardCharsets.UTF_8); + if (this.parser != null) { + return this.parser.parse(response, body, contentType); + } + return serializer.toResult(response.getCode(), body, classOfT); + } + + @Override + public void informationResponse(HttpResponse response, HttpContext context) throws HttpException, IOException { + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/WeaviateAsyncClient.java b/src/main/java/io/weaviate/client/v1/async/WeaviateAsyncClient.java new file mode 100644 index 00000000..2c1bd53b --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/WeaviateAsyncClient.java @@ -0,0 +1,37 @@ +package io.weaviate.client.v1.async; + +import io.weaviate.client.Config; +import io.weaviate.client.base.http.async.AsyncHttpClient; +import io.weaviate.client.v1.async.misc.Misc; +import io.weaviate.client.v1.async.schema.Schema; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.io.CloseMode; + +public class WeaviateAsyncClient implements AutoCloseable { + private final Config config; + private final CloseableHttpAsyncClient client; + + public WeaviateAsyncClient(Config config) { + this.config = config; + this.client = AsyncHttpClient.create(config); + // auto start the client + this.start(); + } + + public Misc misc() { + return new Misc(client, config); + } + + public Schema schema() { + return new Schema(client, config); + } + + private void start() { + this.client.start(); + } + + @Override + public void close() { + this.client.close(CloseMode.GRACEFUL); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/misc/Misc.java b/src/main/java/io/weaviate/client/v1/async/misc/Misc.java new file mode 100644 index 00000000..5db178ea --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/misc/Misc.java @@ -0,0 +1,34 @@ +package io.weaviate.client.v1.async.misc; + +import io.weaviate.client.Config; +import io.weaviate.client.v1.async.misc.api.LiveChecker; +import io.weaviate.client.v1.async.misc.api.MetaGetter; +import io.weaviate.client.v1.async.misc.api.OpenIDConfigGetter; +import io.weaviate.client.v1.async.misc.api.ReadyChecker; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; + +public class Misc { + private final CloseableHttpAsyncClient client; + private final Config config; + + public Misc(CloseableHttpAsyncClient client, Config config) { + this.client = client; + this.config = config; + } + + public MetaGetter metaGetter() { + return new MetaGetter(client, config); + } + + public OpenIDConfigGetter openIDConfigGetter() { + return new OpenIDConfigGetter(client, config); + } + + public LiveChecker liveChecker() { + return new LiveChecker(client, config); + } + + public ReadyChecker readyChecker() { + return new ReadyChecker(client, config); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/misc/api/LiveChecker.java b/src/main/java/io/weaviate/client/v1/async/misc/api/LiveChecker.java new file mode 100644 index 00000000..9f34d462 --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/misc/api/LiveChecker.java @@ -0,0 +1,40 @@ +package io.weaviate.client.v1.async.misc.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Response; +import io.weaviate.client.base.Result; +import io.weaviate.client.base.http.async.ResponseParser; +import java.util.concurrent.Future; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpResponse; + +public class LiveChecker extends AsyncBaseClient implements AsyncClientResult { + + public LiveChecker(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + @Override + public Future> run() { + return sendRequest(null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendRequest(callback); + } + + private Future> sendRequest(FutureCallback> callback) { + return sendGetRequest("/.well-known/live", Boolean.class, callback, new ResponseParser() { + @Override + public Result parse(HttpResponse response, String body, ContentType contentType) { + Response resp = this.serializer.toRsponse(response.getCode(), body, String.class); + return new Result<>(resp.getStatusCode(), resp.getStatusCode() == 200, resp.getErrors()); + } + }); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/misc/api/MetaGetter.java b/src/main/java/io/weaviate/client/v1/async/misc/api/MetaGetter.java new file mode 100644 index 00000000..33ae77fd --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/misc/api/MetaGetter.java @@ -0,0 +1,27 @@ +package io.weaviate.client.v1.async.misc.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Result; +import io.weaviate.client.v1.misc.model.Meta; +import java.util.concurrent.Future; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; + +public class MetaGetter extends AsyncBaseClient implements AsyncClientResult { + + public MetaGetter(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + @Override + public Future> run() { + return sendGetRequest("/meta", Meta.class, null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendGetRequest("/meta", Meta.class, callback); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/misc/api/OpenIDConfigGetter.java b/src/main/java/io/weaviate/client/v1/async/misc/api/OpenIDConfigGetter.java new file mode 100644 index 00000000..381d5d7f --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/misc/api/OpenIDConfigGetter.java @@ -0,0 +1,27 @@ +package io.weaviate.client.v1.async.misc.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Result; +import io.weaviate.client.v1.misc.model.OpenIDConfiguration; +import java.util.concurrent.Future; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; + +public class OpenIDConfigGetter extends AsyncBaseClient implements AsyncClientResult { + + public OpenIDConfigGetter(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + @Override + public Future> run() { + return sendGetRequest("/meta", OpenIDConfiguration.class, null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendGetRequest("/meta", OpenIDConfiguration.class, callback); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/misc/api/ReadyChecker.java b/src/main/java/io/weaviate/client/v1/async/misc/api/ReadyChecker.java new file mode 100644 index 00000000..0bace8c5 --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/misc/api/ReadyChecker.java @@ -0,0 +1,40 @@ +package io.weaviate.client.v1.async.misc.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Response; +import io.weaviate.client.base.Result; +import io.weaviate.client.base.http.async.ResponseParser; +import java.util.concurrent.Future; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpResponse; + +public class ReadyChecker extends AsyncBaseClient implements AsyncClientResult { + + public ReadyChecker(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + @Override + public Future> run() { + return sendRequest(null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendRequest(callback); + } + + private Future> sendRequest(FutureCallback> callback) { + return sendGetRequest("/.well-known/ready", Boolean.class, callback, new ResponseParser() { + @Override + public Result parse(HttpResponse response, String body, ContentType contentType) { + Response resp = this.serializer.toRsponse(response.getCode(), body, String.class); + return new Result<>(resp.getStatusCode(), resp.getStatusCode() == 200, resp.getErrors()); + } + }); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/schema/Schema.java b/src/main/java/io/weaviate/client/v1/async/schema/Schema.java new file mode 100644 index 00000000..5c49996c --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/schema/Schema.java @@ -0,0 +1,34 @@ +package io.weaviate.client.v1.async.schema; + +import io.weaviate.client.Config; +import io.weaviate.client.v1.async.schema.api.ClassCreator; +import io.weaviate.client.v1.async.schema.api.ClassExists; +import io.weaviate.client.v1.async.schema.api.ClassGetter; +import io.weaviate.client.v1.async.schema.api.SchemaGetter; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; + +public class Schema { + private final CloseableHttpAsyncClient client; + private final Config config; + + public Schema(CloseableHttpAsyncClient client, Config config) { + this.client = client; + this.config = config; + } + + public SchemaGetter getter() { + return new SchemaGetter(client, config); + } + + public ClassGetter classGetter() { + return new ClassGetter(client, config); + } + + public ClassExists exists() { + return new ClassExists(client, config); + } + + public ClassCreator classCreator() { + return new ClassCreator(client, config); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/schema/api/ClassCreator.java b/src/main/java/io/weaviate/client/v1/async/schema/api/ClassCreator.java new file mode 100644 index 00000000..12c7637e --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/schema/api/ClassCreator.java @@ -0,0 +1,48 @@ +package io.weaviate.client.v1.async.schema.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Response; +import io.weaviate.client.base.Result; +import io.weaviate.client.base.http.async.ResponseParser; +import io.weaviate.client.v1.schema.model.WeaviateClass; +import java.util.concurrent.Future; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpResponse; + +public class ClassCreator extends AsyncBaseClient implements AsyncClientResult { + + private WeaviateClass clazz; + + public ClassCreator(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + public ClassCreator withClass(WeaviateClass clazz) { + this.clazz = clazz; + return this; + } + + @Override + public Future> run() { + return sendRequest(null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendRequest(callback); + } + + private Future> sendRequest(FutureCallback> callback) { + return sendPostRequest("/schema", clazz, Boolean.class, callback, new ResponseParser() { + @Override + public Result parse(HttpResponse response, String body, ContentType contentType) { + Response resp = this.serializer.toRsponse(response.getCode(), body, WeaviateClass.class); + return new Result<>(resp.getStatusCode(), resp.getStatusCode() == 200, resp.getErrors()); + } + }); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/schema/api/ClassExists.java b/src/main/java/io/weaviate/client/v1/async/schema/api/ClassExists.java new file mode 100644 index 00000000..736d37a4 --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/schema/api/ClassExists.java @@ -0,0 +1,66 @@ +package io.weaviate.client.v1.async.schema.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Result; +import io.weaviate.client.base.WeaviateError; +import io.weaviate.client.base.WeaviateErrorMessage; +import io.weaviate.client.base.WeaviateErrorResponse; +import io.weaviate.client.base.http.async.ResponseParser; +import io.weaviate.client.v1.schema.model.WeaviateClass; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.HttpStatus; + +public class ClassExists extends AsyncBaseClient implements AsyncClientResult { + private String className; + + public ClassExists(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + public ClassExists withClassName(String className) { + this.className = className; + return this; + } + + @Override + public Future> run() { + return sendRequest(null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendRequest(callback); + } + + private Future> sendRequest(FutureCallback> callback) { + if (StringUtils.isEmpty(this.className)) { + WeaviateErrorMessage errorMessage = WeaviateErrorMessage.builder() + .message("classname cannot be empty").build(); + WeaviateErrorResponse errors = WeaviateErrorResponse.builder() + .error(Stream.of(errorMessage).collect(Collectors.toList())).build(); + return CompletableFuture.completedFuture(new Result<>(500, null, errors)); + } + String path = String.format("/schema/%s", this.className); + return sendGetRequest(path, Boolean.class, callback, new ResponseParser() { + @Override + public Result parse(HttpResponse response, String body, ContentType contentType) { + Result getterClass = this.serializer.toResult(response.getCode(), body, WeaviateClass.class); + if (getterClass.hasErrors()) { + WeaviateError error = getterClass.getError(); + return new Result<>(error.getStatusCode(), null, WeaviateErrorResponse.builder().error(error.getMessages()).build()); + } + return new Result<>(HttpStatus.SC_OK, getterClass.getResult() != null, null); + } + }); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/schema/api/ClassGetter.java b/src/main/java/io/weaviate/client/v1/async/schema/api/ClassGetter.java new file mode 100644 index 00000000..bd804813 --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/schema/api/ClassGetter.java @@ -0,0 +1,52 @@ +package io.weaviate.client.v1.async.schema.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Result; +import io.weaviate.client.base.WeaviateErrorMessage; +import io.weaviate.client.base.WeaviateErrorResponse; +import io.weaviate.client.v1.schema.model.WeaviateClass; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; + +public class ClassGetter extends AsyncBaseClient implements AsyncClientResult { + + private String className; + + public ClassGetter(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + public ClassGetter withClassName(String className) { + this.className = className; + return this; + } + + @Override + public Future> run() { + return sendRequest(null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendRequest(callback); + } + + private Future> sendRequest(FutureCallback> callback) { + if (StringUtils.isEmpty(this.className)) { + WeaviateErrorMessage errorMessage = WeaviateErrorMessage.builder() + .message("classname cannot be empty").build(); + WeaviateErrorResponse errors = WeaviateErrorResponse.builder() + .error(Stream.of(errorMessage).collect(Collectors.toList())).build(); + return CompletableFuture.completedFuture(new Result<>(500, null, errors)); + } + String path = String.format("/schema/%s", this.className); + return sendGetRequest(path, WeaviateClass.class, callback); + } +} diff --git a/src/main/java/io/weaviate/client/v1/async/schema/api/SchemaGetter.java b/src/main/java/io/weaviate/client/v1/async/schema/api/SchemaGetter.java new file mode 100644 index 00000000..39e8b445 --- /dev/null +++ b/src/main/java/io/weaviate/client/v1/async/schema/api/SchemaGetter.java @@ -0,0 +1,27 @@ +package io.weaviate.client.v1.async.schema.api; + +import io.weaviate.client.Config; +import io.weaviate.client.base.AsyncBaseClient; +import io.weaviate.client.base.AsyncClientResult; +import io.weaviate.client.base.Result; +import io.weaviate.client.v1.schema.model.Schema; +import java.util.concurrent.Future; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.core5.concurrent.FutureCallback; + +public class SchemaGetter extends AsyncBaseClient implements AsyncClientResult { + + public SchemaGetter(CloseableHttpAsyncClient client, Config config) { + super(client, config); + } + + @Override + public Future> run() { + return sendGetRequest("/schema", Schema.class, null); + } + + @Override + public Future> run(FutureCallback> callback) { + return sendGetRequest("/schema", Schema.class, callback); + } +} diff --git a/src/main/java/io/weaviate/client/v1/auth/nimbus/BaseAuth.java b/src/main/java/io/weaviate/client/v1/auth/nimbus/BaseAuth.java index aea3b353..c84d326d 100644 --- a/src/main/java/io/weaviate/client/v1/auth/nimbus/BaseAuth.java +++ b/src/main/java/io/weaviate/client/v1/auth/nimbus/BaseAuth.java @@ -44,7 +44,7 @@ public AuthResponse getIdAndTokenEndpoint(Config config) throws AuthException { log(msg); throw new AuthException(msg); case 200: - OIDCConfig oidcConfig = serializer.toResponse(response.getBody(), OIDCConfig.class); + OIDCConfig oidcConfig = serializer.toObject(response.getBody(), OIDCConfig.class); HttpResponse resp = sendGetRequest(client, oidcConfig.getHref()); if (resp.getStatusCode() != 200) { String errorMessage = String.format("OIDC configuration url %s returned status code %s", oidcConfig.getHref(), resp.getStatusCode()); diff --git a/src/test/java/io/weaviate/client/base/SerializerTest.java b/src/test/java/io/weaviate/client/base/SerializerTest.java index a7f2dd82..6afb1203 100644 --- a/src/test/java/io/weaviate/client/base/SerializerTest.java +++ b/src/test/java/io/weaviate/client/base/SerializerTest.java @@ -7,13 +7,13 @@ public class SerializerTest extends TestCase { @Test - public void testToResponse() { + public void testToObject() { // given Serializer s = new Serializer(); String description = "test äüëö"; String jsonString = "{\"description\":\""+description+"\"}"; // when - TestObj deserialized = s.toResponse(jsonString, TestObj.class); + TestObj deserialized = s.toObject(jsonString, TestObj.class); // then Assert.assertNotNull(deserialized); Assert.assertEquals(description, deserialized.getDescription()); @@ -37,7 +37,7 @@ public void testErrorResponse() { Serializer s = new Serializer(); String jsonString = "{\"error\":[{\"message\":\"get extend: unknown capability: featureProjection\"}]}"; // when - WeaviateErrorResponse deserialized = s.toResponse(jsonString, WeaviateErrorResponse.class); + WeaviateErrorResponse deserialized = s.toObject(jsonString, WeaviateErrorResponse.class); // then Assert.assertNotNull(deserialized); Assert.assertNull(deserialized.getMessage()); @@ -53,7 +53,7 @@ public void testErrorResponseWithNoError() { Serializer s = new Serializer(); String jsonString = "{\"code\":601,\"message\":\"id in body must be of type uuid: \\\"TODO_4\\\"\"}"; // when - WeaviateErrorResponse deserialized = s.toResponse(jsonString, WeaviateErrorResponse.class); + WeaviateErrorResponse deserialized = s.toObject(jsonString, WeaviateErrorResponse.class); // then Assert.assertNotNull(deserialized); Assert.assertNull(deserialized.getError()); diff --git a/src/test/java/io/weaviate/integration/client/async/misc/ClientMiscTest.java b/src/test/java/io/weaviate/integration/client/async/misc/ClientMiscTest.java new file mode 100644 index 00000000..fb3bd06c --- /dev/null +++ b/src/test/java/io/weaviate/integration/client/async/misc/ClientMiscTest.java @@ -0,0 +1,126 @@ +package io.weaviate.integration.client.async.misc; + +import io.weaviate.client.Config; +import io.weaviate.client.v1.async.WeaviateAsyncClient; +import io.weaviate.client.WeaviateClient; +import io.weaviate.client.base.Result; +import io.weaviate.client.v1.misc.model.Meta; +import io.weaviate.integration.client.WeaviateDockerCompose; +import io.weaviate.integration.tests.misc.MiscTestSuite; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import org.apache.hc.core5.concurrent.FutureCallback; +import static org.junit.Assert.assertNull; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; + +public class ClientMiscTest { + + private WeaviateClient client; + + @ClassRule + public static WeaviateDockerCompose compose = new WeaviateDockerCompose(); + + @Before + public void before() { + Config config = new Config("http", compose.getHttpHostAddress()); + client = new WeaviateClient(config); + } + + @Test + public void testMiscLivenessEndpoint() throws ExecutionException, InterruptedException { + try (WeaviateAsyncClient asyncClient = client.async()) { + // perform operations + Future> future = asyncClient.misc().liveChecker().run(); + Result livenessCheck = future.get(); + // assert results + MiscTestSuite.assertLivenessOrReadiness(livenessCheck); + } + } + + @Test + public void testMiscLivenessEndpointWithCallback() throws ExecutionException, InterruptedException { + try (WeaviateAsyncClient asyncClient = client.async()) { + // perform operations + FutureCallback> callback = new FutureCallback>() { + + @Override + public void completed(Result booleanResult) { + MiscTestSuite.assertLivenessOrReadiness(booleanResult); + } + + @Override + public void failed(Exception e) { + assertNull(e); + } + + @Override + public void cancelled() { + } + }; + Future> future = asyncClient.misc().liveChecker().run(callback); + future.get(); + } + } + + @Test + public void testMiscReadinessEndpoint() throws ExecutionException, InterruptedException { + try (WeaviateAsyncClient asyncClient = client.async()) { + // perform operations + Future> future = asyncClient.misc().readyChecker().run(); + Result readinessCheck = future.get(); + // assert results + MiscTestSuite.assertLivenessOrReadiness(readinessCheck); + } + } + + @Test + public void testMiscReadinessEndpointWithCallback() throws ExecutionException, InterruptedException { + try (WeaviateAsyncClient asyncClient = client.async()) { + // perform operations + FutureCallback> callback = new FutureCallback>() { + + @Override + public void completed(Result booleanResult) { + MiscTestSuite.assertLivenessOrReadiness(booleanResult); + } + + @Override + public void failed(Exception e) { + assertNull(e); + } + + @Override + public void cancelled() { + } + }; + Future> future = asyncClient.misc().readyChecker().run(callback); + future.get(); + } + } + + @Test + public void testMiscMetaEndpointWithCallback() throws ExecutionException, InterruptedException { + try (WeaviateAsyncClient asyncClient = client.async()) { + // perform operations + FutureCallback> callback = new FutureCallback>() { + @Override + public void completed(Result result) { + MiscTestSuite.assertMeta(result); + } + + @Override + public void failed(Exception ex) { + assertNull(ex); + } + + @Override + public void cancelled() { + } + }; + Future> future = asyncClient.misc().metaGetter().run(callback); + future.get(); + } + } +} diff --git a/src/test/java/io/weaviate/integration/client/async/schema/ClientSchemaTest.java b/src/test/java/io/weaviate/integration/client/async/schema/ClientSchemaTest.java new file mode 100644 index 00000000..3e11cfa4 --- /dev/null +++ b/src/test/java/io/weaviate/integration/client/async/schema/ClientSchemaTest.java @@ -0,0 +1,62 @@ +package io.weaviate.integration.client.async.schema; + +import io.weaviate.client.Config; +import io.weaviate.client.WeaviateClient; +import io.weaviate.client.base.Result; +import io.weaviate.client.v1.async.WeaviateAsyncClient; +import io.weaviate.client.v1.schema.model.Schema; +import io.weaviate.client.v1.schema.model.WeaviateClass; +import io.weaviate.integration.client.WeaviateDockerCompose; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; + +public class ClientSchemaTest { + private WeaviateClient client; + + @ClassRule + public static WeaviateDockerCompose compose = new WeaviateDockerCompose(); + + @Before + public void before() { + String httpHost = compose.getHttpHostAddress(); + Config config = new Config("http", httpHost); + + client = new WeaviateClient(config); + } + + @Test + public void testSchemaCreateBandClass() throws ExecutionException, InterruptedException { + try (WeaviateAsyncClient asyncClient = client.async()) { + // given + WeaviateClass clazz = WeaviateClass.builder() + .className("Band") + .description("Band that plays and produces music") + .vectorIndexType("hnsw") + .vectorizer("text2vec-contextionary") + .build(); + // when + Future> createStatusFuture = asyncClient.schema().classCreator().withClass(clazz).run(); + Result createStatus = createStatusFuture.get(); + Future> schemaFuture = asyncClient.schema().getter().run(); + Result schema = schemaFuture.get(); + + // then + assertNotNull(createStatus); + assertTrue(createStatus.getResult()); + assertNotNull(schema); + assertNotNull(schema.getResult()); + assertEquals(1, schema.getResult().getClasses().size()); + + WeaviateClass resultClass = schema.getResult().getClasses().get(0); + assertEquals(clazz.getClassName(), resultClass.getClassName()); + assertEquals(clazz.getDescription(), resultClass.getDescription()); + } + } +} diff --git a/src/test/java/io/weaviate/integration/client/graphql/ClientGraphQLTest.java b/src/test/java/io/weaviate/integration/client/graphql/ClientGraphQLTest.java index 3325c896..10527f91 100644 --- a/src/test/java/io/weaviate/integration/client/graphql/ClientGraphQLTest.java +++ b/src/test/java/io/weaviate/integration/client/graphql/ClientGraphQLTest.java @@ -1707,7 +1707,7 @@ private void checkGroupElements(List expected, List actual) private List getGroups(List> result) { Serializer serializer = new Serializer(); String jsonString = serializer.toJsonString(result); - AdditionalGroupByAdditional[] response = serializer.toResponse(jsonString, AdditionalGroupByAdditional[].class); + AdditionalGroupByAdditional[] response = serializer.toObject(jsonString, AdditionalGroupByAdditional[].class); assertThat(response).isNotNull().hasSize(3); return Arrays.stream(response) .map(AdditionalGroupByAdditional::get_additional) diff --git a/src/test/java/io/weaviate/integration/client/misc/ClientMiscTest.java b/src/test/java/io/weaviate/integration/client/misc/ClientMiscTest.java index 4eb33bad..ecaeef01 100644 --- a/src/test/java/io/weaviate/integration/client/misc/ClientMiscTest.java +++ b/src/test/java/io/weaviate/integration/client/misc/ClientMiscTest.java @@ -1,19 +1,14 @@ package io.weaviate.integration.client.misc; -import io.weaviate.integration.client.WeaviateDockerCompose; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; import io.weaviate.client.Config; import io.weaviate.client.WeaviateClient; import io.weaviate.client.base.Result; import io.weaviate.client.v1.misc.model.Meta; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static io.weaviate.integration.client.WeaviateVersion.EXPECTED_WEAVIATE_VERSION; +import io.weaviate.integration.client.WeaviateDockerCompose; +import io.weaviate.integration.tests.misc.MiscTestSuite; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; public class ClientMiscTest { @@ -33,8 +28,7 @@ public void testMiscLivenessEndpoint() { // when Result livenessCheck = client.misc().liveChecker().run(); // then - assertNotNull(livenessCheck); - assertTrue(livenessCheck.getResult()); + MiscTestSuite.assertLivenessOrReadiness(livenessCheck); } @Test @@ -42,8 +36,7 @@ public void testMiscReadinessEndpoint() { // when Result readinessCheck = client.misc().readyChecker().run(); // then - assertNotNull(readinessCheck); - assertTrue(readinessCheck.getResult()); + MiscTestSuite.assertLivenessOrReadiness(readinessCheck); } @Test @@ -51,12 +44,6 @@ public void testMiscMetaEndpoint() { // when Result meta = client.misc().metaGetter().run(); // then - assertNotNull(meta); - assertNull(meta.getError()); - assertEquals("http://[::]:8080", meta.getResult().getHostname()); - assertEquals(EXPECTED_WEAVIATE_VERSION, meta.getResult().getVersion()); - assertEquals("{backup-filesystem={backupsPath=/tmp/backups}, " + - "generative-openai={documentationHref=https://platform.openai.com/docs/api-reference/completions, name=Generative Search - OpenAI}, " + - "text2vec-contextionary={version=en0.16.0-v1.2.1, wordCount=818072.0}}", meta.getResult().getModules().toString()); + MiscTestSuite.assertMeta(meta); } } diff --git a/src/test/java/io/weaviate/integration/tests/misc/MiscTestSuite.java b/src/test/java/io/weaviate/integration/tests/misc/MiscTestSuite.java new file mode 100644 index 00000000..4cc97d2a --- /dev/null +++ b/src/test/java/io/weaviate/integration/tests/misc/MiscTestSuite.java @@ -0,0 +1,26 @@ +package io.weaviate.integration.tests.misc; + +import io.weaviate.client.base.Result; +import io.weaviate.client.v1.misc.model.Meta; +import static io.weaviate.integration.client.WeaviateVersion.EXPECTED_WEAVIATE_VERSION; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class MiscTestSuite { + public static void assertLivenessOrReadiness(Result result) { + assertNotNull(result); + assertTrue(result.getResult()); + } + + public static void assertMeta(Result meta) { + assertNotNull(meta); + assertNull(meta.getError()); + assertEquals("http://[::]:8080", meta.getResult().getHostname()); + assertEquals(EXPECTED_WEAVIATE_VERSION, meta.getResult().getVersion()); + assertEquals("{backup-filesystem={backupsPath=/tmp/backups}, " + + "generative-openai={documentationHref=https://platform.openai.com/docs/api-reference/completions, name=Generative Search - OpenAI}, " + + "text2vec-contextionary={version=en0.16.0-v1.2.1, wordCount=818072.0}}", meta.getResult().getModules().toString()); + } +} diff --git a/src/test/java/io/weaviate/integration/tests/schema/SchemaTestSuite.java b/src/test/java/io/weaviate/integration/tests/schema/SchemaTestSuite.java new file mode 100644 index 00000000..ee991f34 --- /dev/null +++ b/src/test/java/io/weaviate/integration/tests/schema/SchemaTestSuite.java @@ -0,0 +1,4 @@ +package io.weaviate.integration.tests.schema; + +public class SchemaTestSuite { +}