!refactored client api

This commit is contained in:
Andrey Terentev 2024-05-13 16:45:57 +07:00 committed by Andrey Terentev
parent 1c81d9b37a
commit 03de97846f
9 changed files with 85 additions and 355 deletions

View File

@ -15,6 +15,7 @@ dependencies {
api 'com.squareup.okhttp3:okhttp:4.12.0' api 'com.squareup.okhttp3:okhttp:4.12.0'
api 'com.fasterxml.jackson.core:jackson-databind:2.13.3' api 'com.fasterxml.jackson.core:jackson-databind:2.13.3'
api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310: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 platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation 'org.junit.jupiter:junit-jupiter'

View File

@ -2,15 +2,12 @@ package ru.dragonestia.picker.api.impl;
import okhttp3.Credentials; import okhttp3.Credentials;
import okhttp3.Request; import okhttp3.Request;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.ApiStatus.Internal;
import ru.dragonestia.picker.api.impl.repository.AccountRepositoryImpl; import ru.dragonestia.picker.api.impl.exception.ExceptionService;
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.util.RestTemplate; import ru.dragonestia.picker.api.impl.util.RestTemplate;
import ru.dragonestia.picker.api.impl.util.type.HttpMethod; 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.AccountRepository;
import ru.dragonestia.picker.api.repository.InstanceRepository; import ru.dragonestia.picker.api.repository.InstanceRepository;
import ru.dragonestia.picker.api.repository.RoomRepository; import ru.dragonestia.picker.api.repository.RoomRepository;
@ -27,47 +24,63 @@ public class RoomPickerClient {
private final RoomRepository roomRepository; private final RoomRepository roomRepository;
private final EntityRepository entityRepository; private final EntityRepository entityRepository;
private final AccountRepository accountRepository; 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.url = url;
this.username = username; this.username = username;
this.password = password; this.password = password;
this.restTemplate = new RestTemplate(this); this.restTemplate = new RestTemplate(this, this::updateAccountData);
this.instanceRepository = new InstanceRepositoryImpl(this); this.instanceRepository = null; //new InstanceRepositoryImpl(this);
this.roomRepository = new RoomRepositoryImpl(this); this.roomRepository = null; //new RoomRepositoryImpl(this);
this.entityRepository = new EntityRepositoryImpl(this); this.entityRepository = null; //new EntityRepositoryImpl(this);
this.accountRepository = new AccountRepositoryImpl(this); this.accountRepository = null; //new AccountRepositoryImpl(this);
ExceptionService.init();
} }
@Internal @Internal
public @NotNull RestTemplate getRestTemplate() { public RestTemplate getRestTemplate() {
return restTemplate; return restTemplate;
} }
@Internal @Internal
public @NotNull Request.Builder prepareRequestBuilder(@NotNull String uri) { public Request.Builder prepareRequestBuilder(String uri) {
return new Request.Builder() return new Request.Builder()
.url(url + uri) .url(url + uri)
.addHeader("Authorization", Credentials.basic(username, password)); .addHeader("Authorization", Credentials.basic(username, password));
} }
public @NotNull InstanceRepository getNodeRepository() { public InstanceRepository getNodeRepository() {
return instanceRepository; return instanceRepository;
} }
public @NotNull RoomRepository getRoomRepository() { public RoomRepository getRoomRepository() {
return roomRepository; return roomRepository;
} }
public @NotNull EntityRepository getUserRepository() { public EntityRepository getUserRepository() {
return entityRepository; return entityRepository;
} }
public @NotNull AccountRepository getAccountRepository() { public AccountRepository getAccountRepository() {
return accountRepository; return accountRepository;
} }
public @NotNull RoomPickerInfoResponse getServerInfo() { public RoomPickerInfoResponse getServerInfo() {
return restTemplate.query("/info", HttpMethod.GET, RoomPickerInfoResponse.class, params -> {}); 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;
}
} }

View File

@ -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<String, Class<?>> 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);
}
}
}

View File

@ -0,0 +1,8 @@
package ru.dragonestia.picker.api.impl.exception;
public class UnknownException extends RuntimeException {
public UnknownException(String message) {
super(message);
}
}

View File

@ -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<ResponseAccount> 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<ResponseAccount> 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<String> 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<String> 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);
});
}
}

View File

@ -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<ResponseUser> 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<ResponseUser> 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<ShortResponseRoom> findRoomsLinkedWithUser(@NotNull FindRoomsLinkedWithUser request) {
return rest.query("/users/" + request.getUserId() + "/rooms",
HttpMethod.GET,
LinkedRoomsWithUserResponse.class,
params -> {
params.put("requiredDetails", EnumUtils.enumSetToString(request.getDetails()));
}).rooms();
}
}

View File

@ -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<INode> 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<INode> 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<EntityIdentifier> users) {
return rest.queryPostWithBody(
"/nodes/" + identifier.getValue() + "/pick",
PickedRoomResponse.class,
params -> {}, String.join(",", users.stream().map(user -> user.getValue()).toList())
);
}
}

View File

@ -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<ShortResponseRoom> 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<ResponseRoom> 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));
});
}
}

View File

@ -5,12 +5,12 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*; import okhttp3.*;
import org.jetbrains.annotations.ApiStatus.Internal; 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.NotEnoughPermissions;
import ru.dragonestia.picker.api.impl.exception.AuthException; 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.RoomPickerClient;
import ru.dragonestia.picker.api.impl.util.type.HttpMethod; 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.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -22,11 +22,14 @@ import java.util.function.Consumer;
public class RestTemplate { public class RestTemplate {
private final RoomPickerClient client; private final RoomPickerClient client;
private final Consumer<Account> accountConsumer;
private final OkHttpClient httpClient; private final OkHttpClient httpClient;
private final ObjectMapper json; private final ObjectMapper json;
public RestTemplate(RoomPickerClient client) { public RestTemplate(RoomPickerClient client, Consumer<Account> accountConsumer) {
this.client = client; this.client = client;
this.accountConsumer = accountConsumer;
httpClient = new OkHttpClient(); httpClient = new OkHttpClient();
json = configureJackson(); json = configureJackson();
} }
@ -119,8 +122,12 @@ public class RestTemplate {
throw new NotEnoughPermissions("Not enough permissions"); 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); 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) { if (statusCode == 5) {