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
7 changes: 2 additions & 5 deletions src/main/java/io/weaviate/client/base/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,9 @@ private Response<T> sendRequest(String endpoint, Object payload, String method,
int statusCode = response.getStatusCode();
String responseBody = response.getBody();
if (statusCode < 399) {
T body = toResponse(responseBody, classOfT);
return new Response<>(statusCode, body, null);
return new Response<>(statusCode, toResponse(responseBody, classOfT), null);
}

WeaviateErrorResponse error = toResponse(responseBody, WeaviateErrorResponse.class);
return new Response<>(statusCode, null, error);
return new Response<>(statusCode, null, toResponse(responseBody, WeaviateErrorResponse.class));
} catch (Exception e) {
WeaviateErrorResponse errors = getWeaviateErrorResponse(e);
return new Response<>(0, null, errors);
Expand Down
74 changes: 70 additions & 4 deletions src/main/java/io/weaviate/client/base/Result.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package io.weaviate.client.base;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.hc.core5.http.ContentType;
Expand All @@ -25,6 +29,10 @@ public Result(Response<T> response) {
this(response.getStatusCode(), response.getBody(), response.getErrors());
}

public Result(Response<?> response, T body) {
this(response.getStatusCode(), body, response.getErrors());
}

public Result(int statusCode, T body, WeaviateErrorResponse errors) {
if (errors != null && errors.getError() != null) {
List<WeaviateErrorMessage> items = errors.getError().stream().filter(Objects::nonNull)
Expand Down Expand Up @@ -60,15 +68,37 @@ public <C> Result<C> toErrorResult() {

/**
* Convert {@code Result<Void>} response to a {@code Result<Boolean>}.
* The result contains true if status code is 200.
* The result contains true if status code is in 100-299 range.
*
* @param response Response from a call that does not return a value, like
* {@link BaseClient#sendDeleteRequest}.
* @return {@code Result<Boolean>}
*/
public static Result<Boolean> voidToBoolean(Response<Void> response) {
int status = response.getStatusCode();
return new Result<>(status, status < 299, response.getErrors());
return new Result<>(status, status <= 299, response.getErrors());
}

/**
* Convert {@code Result<Void>} response to a {@code Result<Boolean>}.
* The result contains true if status code is in 100-299 range or is one of the
* allowed codes (e.g. HTTP 409 is used when the request has no effect, because
* a previous one has already succeeded).
*
* @param allowCodes Avoid treating these error codes as an error
* and only return false.
*
* @param response Response from a call that does not return a value, like
* {@link BaseClient#sendDeleteRequest}.
* @return {@code Result<Boolean>}
*/
public static Result<Boolean> voidToBoolean(Response<Void> response, int... allowCodes) {
Integer status = response.getStatusCode();
boolean isCodeAllowed = Arrays.stream(allowCodes).anyMatch(status::equals);
if (status <= 299) {
return new Result<>(status, true, null);
}
return new Result<>(status, false, isCodeAllowed ? null : response.getErrors());
}

/**
Expand All @@ -81,10 +111,46 @@ public static ResponseParser<Boolean> voidToBooleanParser() {
return new ResponseParser<Boolean>() {
@Override
public Result<Boolean> parse(HttpResponse response, String body, ContentType contentType) {
Response<Object> resp = this.serializer.toResponse(response.getCode(), body, Object.class);
return new Result<>(resp.getStatusCode(), resp.getStatusCode() < 299, resp.getErrors());
Response<Void> resp = this.serializer.toResponse(response.getCode(), body, Void.class);
return voidToBoolean(resp);
}
};
}

/**
* Get a custom parser to convert {@code Result<Void>} response as to a
* {@code Result<Void>}. The result contains true if status code is 200.
*
* @param allowCodes Avoid treating these error codes as an error
* and only return false.
*
* @return {@code Result<Boolean>}
*/
public static ResponseParser<Boolean> voidToBooleanParser(int... allowCodes) {
return new ResponseParser<Boolean>() {
@Override
public Result<Boolean> parse(HttpResponse response, String body, ContentType contentType) {
Response<Void> resp = this.serializer.toResponse(response.getCode(), body, Void.class);
return voidToBoolean(resp, allowCodes);
}
};
}

public static <T> ResponseParser<List<T>> arrayToListParser(Class<T[]> cls) {
return arrayToListParser(cls, Function.identity());
}

public static <T, R> ResponseParser<List<R>> arrayToListParser(Class<T[]> cls,
Function<? super T, ? extends R> mapper) {
return new ResponseParser<List<R>>() {
@Override
public Result<List<R>> parse(HttpResponse response, String body, ContentType contentType) {
Response<T[]> resp = this.serializer.toResponse(response.getCode(), body, cls);
List<R> roles = Optional.ofNullable(resp.getBody())
.map(Arrays::asList).orElse(new ArrayList<>())
.stream().map(mapper).collect(Collectors.toList());
return new Result<>(resp.getStatusCode(), roles, resp.getErrors());
}
};
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package io.weaviate.client.base.http.impl;

import io.weaviate.client.base.http.HttpClient;
import io.weaviate.client.base.http.HttpResponse;
import io.weaviate.client.v1.auth.provider.AccessTokenProvider;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpHead;
Expand All @@ -22,6 +20,10 @@
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;

import io.weaviate.client.base.http.HttpClient;
import io.weaviate.client.base.http.HttpResponse;
import io.weaviate.client.v1.auth.provider.AccessTokenProvider;

public class CommonsHttpClientImpl implements HttpClient, Closeable {
private final Map<String, String> headers;
private AccessTokenProvider tokenProvider;
Expand All @@ -31,7 +33,8 @@ public CommonsHttpClientImpl(Map<String, String> headers, CloseableHttpClientBui
this(headers, null, clientBuilder);
}

public CommonsHttpClientImpl(Map<String, String> headers, AccessTokenProvider tokenProvider, CloseableHttpClientBuilder clientBuilder) {
public CommonsHttpClientImpl(Map<String, String> headers, AccessTokenProvider tokenProvider,
CloseableHttpClientBuilder clientBuilder) {
this.headers = headers;
this.clientBuilder = clientBuilder;
this.tokenProvider = tokenProvider;
Expand All @@ -44,6 +47,9 @@ public HttpResponse sendGetRequest(String url) throws Exception {

@Override
public HttpResponse sendPostRequest(String url, String json) throws Exception {
if (json == null) {
return sendRequestWithoutPayload(new HttpPost(url));
}
return sendRequestWithPayload(new HttpPost(url), json);
}

Expand Down Expand Up @@ -95,10 +101,9 @@ private HttpResponse sendRequest(BasicClassicHttpRequest request) throws Excepti

int statusCode = response.getCode();
String body = response.getEntity() != null
? EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8)
: "";
? EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8)
: "";
client.close();

return new HttpResponse(statusCode, body);
}

Expand Down
23 changes: 22 additions & 1 deletion src/main/java/io/weaviate/client/v1/async/rbac/Roles.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.weaviate.client.v1.async.rbac.api.RoleDeleter;
import io.weaviate.client.v1.async.rbac.api.RoleExists;
import io.weaviate.client.v1.async.rbac.api.RoleGetter;
import io.weaviate.client.v1.async.rbac.api.UserAssignmentsGetter;
import io.weaviate.client.v1.auth.provider.AccessTokenProvider;
import lombok.RequiredArgsConstructor;

Expand Down Expand Up @@ -65,11 +66,31 @@ public RoleGetter getter() {
return new RoleGetter(client, config, tokenProvider);
};

/** Get users assigned to a role. */
/**
* Get users assigned to a role.
* <p>
* Deprecated - prefer {@link #userAssignmentsGetter()}.
*/
@Deprecated
public AssignedUsersGetter assignedUsersGetter() {
return new AssignedUsersGetter(client, config, tokenProvider);
};

/**
* Get role assignments.
*
* <p>
* Note, that the result is not a list of unique users,
* but rather a list of all username+namespace combinations
* allowed for this role.
* In clusters with enabled OIDC authorization, users created dynamically
* (db_user) or configured in the environment (db_env_user) will appear twice:
* once as 'db_*' user and once as 'oidc' user.
*/
public UserAssignmentsGetter userAssignmentsGetter() {
return new UserAssignmentsGetter(client, config, tokenProvider);
};

/** Check if a role exists. */
public RoleExists exists() {
return new RoleExists(client, config, tokenProvider);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.weaviate.client.v1.async.rbac.api;

import java.util.List;
import java.util.concurrent.Future;

import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.core5.concurrent.FutureCallback;

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.auth.provider.AccessTokenProvider;
import io.weaviate.client.v1.rbac.model.UserAssignment;

public class UserAssignmentsGetter extends AsyncBaseClient<List<UserAssignment>>
implements AsyncClientResult<List<UserAssignment>> {
private String role;

public UserAssignmentsGetter(CloseableHttpAsyncClient httpClient, Config config, AccessTokenProvider tokenProvider) {
super(httpClient, config, tokenProvider);
}

public UserAssignmentsGetter withRole(String role) {
this.role = role;
return this;
}

@Override
public Future<Result<List<UserAssignment>>> run(FutureCallback<Result<List<UserAssignment>>> callback) {
return sendGetRequest(path(), callback, Result.arrayToListParser(UserAssignment[].class));
}

private String path() {
return String.format("/authz/roles/%s/user-assignments", this.role);
}
}
80 changes: 80 additions & 0 deletions src/main/java/io/weaviate/client/v1/async/users/DbUsers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package io.weaviate.client.v1.async.users;

import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;

import io.weaviate.client.Config;
import io.weaviate.client.v1.async.users.api.RoleAssigner;
import io.weaviate.client.v1.async.users.api.RoleRevoker;
import io.weaviate.client.v1.async.users.api.common.AssignedRolesGetter;
import io.weaviate.client.v1.async.users.api.db.Activator;
import io.weaviate.client.v1.async.users.api.db.AllGetter;
import io.weaviate.client.v1.async.users.api.db.ByNameGetter;
import io.weaviate.client.v1.async.users.api.db.Creator;
import io.weaviate.client.v1.async.users.api.db.Deactivator;
import io.weaviate.client.v1.async.users.api.db.Deleter;
import io.weaviate.client.v1.async.users.api.db.KeyRotator;
import io.weaviate.client.v1.auth.provider.AccessTokenProvider;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class DbUsers {
private static final String USER_TYPE = "db";

private final CloseableHttpAsyncClient client;
private final Config config;
private final AccessTokenProvider tokenProvider;

/** Assign a role to a user. Note that 'root' cannot be assigned. */
public RoleAssigner assigner() {
return new RoleAssigner(client, config, tokenProvider, USER_TYPE);
}

/** Revoke a role from a user. Note that 'root' cannot be revoked. */
public RoleRevoker revoker() {
return new RoleRevoker(client, config, tokenProvider, USER_TYPE);
}

/** Get roles assigned to a user. */
public AssignedRolesGetter userRolesGetter() {
return new AssignedRolesGetter(client, config, tokenProvider, USER_TYPE);
}

/** Create a new user. Returns API key for the user to authenticate by. */
public Creator creator() {
return new Creator(client, config, tokenProvider);
}

/**
* Delete user.
* Users declared in the server environment config cannot be
* deleted ('db_env_user').
*/
public Deleter deleter() {
return new Deleter(client, config, tokenProvider);
}

/** Activate user account. */
public Activator activator() {
return new Activator(client, config, tokenProvider);
}

/** Deactivate user account, optionally revoking its API key. */
public Deactivator deactivator() {
return new Deactivator(client, config, tokenProvider);
}

/** Rotate user's API key. The old key will become invalid. */
public KeyRotator keyRotator() {
return new KeyRotator(client, config, tokenProvider);
}

/** Get information about the user. */
public ByNameGetter getUser() {
return new ByNameGetter(client, config, tokenProvider);
}

/** List all known (non-OIDC) users. */
public AllGetter allGetter() {
return new AllGetter(client, config, tokenProvider);
}
}
34 changes: 34 additions & 0 deletions src/main/java/io/weaviate/client/v1/async/users/OidcUsers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.weaviate.client.v1.async.users;

import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;

import io.weaviate.client.Config;
import io.weaviate.client.v1.async.users.api.RoleAssigner;
import io.weaviate.client.v1.async.users.api.RoleRevoker;
import io.weaviate.client.v1.async.users.api.common.AssignedRolesGetter;
import io.weaviate.client.v1.auth.provider.AccessTokenProvider;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class OidcUsers {
private static final String USER_TYPE = "oidc";

private final CloseableHttpAsyncClient client;
private final Config config;
private final AccessTokenProvider tokenProvider;

/** Assign a role to a user. Note that 'root' cannot be assigned. */
public RoleAssigner assigner() {
return new RoleAssigner(client, config, tokenProvider, USER_TYPE);
}

/** Revoke a role from a user. Note that 'root' cannot be revoked. */
public RoleRevoker revoker() {
return new RoleRevoker(client, config, tokenProvider, USER_TYPE);
}

/** Get roles assigned to a user. */
public AssignedRolesGetter userRolesGetter() {
return new AssignedRolesGetter(client, config, tokenProvider, USER_TYPE);
}
}
Loading