Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>${grpc-netty-shaded.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
Expand Down
144 changes: 136 additions & 8 deletions src/main/java/io/weaviate/client6/v1/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.util.Map;
import java.util.function.Function;

import javax.net.ssl.TrustManagerFactory;

import io.weaviate.client6.v1.internal.ObjectBuilder;
import io.weaviate.client6.v1.internal.TokenProvider;
import io.weaviate.client6.v1.internal.grpc.GrpcChannelOptions;
Expand All @@ -17,7 +19,8 @@ public record Config(
String grpcHost,
int grpcPort,
Map<String, String> headers,
TokenProvider tokenProvider) {
TokenProvider tokenProvider,
TrustManagerFactory trustManagerFactory) {

public static Config of(Function<Custom, ObjectBuilder<Config>> fn) {
return fn.apply(new Custom()).build();
Expand All @@ -31,15 +34,16 @@ private Config(Builder<?> builder) {
builder.grpcHost,
builder.grpcPort,
builder.headers,
builder.tokenProvider);
builder.tokenProvider,
builder.trustManagerFactory);
}

public RestTransportOptions restTransportOptions() {
return new RestTransportOptions(scheme, httpHost, httpPort, headers, tokenProvider);
RestTransportOptions restTransportOptions() {
return new RestTransportOptions(scheme, httpHost, httpPort, headers, tokenProvider, trustManagerFactory);
}

public GrpcChannelOptions grpcTransportOptions() {
return new GrpcChannelOptions(scheme, grpcHost, grpcPort, headers, tokenProvider);
GrpcChannelOptions grpcTransportOptions() {
return new GrpcChannelOptions(scheme, grpcHost, grpcPort, headers, tokenProvider, trustManagerFactory);
}

private abstract static class Builder<SELF extends Builder<SELF>> implements ObjectBuilder<Config> {
Expand All @@ -50,20 +54,33 @@ private abstract static class Builder<SELF extends Builder<SELF>> implements Obj
protected String grpcHost;
protected int grpcPort;
protected TokenProvider tokenProvider;
protected TrustManagerFactory trustManagerFactory;
protected Map<String, String> headers = new HashMap<>();

/**
* Set URL scheme. Subclasses may increase the visibility of this method to
* {@code public} if using a different scheme is allowed.
*/
@SuppressWarnings("unchecked")
protected SELF scheme(String scheme) {
this.scheme = scheme;
return (SELF) this;
}

/**
* Set port for REST requests. Subclasses may increase the visibility of this
* method to {@code public} if using a different port is allowed.
*/
@SuppressWarnings("unchecked")
protected SELF httpHost(String httpHost) {
this.httpHost = trimScheme(httpHost);
return (SELF) this;
}

/**
* Set port for gRPC requests. Subclasses may increase the visibility of this
* method to {@code public} if using a different port is allowed.
*/
@SuppressWarnings("unchecked")
protected SELF grpcHost(String grpcHost) {
this.grpcHost = trimScheme(grpcHost);
Expand All @@ -75,18 +92,41 @@ private String trimScheme(String url) {
return url.replaceFirst("^https?\\/\\/", "");
}

/**
* Provide a {@link TrustManagerFactory}. Subclasses which support
* secure connection should expose this method.
*/
@SuppressWarnings("unchecked")
protected SELF trustManagerFactory(TrustManagerFactory tmf) {
this.trustManagerFactory = tmf;
return (SELF) this;
}

/**
* Set a single request header. The client does not support header lists,
* so there is no equivalent {@code addHeader} to append to existing header.
* This will be applied both to REST and gRPC requests.
*/
@SuppressWarnings("unchecked")
public SELF setHeader(String key, String value) {
this.headers.put(key, value);
return (SELF) this;
}

/**
* Set multiple request headers.
* This will be applied both to REST and gRPC requests.
*/
@SuppressWarnings("unchecked")
public SELF setHeaders(Map<String, String> headers) {
this.headers = Map.copyOf(headers);
this.headers.putAll(Map.copyOf(headers));
return (SELF) this;
}

/**
* Weaviate will use the URL in this header to call Weaviate Embeddings
* Service if an appropriate vectorizer is configured for collection.
*/
private static final String HEADER_X_WEAVIATE_CLUSTER_URL = "X-Weaviate-Cluster-URL";

/**
Expand All @@ -102,13 +142,27 @@ private static boolean isWeaviateDomain(String host) {

@Override
public Config build() {
// For clusters hosted on Weaviate Cloud, Weaviate Embedding Service
// will be available under the same domain.
if (isWeaviateDomain(httpHost) && tokenProvider != null) {
setHeader(HEADER_X_WEAVIATE_CLUSTER_URL, "https://" + httpHost + ":" + httpPort);
}
return new Config(this);
}
}

/**
* Configuration for Weaviate instances deployed locally.
*
* <p>
* Has sane defaults that match standard Weaviate deployment configuration:
* <ul>
* <li>{@code scheme: http}</li>
* <li>{@code host: localhost}</li>
* <li>{@code httpPort: 8080}</li>
* <li>{@code grpcPort: 50051}</li>
* </ul>
*/
public static class Local extends Builder<Local> {
public Local() {
scheme("http");
Expand All @@ -117,23 +171,37 @@ public Local() {
grpcPort(50051);
}

/**
* Set a different hostname.
* This changes both {@code httpHost} and {@code grpcHost}.
*/
public Local host(String host) {
httpHost(host);
grpcHost(host);
return this;
}

/** Override default HTTP port. */
public Local httpPort(int port) {
this.httpPort = port;
return this;
}

/** Override default gRPC port. */
public Local grpcPort(int port) {
this.grpcPort = port;
return this;
}
}

/**
* Configuration for instances hosted on Weaviate Cloud.
* {@link WeaviateCloud} will create a secure client
* with {@code schema: https} and {@code http-/grpcPort: 443}.
*
* Custom SSL certificates are suppored via
* {@link #trustManagerFactory}.
*/
public static class WeaviateCloud extends Builder<WeaviateCloud> {
public WeaviateCloud(String httpHost, TokenProvider tokenProvider) {
this(URI.create(httpHost), tokenProvider);
Expand All @@ -144,48 +212,108 @@ public WeaviateCloud(URI clusterUri, TokenProvider tokenProvider) {
super.httpHost(clusterUri.getHost() != null
? clusterUri.getHost() // https://[example.com]/about
: clusterUri.getPath().split("/")[0]); // [example.com]/about
this.httpPort = 443;
super.grpcHost("grpc-" + this.httpHost);
this.httpPort = 443;
this.grpcPort = 443;
this.tokenProvider = tokenProvider;
}

/**
* Configure a custom TrustStore to validate third-party SSL certificates.
*
* <p>
* Usage:
*
* <pre>{@code
* // Create a TrustManagerFactory to validate custom certificates.
* TrustManagerFactory tmf;
* try (var keys = new FileInputStream("/path/to/custom/truststore.p12")) {
* KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
* trustStore.load(myKeys, "secret-password".toCharArra());
*
* tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
* tmf.init(trustStore);
* }
*
* // Pass it to wcd -> wcd.trustManagerFactory(tmf)
* }</pre>
*/
public WeaviateCloud trustManagerFactory(TrustManagerFactory tmf) {
return super.trustManagerFactory(tmf);
}
}

/** Configuration for custom Weaviate deployements. */
public static class Custom extends Builder<Custom> {
/**
* Scheme controls which protocol will be used for the database connection.
* REST and gRPC ports will be automatically inferred from it:
* <strong>443</strong> for HTTPS connection and <strong>80</strong> for HTTP.
*
* These can be overriden with {@link #httpPort(int)} and
* {@link #grpcPort(int)}.
*/
public Custom scheme(String scheme) {
httpPort("https".equals(scheme) ? 443 : 80);
grpcPort("https".equals(scheme) ? 443 : 80);
return super.scheme(scheme);
}

/** Set HTTP hostname. */
public Custom httpHost(String httpHost) {
super.httpHost(httpHost);
return this;
}

/** Set HTTP port. */
public Custom httpPort(int port) {
this.httpPort = port;
return this;
}

/** Set gRPC hostname. */
public Custom grpcHost(String grpcHost) {
super.grpcHost(grpcHost);
return this;
}

/** Set gRPC port. */
public Custom grpcPort(int port) {
this.grpcPort = port;
return this;
}

/**
* Set authorization method. Setting this to {@code null} or omitting
* will not use any authorization mechanism.
*/
public Custom authorization(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
return this;
}

/**
* Configure a custom TrustStore to validate third-party SSL certificates.
*
* <p>
* Usage:
*
* <pre>{@code
* // Create a TrustManagerFactory to validate custom certificates.
* TrustManagerFactory tmf;
* try (var keys = new FileInputStream("/path/to/custom/truststore.p12")) {
* KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
* trustStore.load(myKeys, "secret-password".toCharArra());
*
* tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
* tmf.init(trustStore);
* }
*
* // Pass it to custom -> custom.trustManagerFactory(tmf)
* }</pre>
*/
public Custom trustManagerFactory(TrustManagerFactory tmf) {
return super.trustManagerFactory(tmf);
}
}
}
49 changes: 49 additions & 0 deletions src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,81 @@ public WeaviateClient(Config config) {
this.collections = new WeaviateCollectionsClient(restTransport, grpcTransport);
}

/**
* Create {@link WeaviateClientAsync} with identical configurations.
* It is a shorthand for:
*
* <pre>{@code
* var config = new Config(...);
* var client = new WeaviateClient(config);
* var async = new WeaviateClientAsync(config);
* }</pre>
*
* and as such, this does not manage or reuse resources (transport, gRPC
* channel, etc) used by the original client. Keep that in mind and make
* sure to close the original and async clients individually.
*
* <p>
* Example:
*
* <pre>{@code
* var client = WeaviateClient.local();
*
* // Need to make the next request non-blocking
* try (final var async = client.async()) {
* async.collections.create("Things");
* }
* // At this point only `async` resource has been auto-closed.
*
* client.close();
* }</pre>
*
*
* If you only intend to use {@link WeaviateClientAsync}, prefer creating it
* directly via one of its static factories:
* <ul>
* <li>{@link WeaviateClientAsync#local}
* <li>{@link WeaviateClientAsync#wcd}
* <li>{@link WeaviateClientAsync#custom}
* </ul>
*
* Otherwise the client wastes time initializing resources it will never use.
*/
public WeaviateClientAsync async() {
return new WeaviateClientAsync(config);
}

/** Connect to a local Weaviate instance. */
public static WeaviateClient local() {
return local(ObjectBuilder.identity());
}

/** Connect to a local Weaviate instance. */
public static WeaviateClient local(Function<Config.Local, ObjectBuilder<Config>> fn) {
return new WeaviateClient(fn.apply(new Config.Local()).build());
}

/** Connect to a Weaviate Cloud instance. */
public static WeaviateClient wcd(String httpHost, String apiKey) {
return wcd(httpHost, apiKey, ObjectBuilder.identity());
}

/** Connect to a Weaviate Cloud instance. */
public static WeaviateClient wcd(String httpHost, String apiKey,
Function<Config.WeaviateCloud, ObjectBuilder<Config>> fn) {
var config = new Config.WeaviateCloud(httpHost, Authorization.apiKey(apiKey));
return new WeaviateClient(fn.apply(config).build());
}

/** Connect to a Weaviate instance with custom configuration. */
public static WeaviateClient custom(Function<Config.Custom, ObjectBuilder<Config>> fn) {
return new WeaviateClient(fn.apply(new Config.Custom()).build());
}

/**
* Close {@link #restTransport} and {@link #grpcTransport}
* and release associated resources.
*/
@Override
public void close() throws IOException {
this.restTransport.close();
Expand Down
Loading