diff --git a/client-impl/build.gradle b/client-impl/build.gradle index e4dc659..de75c4b 100644 --- a/client-impl/build.gradle +++ b/client-impl/build.gradle @@ -15,6 +15,7 @@ dependencies { api 'com.squareup.okhttp3:okhttp:4.12.0' api 'com.fasterxml.jackson.core:jackson-databind:2.13.3' api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3' + api 'org.reflections:reflections:0.9.12' testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/RoomPickerClient.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/RoomPickerClient.java index e279bb2..101de04 100644 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/RoomPickerClient.java +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/RoomPickerClient.java @@ -2,15 +2,12 @@ package ru.dragonestia.picker.api.impl; import okhttp3.Credentials; import okhttp3.Request; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.ApiStatus.Internal; -import ru.dragonestia.picker.api.impl.repository.AccountRepositoryImpl; -import ru.dragonestia.picker.api.impl.repository.InstanceRepositoryImpl; -import ru.dragonestia.picker.api.impl.repository.RoomRepositoryImpl; -import ru.dragonestia.picker.api.impl.repository.EntityRepositoryImpl; +import ru.dragonestia.picker.api.impl.exception.ExceptionService; import ru.dragonestia.picker.api.impl.util.RestTemplate; import ru.dragonestia.picker.api.impl.util.type.HttpMethod; +import ru.dragonestia.picker.api.model.account.Account; import ru.dragonestia.picker.api.repository.AccountRepository; import ru.dragonestia.picker.api.repository.InstanceRepository; import ru.dragonestia.picker.api.repository.RoomRepository; @@ -27,47 +24,63 @@ public class RoomPickerClient { private final RoomRepository roomRepository; private final EntityRepository entityRepository; private final AccountRepository accountRepository; + private Account account; - public RoomPickerClient(@NotNull String url, @NotNull String username, @NotNull String password) { + public RoomPickerClient(String url, String username, String password) { this.url = url; this.username = username; this.password = password; - this.restTemplate = new RestTemplate(this); - this.instanceRepository = new InstanceRepositoryImpl(this); - this.roomRepository = new RoomRepositoryImpl(this); - this.entityRepository = new EntityRepositoryImpl(this); - this.accountRepository = new AccountRepositoryImpl(this); + this.restTemplate = new RestTemplate(this, this::updateAccountData); + this.instanceRepository = null; //new InstanceRepositoryImpl(this); + this.roomRepository = null; //new RoomRepositoryImpl(this); + this.entityRepository = null; //new EntityRepositoryImpl(this); + this.accountRepository = null; //new AccountRepositoryImpl(this); + + ExceptionService.init(); } @Internal - public @NotNull RestTemplate getRestTemplate() { + public RestTemplate getRestTemplate() { return restTemplate; } @Internal - public @NotNull Request.Builder prepareRequestBuilder(@NotNull String uri) { + public Request.Builder prepareRequestBuilder(String uri) { return new Request.Builder() .url(url + uri) .addHeader("Authorization", Credentials.basic(username, password)); } - public @NotNull InstanceRepository getNodeRepository() { + public InstanceRepository getNodeRepository() { return instanceRepository; } - public @NotNull RoomRepository getRoomRepository() { + public RoomRepository getRoomRepository() { return roomRepository; } - public @NotNull EntityRepository getUserRepository() { + public EntityRepository getUserRepository() { return entityRepository; } - public @NotNull AccountRepository getAccountRepository() { + public AccountRepository getAccountRepository() { return accountRepository; } - public @NotNull RoomPickerInfoResponse getServerInfo() { + public RoomPickerInfoResponse getServerInfo() { return restTemplate.query("/info", HttpMethod.GET, RoomPickerInfoResponse.class, params -> {}); } + + public Account getAccount() { + if (account == null) { + getServerInfo(); + assert account != null; + } + + return account; + } + + private void updateAccountData(Account account) { + this.account = account; + } } diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/ExceptionService.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/ExceptionService.java new file mode 100644 index 0000000..9b1caea --- /dev/null +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/ExceptionService.java @@ -0,0 +1,34 @@ +package ru.dragonestia.picker.api.impl.exception; + +import org.reflections.Reflections; +import ru.dragonestia.picker.api.exception.ApiException; + +import java.util.HashMap; +import java.util.Map; + +public class ExceptionService { + + private static Map> exceptionMap = null; + + public static void init() { + if (exceptionMap != null) return; + exceptionMap = new HashMap<>(); + + var ref = new Reflections("ru.dragonestia.picker.api.exception"); + for (var clazz: ref.getTypesAnnotatedWith(ApiException.class)) { + if (!clazz.isNestmateOf(RuntimeException.class)) continue; + + exceptionMap.put(clazz.getSimpleName(), clazz); + } + } + + public static RuntimeException prepare(String ex, String message) { + try { + return (RuntimeException) exceptionMap.getOrDefault(ex, UnknownException.class) + .getConstructor(String.class) + .newInstance(message); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/UnknownException.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/UnknownException.java new file mode 100644 index 0000000..e9d84b6 --- /dev/null +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/exception/UnknownException.java @@ -0,0 +1,8 @@ +package ru.dragonestia.picker.api.impl.exception; + +public class UnknownException extends RuntimeException { + + public UnknownException(String message) { + super(message); + } +} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/AccountRepositoryImpl.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/AccountRepositoryImpl.java deleted file mode 100644 index 9b81749..0000000 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/AccountRepositoryImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -package ru.dragonestia.picker.api.impl.repository; - -import org.jetbrains.annotations.ApiStatus.Internal; -import org.jetbrains.annotations.NotNull; -import ru.dragonestia.picker.api.exception.AccountDoesNotExistsException; -import ru.dragonestia.picker.api.impl.RoomPickerClient; -import ru.dragonestia.picker.api.impl.util.RestTemplate; -import ru.dragonestia.picker.api.impl.util.type.HttpMethod; -import ru.dragonestia.picker.api.model.account.IAccount; -import ru.dragonestia.picker.api.model.account.ResponseAccount; -import ru.dragonestia.picker.api.repository.AccountRepository; -import ru.dragonestia.picker.api.repository.response.AllAccountsResponse; - -import java.util.List; -import java.util.Optional; -import java.util.Set; - -public class AccountRepositoryImpl implements AccountRepository { - - private final RestTemplate rest; - - @Internal - public AccountRepositoryImpl(RoomPickerClient client) { - this.rest = client.getRestTemplate(); - } - - @Override - public Optional findAccountByUsername(@NotNull String username) { - try { - var response = rest.query("/accounts/" + username, HttpMethod.GET, ResponseAccount.class); - return Optional.of(response); - } catch (AccountDoesNotExistsException ex) { - return Optional.empty(); - } - } - - @Override - public @NotNull List allAccounts() { - var response = rest.query("/accounts", HttpMethod.GET, AllAccountsResponse.class); - return response.accounts(); - } - - @Override - public void createAccount(@NotNull String accountId, @NotNull String password, @NotNull Set permissions) { - rest.query("/accounts", HttpMethod.POST, params -> { - params.put("username", accountId); - params.put("password", password); - params.put("permissions", String.join(",", permissions)); - }); - } - - @Override - public void removeAccount(@NotNull IAccount account) { - rest.query("/accounts/" + account.getUsername(), HttpMethod.DELETE); - } - - @Override - public void setPermissions(@NotNull IAccount account, @NotNull List permissions) { - rest.query("/accounts/" + account.getUsername(), HttpMethod.PUT, params -> { - params.put("permissions", String.join(",", permissions)); - }); - } - - @Override - public void setPassword(@NotNull IAccount account, @NotNull String newPassword) { - rest.query("/accounts/" + account.getUsername() + "/password", HttpMethod.PUT, params -> { - params.put("newPassword", newPassword); - }); - } -} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/EntityRepositoryImpl.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/EntityRepositoryImpl.java deleted file mode 100644 index 12b91ae..0000000 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/EntityRepositoryImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -package ru.dragonestia.picker.api.impl.repository; - -import org.jetbrains.annotations.ApiStatus.Internal; -import org.jetbrains.annotations.NotNull; -import ru.dragonestia.picker.api.impl.RoomPickerClient; -import ru.dragonestia.picker.api.impl.util.EnumUtils; -import ru.dragonestia.picker.api.impl.util.RestTemplate; -import ru.dragonestia.picker.api.impl.util.type.HttpMethod; -import ru.dragonestia.picker.api.model.room.ShortResponseRoom; -import ru.dragonestia.picker.api.model.user.ResponseUser; -import ru.dragonestia.picker.api.repository.EntityRepository; -import ru.dragonestia.picker.api.repository.query.user.*; -import ru.dragonestia.picker.api.repository.response.LinkedRoomsWithUserResponse; -import ru.dragonestia.picker.api.repository.response.RoomUserListResponse; -import ru.dragonestia.picker.api.repository.response.SearchUserResponse; -import ru.dragonestia.picker.api.repository.response.UserDetailsResponse; - -import java.util.List; - -public class EntityRepositoryImpl implements EntityRepository { - - private final RestTemplate rest; - - @Internal - public EntityRepositoryImpl(RoomPickerClient client) { - rest = client.getRestTemplate(); - } - - @Override - public void linkUsersWithRoom(@NotNull LinkUsersWithRoom request) { - rest.query("/nodes/%s/rooms/%s/users".formatted(request.getNodeId(), request.getRoomId()), - HttpMethod.POST, - params -> { - params.put("userIds", String.join(",", request.getUsers())); - params.put("force", Boolean.toString(request.ignoreSlotLimitation())); - }); - } - - @Override - public void unlinkUsersFromRoom(@NotNull UnlinkUsersFromRoom request) { - rest.query("/nodes/%s/rooms/%s/users".formatted(request.getNodeId(), request.getRoomId()), - HttpMethod.DELETE, - params -> { - params.put("userIds", String.join(",", request.getUsers())); - }); - } - - @Override - public @NotNull List getAllUsersFormRoom(@NotNull GetAllUsersFromRoom request) { - return rest.query("/nodes/%s/rooms/%s/users".formatted(request.getNodeId(), request.getRoomId()), - HttpMethod.GET, - RoomUserListResponse.class, - params -> { - var detailsStr = String.join(",", EnumUtils.enumSetToString(request.getDetails())); - - params.put("requiredDetails", detailsStr); - }).users(); - } - - @Override - public @NotNull List searchUsers(@NotNull SearchUsers request) { - return rest.query("/users/search", - HttpMethod.GET, - SearchUserResponse.class, - params -> { - params.put("requiredDetails", EnumUtils.enumSetToString(request.getDetails())); - params.put("input", request.getSearchInput()); - }).users(); - } - - @Override - public @NotNull ResponseUser findUserById(@NotNull FindUserById request) { - return rest.query("/users/" + request.getUserId(), - HttpMethod.GET, - UserDetailsResponse.class, - params -> { - params.put("requiredDetails", EnumUtils.enumSetToString(request.getDetails())); - }).user(); - } - - @Override - public @NotNull List findRoomsLinkedWithUser(@NotNull FindRoomsLinkedWithUser request) { - return rest.query("/users/" + request.getUserId() + "/rooms", - HttpMethod.GET, - LinkedRoomsWithUserResponse.class, - params -> { - params.put("requiredDetails", EnumUtils.enumSetToString(request.getDetails())); - }).rooms(); - } -} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/InstanceRepositoryImpl.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/InstanceRepositoryImpl.java deleted file mode 100644 index 9896c69..0000000 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/InstanceRepositoryImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -package ru.dragonestia.picker.api.impl.repository; - -import org.jetbrains.annotations.ApiStatus.Internal; -import org.jetbrains.annotations.NotNull; -import ru.dragonestia.picker.api.exception.InstanceNotFoundException; -import ru.dragonestia.picker.api.impl.RoomPickerClient; -import ru.dragonestia.picker.api.impl.util.EnumUtils; -import ru.dragonestia.picker.api.impl.util.RestTemplate; -import ru.dragonestia.picker.api.impl.util.type.HttpMethod; -import ru.dragonestia.picker.api.model.node.INode; -import ru.dragonestia.picker.api.model.node.NodeDefinition; -import ru.dragonestia.picker.api.repository.InstanceRepository; -import ru.dragonestia.picker.api.repository.query.node.FindNodeById; -import ru.dragonestia.picker.api.repository.query.node.GetAllNodes; -import ru.dragonestia.picker.api.repository.query.node.RemoveNodesByIds; -import ru.dragonestia.picker.api.repository.response.NodeDetailsResponse; -import ru.dragonestia.picker.api.repository.response.NodeListResponse; -import ru.dragonestia.picker.api.repository.response.PickedRoomResponse; -import ru.dragonestia.picker.api.repository.type.NodeIdentifier; -import ru.dragonestia.picker.api.repository.type.EntityIdentifier; - -import java.util.List; -import java.util.Optional; -import java.util.Set; - -public class InstanceRepositoryImpl implements InstanceRepository { - - private final RestTemplate rest; - - @Internal - public InstanceRepositoryImpl(RoomPickerClient client) { - rest = client.getRestTemplate(); - } - - @Override - public @NotNull List allNodes(@NotNull GetAllNodes data) { - return rest.query("/nodes", HttpMethod.GET, NodeListResponse.class, params -> { - params.put("requiredDetails", EnumUtils.enumSetToString(data.getDetails())); - }).nodes().stream().map(node -> (INode) node).toList(); - } - - @Override - public @NotNull Optional findNodeById(@NotNull FindNodeById data) { - try { - var response = rest.query("/nodes/" + data.getId(), HttpMethod.GET, NodeDetailsResponse.class, params -> { - params.put("requiredDetails", EnumUtils.enumSetToString(data.getDetails())); - }); - return Optional.of(response.node()); - } catch (InstanceNotFoundException ex) { - return Optional.empty(); - } - } - - @Override - public void removeNodesById(@NotNull RemoveNodesByIds data) { - if (data.getNodeIds().isEmpty()) return; - - rest.query("/nodes", HttpMethod.DELETE, params -> { - params.put("toDelete", String.join(",", data.getNodeIds())); - }); - } - - @Override - public void removeNode(@NotNull INode node) { - rest.query("/nodes/" + node.getIdentifier(), HttpMethod.DELETE, params -> {}); - } - - @Override - public void saveNode(@NotNull NodeDefinition definition) { - rest.query("/nodes", HttpMethod.POST, params -> { - params.put("nodeId", definition.getIdentifier()); - params.put("method", definition.getPickingMethod().name()); - params.put("persist", Boolean.toString(definition.isPersist())); - }); - } - - @Override - public @NotNull PickedRoomResponse pickRoom(@NotNull NodeIdentifier identifier, @NotNull Set users) { - return rest.queryPostWithBody( - "/nodes/" + identifier.getValue() + "/pick", - PickedRoomResponse.class, - params -> {}, String.join(",", users.stream().map(user -> user.getValue()).toList()) - ); - } -} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/RoomRepositoryImpl.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/RoomRepositoryImpl.java deleted file mode 100644 index db998ab..0000000 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/RoomRepositoryImpl.java +++ /dev/null @@ -1,88 +0,0 @@ -package ru.dragonestia.picker.api.impl.repository; - -import org.jetbrains.annotations.ApiStatus.Internal; -import org.jetbrains.annotations.NotNull; -import ru.dragonestia.picker.api.exception.RoomNotFoundException; -import ru.dragonestia.picker.api.impl.RoomPickerClient; -import ru.dragonestia.picker.api.impl.util.EnumUtils; -import ru.dragonestia.picker.api.impl.util.RestTemplate; -import ru.dragonestia.picker.api.impl.util.type.HttpMethod; -import ru.dragonestia.picker.api.model.room.IRoom; -import ru.dragonestia.picker.api.model.room.ResponseRoom; -import ru.dragonestia.picker.api.model.room.RoomDefinition; -import ru.dragonestia.picker.api.model.room.ShortResponseRoom; -import ru.dragonestia.picker.api.repository.RoomRepository; -import ru.dragonestia.picker.api.repository.query.room.FindRoomById; -import ru.dragonestia.picker.api.repository.query.room.GetAllRooms; -import ru.dragonestia.picker.api.repository.query.room.RemoveRoomsByIds; -import ru.dragonestia.picker.api.repository.response.RoomInfoResponse; -import ru.dragonestia.picker.api.repository.response.RoomListResponse; -import ru.dragonestia.picker.api.repository.type.RoomPath; - -import java.util.List; -import java.util.Optional; - -public class RoomRepositoryImpl implements RoomRepository { - - private final RestTemplate rest; - - @Internal - public RoomRepositoryImpl(RoomPickerClient client) { - rest = client.getRestTemplate(); - } - - @Override - public void saveRoom(@NotNull RoomDefinition definition) { - rest.query("/nodes/" + definition.getInstanceIdentifier() + "/rooms", HttpMethod.POST, params -> { - params.put("roomId", definition.getIdentifier()); - params.put("slots", Integer.toString(definition.getMaxSlots())); - params.put("payload", definition.getPayload()); - params.put("locked", Boolean.toString(definition.isLocked())); - params.put("persist", Boolean.toString(definition.isPersist())); - }); - } - - @Override - public void removeRooms(@NotNull RemoveRoomsByIds request) { - if (request.getRoomsIds().isEmpty()) return; - - rest.query("/nodes/" + request.getNodeId() + "/rooms", HttpMethod.DELETE, params -> { - params.put("toDelete", String.join(",", request.getRoomsIds())); - }); - } - - @Override - public void removeRoom(@NotNull IRoom room) { - rest.query( - "/nodes/%s/rooms/%s".formatted(room.getInstanceIdentifier(), room.getIdentifier()), - HttpMethod.DELETE, - params -> {} - ); - } - - @Override - public @NotNull List allRooms(@NotNull GetAllRooms request) { - return rest.query("/nodes/" + request.getNodeId() + "/rooms", HttpMethod.GET, RoomListResponse.class, params -> { - params.put("requiredDetails", EnumUtils.enumSetToString(request.getDetails())); - }).rooms(); - } - - @Override - public @NotNull Optional find(@NotNull FindRoomById request) { - try { - var response = rest.query("/nodes/%s/rooms/%s".formatted(request.getNodeId(), request.getId()), HttpMethod.GET, RoomInfoResponse.class, params -> { - params.put("requiredDetails", EnumUtils.enumSetToString(request.getDetails())); - }); - return Optional.of(response.room()); - } catch (RoomNotFoundException ex) { - return Optional.empty(); - } - } - - @Override - public void lockRoom(@NotNull RoomPath path, boolean value) { - rest.query("/nodes/%s/rooms/%s/lock".formatted(path.getNodeId(), path.getRoomId()), HttpMethod.PUT, params -> { - params.put("newState", Boolean.toString(value)); - }); - } -} diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java index 78c2cad..efb9036 100644 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java @@ -5,12 +5,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.*; import org.jetbrains.annotations.ApiStatus.Internal; +import ru.dragonestia.picker.api.impl.exception.ExceptionService; import ru.dragonestia.picker.api.impl.exception.NotEnoughPermissions; import ru.dragonestia.picker.api.impl.exception.AuthException; -import ru.dragonestia.picker.api.exception.ExceptionFactory; import ru.dragonestia.picker.api.impl.RoomPickerClient; import ru.dragonestia.picker.api.impl.util.type.HttpMethod; -import ru.dragonestia.picker.api.repository.response.ErrorResponse; +import ru.dragonestia.picker.api.model.account.Account; import java.io.IOException; import java.net.URLEncoder; @@ -22,11 +22,14 @@ import java.util.function.Consumer; public class RestTemplate { private final RoomPickerClient client; + private final Consumer accountConsumer; private final OkHttpClient httpClient; private final ObjectMapper json; - public RestTemplate(RoomPickerClient client) { + public RestTemplate(RoomPickerClient client, Consumer accountConsumer) { this.client = client; + this.accountConsumer = accountConsumer; + httpClient = new OkHttpClient(); json = configureJackson(); } @@ -119,8 +122,12 @@ public class RestTemplate { throw new NotEnoughPermissions("Not enough permissions"); } + var accountData = json.readValue(response.header("X-Account"), Account.class); + accountConsumer.accept(accountData); + + var exceptionClass = response.header("X-Server-Exception"); var body = new String(Objects.requireNonNull(response.body()).bytes(), StandardCharsets.UTF_8); - throw ExceptionFactory.of(json.readValue(body, ErrorResponse.class)); + throw ExceptionService.prepare(exceptionClass, body); } if (statusCode == 5) {