!refactored client-api repositories

This commit is contained in:
Andrey Terentev 2024-05-13 15:20:34 +07:00 committed by Andrey Terentev
parent d66fb76c48
commit 1c81d9b37a
94 changed files with 539 additions and 2720 deletions

View File

@ -9,13 +9,6 @@ repositories {
dependencies {
api 'org.jetbrains:annotations:24.1.0'
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
tasks.jar {

View File

@ -1,39 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public class AccountDoesNotExistsException extends ApiException {
public static final String ERROR_ID = "err.account.does_not_exists";
private final String accountId;
public AccountDoesNotExistsException(String accountId) {
this.accountId = accountId;
}
public AccountDoesNotExistsException(ErrorResponse errorResponse) {
this(errorResponse.details().get("accountId"));
}
public String getAccountId() {
return accountId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public String getMessage() {
return "Account with id '" + accountId + "' does not exists";
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("accountId", accountId);
}
}

View File

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

View File

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

View File

@ -1,10 +1,11 @@
package ru.dragonestia.picker.api.exception;
import java.util.Map;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public abstract class ApiException extends RuntimeException {
public abstract String getErrorId();
public abstract void appendDetailsToErrorResponse(Map<String, String> details);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface ApiException {}

View File

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

View File

@ -1,16 +0,0 @@
package ru.dragonestia.picker.api.exception;
import java.util.Map;
public class ConstantAdminParamsException extends ApiException {
public static final String ERROR_ID = "err.account.admin.modification";
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {}
}

View File

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

View File

@ -1,40 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class ExceptionFactory {
private final static Map<String, Constructor> factory = init();
private ExceptionFactory() {}
private static Map<String, Constructor> init() {
var factory = new HashMap<String, Constructor>();
factory.put(InvalidInstanceIdentifierException.ERROR_ID, InvalidInstanceIdentifierException::new);
factory.put(InvalidRoomIdentifierException.ERROR_ID, InvalidRoomIdentifierException::new);
factory.put(InvalidUsernamesException.ERROR_ID, InvalidUsernamesException::new);
factory.put(InstanceAlreadyExistException.ERROR_ID, InstanceAlreadyExistException::new);
factory.put(InstanceNotFoundException.ERROR_ID, InstanceNotFoundException::new);
factory.put(NoRoomsAvailableException.ERROR_ID, NoRoomsAvailableException::new);
factory.put(RoomAlreadyExistException.ERROR_ID, RoomAlreadyExistException::new);
factory.put(RoomAreFullException.ERROR_ID, RoomAreFullException::new);
factory.put(RoomNotFoundException.ERROR_ID, RoomNotFoundException::new);
factory.put(NotPersistedNodeException.ERROR_ID, NotPersistedNodeException::new);
factory.put(AccountDoesNotExistsException.ERROR_ID, AccountDoesNotExistsException::new);
factory.put(PermissionNotFoundException.ERROR_ID, PermissionNotFoundException::new);
factory.put(ConstantAdminParamsException.ERROR_ID, err -> new ConstantAdminParamsException());
return factory;
}
public static ApiException of(ErrorResponse errorResponse) {
return factory.get(errorResponse.errorId()).apply(errorResponse);
}
private interface Constructor extends Function<ErrorResponse, ApiException> {}
}

View File

@ -1,39 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class InstanceAlreadyExistException extends ApiException {
public static final String ERROR_ID = "err.node.already_exists";
private final String nodeId;
public InstanceAlreadyExistException(String nodeId) {
this.nodeId = nodeId;
}
public InstanceAlreadyExistException(ErrorResponse errorResponse) {
this(errorResponse.details().get("nodeId"));
}
@Override
public String getMessage() {
return "Node with identifier '" + nodeId + "' is already exists";
}
public String getNodeId() {
return nodeId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("nodeId", getNodeId());
}
}

View File

@ -1,39 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class InstanceNotFoundException extends ApiException {
public static final String ERROR_ID = "err.instance.not_found";
private final String nodeId;
public InstanceNotFoundException(String nodeId) {
this.nodeId = nodeId;
}
public InstanceNotFoundException(ErrorResponse errorResponse) {
this(errorResponse.details().get("instance"));
}
@Override
public String getMessage() {
return "Instance '" + nodeId + "' does not found";
}
public String getNodeId() {
return nodeId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("instance", getNodeId());
}
}

View File

@ -0,0 +1,15 @@
package ru.dragonestia.picker.api.exception;
import org.jetbrains.annotations.Nullable;
@ApiException
public class InvalidIdentifierException extends RuntimeException {
public InvalidIdentifierException(String message) {
super(message);
}
public static InvalidIdentifierException taken(@Nullable String input) {
return new InvalidIdentifierException("Taken identifier: " + input);
}
}

View File

@ -1,39 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class InvalidInstanceIdentifierException extends ApiException {
public static final String ERROR_ID = "err.node.invalid_identifier";
private final String nodeId;
public InvalidInstanceIdentifierException(String nodeId) {
this.nodeId = nodeId;
}
public InvalidInstanceIdentifierException(ErrorResponse errorResponse) {
this(errorResponse.details().get("identifier"));
}
@Override
public String getMessage() {
return "Invalid node identifier. Taken '" + nodeId + "'";
}
public String getNodeId() {
return nodeId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("identifier", getNodeId());
}
}

View File

@ -1,46 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class InvalidRoomIdentifierException extends ApiException {
public static final String ERROR_ID = "err.room.invalid_identifier";
private final String nodeId;
private final String roomId;
public InvalidRoomIdentifierException(String nodeId, String roomId) {
this.nodeId = nodeId;
this.roomId = roomId;
}
public InvalidRoomIdentifierException(ErrorResponse errorResponse) {
this(errorResponse.details().get("nodeId"), errorResponse.details().get("roomId"));
}
@Override
public String getMessage() {
return "Invalid room identifier. Taken '" + roomId + "'";
}
public String getRoomId() {
return roomId;
}
public String getNodeId() {
return nodeId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("nodeId", getNodeId());
details.put("roomId", getRoomId());
}
}

View File

@ -1,50 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.*;
import java.util.function.Function;
public final class InvalidUsernamesException extends ApiException {
public static final String ERROR_ID = "err.user.invalid_identifier";
private final List<String> givenUsernames;
private final List<String> invalidUsernames;
public InvalidUsernamesException(List<String> givenUsernames, List<String> invalidUsernames) {
this.givenUsernames = givenUsernames;
this.invalidUsernames = invalidUsernames;
}
public InvalidUsernamesException(ErrorResponse errorResponse) {
this(Arrays.stream(errorResponse.details().get("givenUsernames").split(",")).toList(),
Arrays.stream(errorResponse.details().get("invalidUsernames").split(",")).toList());
}
@Override
public String getMessage() {
return "Users with invalid identifiers were found";
}
public List<String> getGivenUsernames() {
return givenUsernames;
}
public List<String> getInvalidUsernames() {
return invalidUsernames;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
Function<Collection<String>, String> toString = input -> String.join(",", input);
details.put("givenUsernames", toString.apply(getGivenUsernames()));
details.put("invalidUsernames", toString.apply(getInvalidUsernames()));
}
}

View File

@ -1,39 +1,9 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
@ApiException
public class NoRoomsAvailableException extends RuntimeException {
import java.util.Map;
public final class NoRoomsAvailableException extends ApiException {
public static final String ERROR_ID = "err.room.no_available";
private final String nodeId;
public NoRoomsAvailableException(String nodeId) {
this.nodeId = nodeId;
}
public NoRoomsAvailableException(ErrorResponse errorResponse) {
this(errorResponse.details().get("nodeId"));
}
@Override
public String getMessage() {
return "There are no rooms available in node '" + nodeId + "'";
}
public String getNodeId() {
return nodeId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("nodeId", getNodeId());
public NoRoomsAvailableException(String message) {
super(message);
}
}

View File

@ -1,46 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class NotPersistedNodeException extends ApiException {
public final static String ERROR_ID = "rr.room.node_not_persisted";
private final String nodeId;
private final String roomId;
public NotPersistedNodeException(String nodeId, String roomId) {
this.nodeId = nodeId;
this.roomId = roomId;
}
public NotPersistedNodeException(ErrorResponse errorResponse) {
this(errorResponse.details().get("node"), errorResponse.details().get("room"));
}
@Override
public String getMessage() {
return "Cannot create persist room '%s' in non persist node '%s'".formatted(getRoomId(), getNodeId());
}
public String getNodeId() {
return nodeId;
}
public String getRoomId() {
return roomId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("node", getNodeId());
details.put("room", getRoomId());
}
}

View File

@ -1,34 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class PermissionNotFoundException extends ApiException {
public static final String ERROR_ID = "err.permission.not_found";
private final String permissionId;
public PermissionNotFoundException(String permissionId) {
this.permissionId = permissionId;
}
public PermissionNotFoundException(ErrorResponse errorResponse) {
this(errorResponse.details().get("permissionId"));
}
public String getPermissionId() {
return permissionId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("permissionId", permissionId);
}
}

View File

@ -1,47 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class RoomAlreadyExistException extends ApiException {
public static final String ERROR_ID = "err.room.already_exists";
private final String nodeId;
private final String roomId;
public RoomAlreadyExistException(String nodeId, String roomId) {
this.nodeId = nodeId;
this.roomId = roomId;
}
public RoomAlreadyExistException(ErrorResponse errorResponse) {
this(errorResponse.details().get("nodeId"),
errorResponse.details().get("roomId"));
}
@Override
public String getMessage() {
return "Room with identifier '" + roomId + "' in node '" + nodeId + "' is already exists";
}
public String getNodeId() {
return nodeId;
}
public String getRoomId() {
return roomId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("nodeId", getNodeId());
details.put("roomId", getRoomId());
}
}

View File

@ -1,47 +1,9 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
@ApiException
public class RoomAreFullException extends RuntimeException {
import java.util.Map;
public final class RoomAreFullException extends ApiException {
public static final String ERROR_ID = "err.room.are_full";
private final String nodeId;
private final String roomId;
public RoomAreFullException(String nodeId, String roomId) {
this.nodeId = nodeId;
this.roomId = roomId;
}
public RoomAreFullException(ErrorResponse errorResponse) {
this(errorResponse.details().get("nodeId"),
errorResponse.details().get("roomId"));
}
@Override
public String getMessage() {
return "Room with identifier '" + roomId + "' in node '" + nodeId + "' are full";
}
public String getNodeId() {
return nodeId;
}
public String getRoomId() {
return roomId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("nodeId", getNodeId());
details.put("roomId", getRoomId());
public RoomAreFullException(String message) {
super(message);
}
}

View File

@ -1,47 +0,0 @@
package ru.dragonestia.picker.api.exception;
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
import java.util.Map;
public final class RoomNotFoundException extends ApiException {
public static final String ERROR_ID = "err.room.not_found";
private final String nodeId;
private final String roomId;
public RoomNotFoundException(String nodeId, String roomId) {
this.nodeId = nodeId;
this.roomId = roomId;
}
public RoomNotFoundException(ErrorResponse errorResponse) {
this(errorResponse.details().get("nodeId"),
errorResponse.details().get("roomId"));
}
@Override
public String getMessage() {
return "Room '" + roomId + "' in node '" + nodeId + "' does not found";
}
public String getNodeId() {
return nodeId;
}
public String getRoomId() {
return roomId;
}
@Override
public String getErrorId() {
return ERROR_ID;
}
@Override
public void appendDetailsToErrorResponse(Map<String, String> details) {
details.put("node", getNodeId());
details.put("room", getRoomId());
}
}

View File

@ -0,0 +1,5 @@
package ru.dragonestia.picker.api.model.account;
import java.util.List;
public record Account(AccountId id, List<Permission> permissions, boolean locked) {}

View File

@ -0,0 +1,44 @@
package ru.dragonestia.picker.api.model.account;
import ru.dragonestia.picker.api.exception.InvalidIdentifierException;
import java.util.Objects;
public final class AccountId {
private final String value;
private AccountId(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AccountId accountId = (AccountId) o;
return Objects.equals(value, accountId.value);
}
public static AccountId of(String identifier) throws InvalidIdentifierException {
if (identifier.matches("^[aA-zZ\\d]{3,32}$")) {
return new AccountId(identifier);
}
throw InvalidIdentifierException.taken(identifier);
}
}

View File

@ -1,16 +0,0 @@
package ru.dragonestia.picker.api.model.account;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
public interface IAccount {
@NotNull String getUsername();
@NotNull String getPassword();
@NotNull Set<String> getPermissions();
boolean isLocked();
}

View File

@ -0,0 +1,11 @@
package ru.dragonestia.picker.api.model.account;
public enum Permission {
ADMIN, // account management
NODE_MANAGEMENT, // create and remove nodes
;
public String getAuthority() {
return "ROLE_" + name();
}
}

View File

@ -1,42 +0,0 @@
package ru.dragonestia.picker.api.model.account;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
public class ResponseAccount implements IAccount {
private String username;
private String password;
private Set<String> permissions;
private boolean locked;
public ResponseAccount() {}
public ResponseAccount(String username, String password, Set<String> permissions, boolean locked) {
this.username = username;
this.password = password;
this.permissions = permissions;
this.locked = locked;
}
@Override
public @NotNull String getUsername() {
return username;
}
@Override
public @NotNull String getPassword() {
return password;
}
@Override
public @NotNull Set<String> getPermissions() {
return permissions;
}
@Override
public boolean isLocked() {
return locked;
}
}

View File

@ -0,0 +1,44 @@
package ru.dragonestia.picker.api.model.entity;
import ru.dragonestia.picker.api.exception.InvalidIdentifierException;
import java.util.Objects;
public final class EntityId {
private final String value;
private EntityId(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EntityId entityId = (EntityId) o;
return Objects.equals(value, entityId.value);
}
public static EntityId of(String identifier) throws InvalidIdentifierException {
if (identifier.matches("^[aA-zZ\\d-.\\s:@_;]{1,64}$")) {
return new EntityId(identifier);
}
throw InvalidIdentifierException.taken(identifier);
}
}

View File

@ -0,0 +1,5 @@
package ru.dragonestia.picker.api.model.instance;
import ru.dragonestia.picker.api.model.instance.type.PickingMethod;
public record Instance(InstanceId id, PickingMethod method, boolean persist) {}

View File

@ -0,0 +1,55 @@
package ru.dragonestia.picker.api.model.instance;
import ru.dragonestia.picker.api.exception.InvalidIdentifierException;
import java.util.Objects;
import java.util.Random;
public final class InstanceId {
private static final Random random = new Random();
private final String value;
private InstanceId(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InstanceId that = (InstanceId) o;
return Objects.equals(value, that.value);
}
public static InstanceId of(String identifier) throws InvalidIdentifierException {
if (identifier.matches("^(?!-)[a-z\\d-]{0,31}[a-z\\d](?!-)$")) {
return new InstanceId(identifier);
}
throw InvalidIdentifierException.taken(identifier);
}
public static InstanceId random() {
char[] chars = new char[32];
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) ('a' + random.nextInt('z' - 'a'));
}
return new InstanceId(new String(chars));
}
}

View File

@ -1,4 +1,4 @@
package ru.dragonestia.picker.api.model.node;
package ru.dragonestia.picker.api.model.instance.type;
public enum PickingMethod {
SEQUENTIAL_FILLING,

View File

@ -1,24 +0,0 @@
package ru.dragonestia.picker.api.model.node;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import java.beans.Transient;
public interface INode {
@NotNull String getIdentifier();
@Transient
default @NotNull NodeIdentifier getIdentifierObject() {
return NodeIdentifier.of(getIdentifier());
}
@NotNull PickingMethod getPickingMethod();
@Transient
@Nullable Boolean isPersist();
@Nullable String getDetail(@NotNull NodeDetails detail);
}

View File

@ -1,49 +0,0 @@
package ru.dragonestia.picker.api.model.node;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
public class NodeDefinition implements INode {
private final String identifier;
private PickingMethod pickingMethod = PickingMethod.SEQUENTIAL_FILLING;
private boolean persist = false;
public NodeDefinition(@NotNull NodeIdentifier identifier) {
this.identifier = identifier.getValue();
}
@Override
public @NotNull String getIdentifier() {
return identifier;
}
@Override
public @NotNull PickingMethod getPickingMethod() {
return pickingMethod;
}
@Contract("_ -> this")
public @NotNull NodeDefinition setPickingMethod(@NotNull PickingMethod pickingMethod) {
this.pickingMethod = pickingMethod;
return this;
}
@Override
public @NotNull Boolean isPersist() {
return persist;
}
@Contract("_ -> this")
public @NotNull NodeDefinition setPersist(boolean value) {
persist = value;
return this;
}
@Override
public @Nullable String getDetail(@NotNull NodeDetails detail) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,5 +0,0 @@
package ru.dragonestia.picker.api.model.node;
public enum NodeDetails {
PERSIST
}

View File

@ -1,77 +0,0 @@
package ru.dragonestia.picker.api.model.node;
import io.swagger.v3.oas.annotations.media.Schema;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.beans.Transient;
import java.util.HashMap;
import java.util.Map;
@Schema(title = "Node")
public class ResponseNode implements INode {
@Schema(description = "Node identifier", example = "test-node")
private String id;
@Schema(description = "Picking method for users between rooms", example = "LEAST_PICKED")
private PickingMethod method;
@Schema(description = "Additional data requested (Key-Value)")
private Map<NodeDetails, String> details;
@Internal
public ResponseNode() {}
public ResponseNode(@NotNull String id, @NotNull PickingMethod pickingMethod) {
this.id = id;
this.method = pickingMethod;
this.details = new HashMap<>();
}
@Override
public @NotNull String getIdentifier() {
return id;
}
@Override
public @NotNull PickingMethod getPickingMethod() {
return method;
}
@Override
public @Nullable Boolean isPersist() {
var val = getDetail(NodeDetails.PERSIST);
return val == null? null : "true".equals(val);
}
@Override
public @Nullable String getDetail(@NotNull NodeDetails detail) {
return details.get(detail);
}
public void putDetail(@NotNull NodeDetails detail, @NotNull String value) {
details.put(detail, value);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof ResponseNode other) {
return id.equals(other.id);
}
return false;
}
@Override
public String toString() {
return "[ResponseNode id='%s' pickingMethod=%s]".formatted(id, method);
}
}

View File

@ -1,47 +0,0 @@
package ru.dragonestia.picker.api.model.room;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomPath;
import java.beans.Transient;
public interface IRoom {
int UNLIMITED_SLOTS = -1;
@NotNull String getIdentifier();
@Transient
default @NotNull RoomIdentifier getIdentifierObject() {
return RoomIdentifier.of(getIdentifier());
}
@NotNull String getInstanceIdentifier();
@Transient
default @NotNull NodeIdentifier getNodeIdentifierObject() {
return NodeIdentifier.of(getInstanceIdentifier());
}
default @NotNull RoomPath getPath() {
return new RoomPath(NodeIdentifier.of(getInstanceIdentifier()), RoomIdentifier.of(getIdentifier()));
}
int getMaxSlots();
default boolean hasUnlimitedSlots() {
return getMaxSlots() == UNLIMITED_SLOTS;
}
boolean isLocked();
@Transient
@Nullable Boolean isPersist();
@Nullable String getPayload();
@Nullable String getDetail(@NotNull RoomDetails detail);
}

View File

@ -1,137 +0,0 @@
package ru.dragonestia.picker.api.model.room;
import io.swagger.v3.oas.annotations.media.Schema;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.RoomPath;
import java.beans.Transient;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Schema(title = "Room")
public class ResponseRoom implements IRoom {
@Schema(description = "Room identifier", example = "test-room")
private String id;
@Schema(description = "Node identifier", example = "test-node")
private String instanceId;
@Schema(description = "Slots for users. -1 - unlimited slots", example = "25")
private int slots;
@Schema(description = "Does picking skip this room?")
private boolean locked;
@Schema(description = "Payload. Some data")
private String payload;
@Schema(description = "Additional data requested (Key-Value)")
private Map<RoomDetails, String> details;
@Internal
public ResponseRoom() {}
public ResponseRoom(@NotNull String id, @NotNull String instanceId, int slots, boolean locked, @NotNull String payload) {
this.id = id;
this.instanceId = instanceId;
this.slots = slots;
this.locked = locked;
this.payload = payload;
details = new HashMap<>();
}
@Override
public @NotNull String getIdentifier() {
return id;
}
@Override
public @NotNull String getInstanceIdentifier() {
return instanceId;
}
@Transient
@Override
public @NotNull RoomPath getPath() {
return IRoom.super.getPath();
}
@Override
public int getMaxSlots() {
return slots;
}
@Contract("_ -> this")
public @NotNull ResponseRoom setSlots(int slots) {
this.slots = slots;
return this;
}
@Override
public boolean isLocked() {
return locked;
}
@Contract("_ -> this")
public @NotNull ResponseRoom setLocked(boolean locked) {
this.locked = locked;
return this;
}
@Override
public @Nullable Boolean isPersist() {
var val = getDetail(RoomDetails.PERSIST);
return val == null? null : "true".equals(val);
}
@Override
public @Nullable String getPayload() {
return payload;
}
@Contract("_ -> this")
public @NotNull ResponseRoom setPayload(@NotNull String payload) {
this.payload = payload;
return this;
}
@Transient
@Override
public boolean hasUnlimitedSlots() {
return IRoom.super.hasUnlimitedSlots();
}
@Override
public @Nullable String getDetail(@NotNull RoomDetails detail) {
return details.get(detail);
}
public void putDetail(@NotNull RoomDetails detail, @NotNull String value) {
details.put(detail, value);
}
@Override
public int hashCode() {
return Objects.hash(id, instanceId);
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof ResponseRoom other) {
return id.equals(other.id) && instanceId.equals(other.instanceId);
}
return false;
}
@Override
public String toString() {
return "[ResponseRoom id='%s' nodeId='%s' slots=%s payload.len=%s]".formatted(id, instanceId, slots, payload.length());
}
}

View File

@ -0,0 +1,5 @@
package ru.dragonestia.picker.api.model.room;
import ru.dragonestia.picker.api.model.instance.InstanceId;
public record Room(RoomId id, InstanceId instanceId, int slots, boolean locked, String payload, boolean persist) {}

View File

@ -1,87 +0,0 @@
package ru.dragonestia.picker.api.model.room;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomIdentifier;
import java.security.InvalidParameterException;
public class RoomDefinition implements IRoom {
private final String id;
private final String nodeId;
private int slots = 5;
private boolean locked = false;
private boolean persist = false;
private String payload = "";
public RoomDefinition(@NotNull NodeIdentifier nodeIdentifier, @NotNull RoomIdentifier roomIdentifier) {
nodeId = nodeIdentifier.getValue();
id = roomIdentifier.getValue();
}
@Override
public @NotNull String getIdentifier() {
return id;
}
@Override
public @NotNull String getInstanceIdentifier() {
return nodeId;
}
@Override
public int getMaxSlots() {
return slots;
}
@Contract("_ -> this")
public @NotNull RoomDefinition setMaxSlots(int value) {
if (value == 0 || value < -1) {
throw new InvalidParameterException("Slots cannot be negative");
}
slots = value;
return this;
}
@Override
public boolean isLocked() {
return locked;
}
@Contract("_ -> this")
public @NotNull RoomDefinition setLocked(boolean value) {
locked = value;
return this;
}
@Override
public @NotNull Boolean isPersist() {
return persist;
}
@Contract("_ -> this")
public @NotNull RoomDefinition setPersist(boolean value) {
persist = value;
return this;
}
@Override
public @Nullable String getPayload() {
return payload;
}
@Contract("_ -> this")
public @NotNull RoomDefinition setPayload(@NotNull String value) {
payload = value;
return this;
}
@Override
public @Nullable String getDetail(@NotNull RoomDetails detail) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,6 +0,0 @@
package ru.dragonestia.picker.api.model.room;
public enum RoomDetails {
COUNT_USERS,
PERSIST
}

View File

@ -0,0 +1,55 @@
package ru.dragonestia.picker.api.model.room;
import ru.dragonestia.picker.api.exception.InvalidIdentifierException;
import java.util.Objects;
import java.util.Random;
public final class RoomId {
private final static Random random = new Random();
private final String value;
private RoomId(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RoomId roomId = (RoomId) o;
return Objects.equals(value, roomId.value);
}
public static RoomId of(String identifier) throws InvalidIdentifierException {
if (identifier.matches("^(?!-)[a-z\\d-]{0,31}[a-z\\d](?!-)$")) {
return new RoomId(identifier);
}
throw InvalidIdentifierException.taken(identifier);
}
public static RoomId random() {
char[] chars = new char[32];
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) ('a' + random.nextInt('z' - 'a'));
}
return new RoomId(new String(chars));
}
}

View File

@ -1,113 +0,0 @@
package ru.dragonestia.picker.api.model.room;
import io.swagger.v3.oas.annotations.media.Schema;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.RoomPath;
import java.beans.Transient;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Schema(title = "Room (Short)")
public class ShortResponseRoom implements IRoom {
@Schema(description = "Room identifier", example = "test-room")
private String id;
@Schema(description = "Node identifier", example = "test-node")
private String instanceId;
@Schema(description = "Slots for users. -1 - unlimited slots", example = "25")
private int slots;
@Schema(description = "Does picking skip this room?")
private boolean locked;
@Schema(description = "Additional data requested (Key-Value)")
private Map<RoomDetails, String> details;
@Internal
public ShortResponseRoom() {}
public ShortResponseRoom(String id, String instanceId, int slots, boolean locked) {
this.id = id;
this.instanceId = instanceId;
this.slots = slots;
this.locked = locked;
this.details = new HashMap<>();
}
@Override
public @NotNull String getIdentifier() {
return id;
}
@Override
public @NotNull String getInstanceIdentifier() {
return instanceId;
}
@Transient
@Override
public @NotNull RoomPath getPath() {
return IRoom.super.getPath();
}
@Override
public int getMaxSlots() {
return slots;
}
@Override
public boolean isLocked() {
return locked;
}
@Override
public @Nullable Boolean isPersist() {
return null;
}
@Override
public @Nullable String getPayload() {
return null;
}
@Transient
@Override
public boolean hasUnlimitedSlots() {
return IRoom.super.hasUnlimitedSlots();
}
@Override
public @Nullable String getDetail(@NotNull RoomDetails detail) {
return details.get(detail);
}
public void putDetail(@NotNull RoomDetails detail, @NotNull String value) {
details.put(detail, value);
}
@Override
public int hashCode() {
return Objects.hash(id, instanceId);
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof ShortResponseRoom other) {
return id.equals(other.id) && instanceId.equals(other.instanceId);
}
return false;
}
@Override
public String toString() {
return "[ShortResponseRoom id='%s' nodeId='%s' slots=%s]".formatted(id, instanceId, slots);
}
}

View File

@ -1,19 +0,0 @@
package ru.dragonestia.picker.api.model.user;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
import java.beans.Transient;
public interface IUser {
@NotNull String getIdentifier();
@Transient
default @NotNull EntityIdentifier getIdentifierObject() {
return EntityIdentifier.of(getIdentifier());
}
@Nullable String getDetail(@NotNull UserDetails detail);
}

View File

@ -1,61 +0,0 @@
package ru.dragonestia.picker.api.model.user;
import io.swagger.v3.oas.annotations.media.Schema;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@Schema(title = "User")
public class ResponseUser implements IUser {
@Schema(description = "User identifier", example = "test-user")
private String id;
@Schema(description = "Additional data requested (Key-Value)")
private Map<UserDetails, String> details;
@Internal
public ResponseUser() {}
public ResponseUser(@NotNull String id) {
this.id = id;
this.details = new HashMap<>();
}
@Override
public @NotNull String getIdentifier() {
return id;
}
@Override
public @Nullable String getDetail(@NotNull UserDetails detail) {
return details.get(detail);
}
public void putDetail(@NotNull UserDetails detail, @NotNull String value) {
details.put(detail, value);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof ResponseUser other) {
return id.equals(other.id);
}
return false;
}
@Override
public String toString() {
return "[ResponseUser id='%s]".formatted(id);
}
}

View File

@ -1,24 +0,0 @@
package ru.dragonestia.picker.api.model.user;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
public class UserDefinition implements IUser {
private final String id;
public UserDefinition(@NotNull EntityIdentifier identifier) {
id = identifier.getValue();
}
@Override
public @NotNull String getIdentifier() {
return id;
}
@Override
public @Nullable String getDetail(@NotNull UserDetails detail) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,5 +0,0 @@
package ru.dragonestia.picker.api.model.user;
public enum UserDetails {
COUNT_ROOMS
}

View File

@ -1,24 +1,5 @@
package ru.dragonestia.picker.api.repository;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.account.IAccount;
import ru.dragonestia.picker.api.model.account.ResponseAccount;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface AccountRepository {
Optional<ResponseAccount> findAccountByUsername(@NotNull String username);
@NotNull List<ResponseAccount> allAccounts();
void createAccount(@NotNull String accountId, @NotNull String password, @NotNull Set<String> permissions);
void removeAccount(@NotNull IAccount account);
void setPermissions(@NotNull IAccount account, @NotNull List<String> permissions);
void setPassword(@NotNull IAccount account, @NotNull String newPassword);
}

View File

@ -0,0 +1,45 @@
package ru.dragonestia.picker.api.repository;
import ru.dragonestia.picker.api.model.entity.EntityId;
import ru.dragonestia.picker.api.model.instance.InstanceId;
import ru.dragonestia.picker.api.model.room.Room;
import ru.dragonestia.picker.api.model.room.RoomId;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public interface EntityRepository {
List<EntityId> searchUsers(EntityId input);
List<Room> getRooms(EntityId entity);
Map<EntityId, List<Room>> getRooms(Collection<EntityId> entities);
List<EntityId> getRoomEntities(InstanceId instanceId, RoomId roomId);
default List<EntityId> getRoomEntities(Room room) {
return getRoomEntities(room.instanceId(), room.id());
}
void linkEntitiesWithRoom(InstanceId instanceId, RoomId roomId, Collection<EntityId> entities, boolean force);
default void linkEntitiesWithRoom(InstanceId instanceId, RoomId roomId, Collection<EntityId> entities) {
linkEntitiesWithRoom(instanceId, roomId, entities, false);
}
default void linkEntitiesWithRoom(Room room, Collection<EntityId> entities) {
linkEntitiesWithRoom(room.instanceId(), room.id(), entities);
}
default void linkEntitiesWithRoom(Room room, Collection<EntityId> entities, boolean force) {
linkEntitiesWithRoom(room.instanceId(), room.id(), entities, force);
}
void unlinkEntitiesFromRoom(InstanceId instanceId, RoomId roomId, Collection<EntityId> entities);
default void unlinkEntitiesFromRoom(Room room, Collection<EntityId> entities) {
unlinkEntitiesFromRoom(room.instanceId(), room.id(), entities);
}
}

View File

@ -0,0 +1,40 @@
package ru.dragonestia.picker.api.repository;
import ru.dragonestia.picker.api.model.entity.EntityId;
import ru.dragonestia.picker.api.model.instance.Instance;
import ru.dragonestia.picker.api.model.instance.InstanceId;
import ru.dragonestia.picker.api.model.instance.type.PickingMethod;
import ru.dragonestia.picker.api.repository.response.ResponseObject;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public interface InstanceRepository {
List<InstanceId> allInstancesIds();
Instance getInstance(InstanceId id);
Map<InstanceId, Instance> getInstances(Collection<InstanceId> ids);
void createInstance(InstanceId id, PickingMethod method, boolean persist);
void deleteInstance(InstanceId id);
default void deleteInstance(Instance instance) {
deleteInstance(instance.id());
}
void deleteInstances(Collection<InstanceId> ids);
default void justDeleteInstances(Collection<Instance> instances) {
deleteInstances(instances.stream().map(Instance::id).toList());
}
default ResponseObject.PickedRoom pickRoom(InstanceId id, Collection<EntityId> entities) {
return pickRoom(id, entities, false);
}
ResponseObject.PickedRoom pickRoom(InstanceId id, Collection<EntityId> entities, boolean dontReturnEntities);
}

View File

@ -1,30 +0,0 @@
package ru.dragonestia.picker.api.repository;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.node.INode;
import ru.dragonestia.picker.api.model.node.NodeDefinition;
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.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 interface NodeRepository {
@NotNull List<INode> allNodes(@NotNull GetAllNodes request);
@NotNull Optional<INode> findNodeById(@NotNull FindNodeById request);
void removeNodesById(@NotNull RemoveNodesByIds removeNodesByIds);
void removeNode(@NotNull INode node);
void saveNode(@NotNull NodeDefinition definition);
@NotNull PickedRoomResponse pickRoom(@NotNull NodeIdentifier identifier, @NotNull Set<EntityIdentifier> users);
}

View File

@ -1,29 +1,57 @@
package ru.dragonestia.picker.api.repository;
import org.jetbrains.annotations.NotNull;
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.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.type.RoomPath;
import ru.dragonestia.picker.api.model.instance.InstanceId;
import ru.dragonestia.picker.api.model.room.Room;
import ru.dragonestia.picker.api.model.room.RoomId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Map;
public interface RoomRepository {
void saveRoom(@NotNull RoomDefinition definition);
List<RoomId> allRoomsIds(InstanceId instanceId);
void removeRooms(@NotNull RemoveRoomsByIds request);
Room getRoom(InstanceId instanceId, RoomId roomId);
void removeRoom(@NotNull IRoom room);
Map<RoomId, Room> getRooms(InstanceId instanceId, Collection<RoomId> rooms);
@NotNull List<ShortResponseRoom> allRooms(@NotNull GetAllRooms request);
void createRoom(InstanceId instanceId, RoomId roomId, int slots, String payload, boolean locked, boolean persist);
@NotNull Optional<ResponseRoom> find(@NotNull FindRoomById request);
void deleteRoom(InstanceId instanceId, RoomId roomId);
void lockRoom(@NotNull RoomPath path, boolean value);
default void deleteRoom(Room room) {
deleteRoom(room.instanceId(), room.id());
}
void deleteRooms(InstanceId instanceId, Collection<RoomId> rooms);
default void deleteRooms(Collection<Room> rooms) {
InstanceId targetInstance = null;
List<RoomId> toDelete = new ArrayList<>();
List<Room> undeleted = new ArrayList<>();
for (var room: rooms) {
if (targetInstance == null) {
targetInstance = room.instanceId();
} else if (!targetInstance.equals(room.instanceId())) {
undeleted.add(room);
continue;
}
toDelete.add(room.id());
}
deleteRooms(targetInstance, toDelete);
if (!undeleted.isEmpty()) {
deleteRooms(undeleted);
}
}
void lockRoom(InstanceId instanceId, RoomId roomId, boolean newState);
default void lockRoom(Room room, boolean newState) {
lockRoom(room.instanceId(), room.id(), newState);
}
}

View File

@ -1,23 +0,0 @@
package ru.dragonestia.picker.api.repository;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.room.ShortResponseRoom;
import ru.dragonestia.picker.api.model.user.ResponseUser;
import ru.dragonestia.picker.api.repository.query.user.*;
import java.util.List;
public interface UserRepository {
void linkUsersWithRoom(@NotNull LinkUsersWithRoom request);
void unlinkUsersFromRoom(@NotNull UnlinkUsersFromRoom request);
@NotNull List<ResponseUser> getAllUsersFormRoom(@NotNull GetAllUsersFromRoom request);
@NotNull List<ResponseUser> searchUsers(@NotNull SearchUsers request);
@NotNull ResponseUser findUserById(@NotNull FindUserById request);
@NotNull List<ShortResponseRoom> findRoomsLinkedWithUser(@NotNull FindRoomsLinkedWithUser request);
}

View File

@ -1,82 +0,0 @@
package ru.dragonestia.picker.api.repository.query.node;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.node.NodeDetails;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FindNodeById {
private final String id;
private final Set<NodeDetails> details;
private FindNodeById(String id, Set<NodeDetails> details) {
this.id = id;
this.details = details;
}
public @NotNull String getId() {
return id;
}
public @NotNull Set<NodeDetails> getDetails() {
return details;
}
@Contract("_ -> new")
public static @NotNull FindNodeById justFind(@NotNull NodeIdentifier identifier) {
return FindNodeById.builder().setId(identifier.getValue()).build();
}
@Contract("_ -> new")
public static @NotNull FindNodeById findWithAllDetails(@NotNull NodeIdentifier identifier) {
return FindNodeById.builder()
.setId(identifier.getValue())
.setDetails(Stream.of(NodeDetails.values()).collect(Collectors.toSet()))
.build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String id = null;
private Set<NodeDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setId(@NotNull String id) {
this.id = id;
return this;
}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<NodeDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull NodeDetails detail) {
this.details.add(detail);
return this;
}
public @NotNull FindNodeById build() {
if (id == null) {
throw new NullPointerException("Id is null");
}
return new FindNodeById(id, Collections.unmodifiableSet(details));
}
}
}

View File

@ -1,55 +0,0 @@
package ru.dragonestia.picker.api.repository.query.node;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.node.NodeDetails;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GetAllNodes {
public static GetAllNodes JUST = GetAllNodes.builder().build();
public static GetAllNodes WITH_ALL_DETAILS = GetAllNodes.builder()
.setDetails(Stream.of(NodeDetails.values()).collect(Collectors.toSet())).build();
private final Set<NodeDetails> details;
private GetAllNodes(Set<NodeDetails> details) {
this.details = details;
}
public @NotNull Set<NodeDetails> getDetails() {
return details;
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private Set<NodeDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<NodeDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull NodeDetails detail) {
this.details.add(detail);
return this;
}
public @NotNull GetAllNodes build() {
return new GetAllNodes(Collections.unmodifiableSet(details));
}
}
}

View File

@ -1,54 +0,0 @@
package ru.dragonestia.picker.api.repository.query.node;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class RemoveNodesByIds {
private final Set<String> nodeIds;
private RemoveNodesByIds(Set<String> nodeIds) {
this.nodeIds = nodeIds;
}
public @NotNull Set<String> getNodeIds() {
return nodeIds;
}
@Contract("_ -> new")
public static @NotNull RemoveNodesByIds just(@NotNull NodeIdentifier nodeIdentifier) {
return RemoveNodesByIds.builder().appendNodeId(nodeIdentifier).build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private Set<NodeIdentifier> nodeIds = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setNodeIds(@NotNull HashSet<NodeIdentifier> nodeIds) {
this.nodeIds = nodeIds;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendNodeId(@NotNull NodeIdentifier nodeId) {
nodeIds.add(nodeId);
return this;
}
public @NotNull RemoveNodesByIds build() {
return new RemoveNodesByIds(nodeIds.stream().map(obj -> obj.getValue()).collect(Collectors.toSet()));
}
}
}

View File

@ -1,103 +0,0 @@
package ru.dragonestia.picker.api.repository.query.room;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.room.RoomDetails;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomIdentifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class FindRoomById {
private final String nodeId;
private final String id;
private final Set<RoomDetails> details;
private FindRoomById(String nodeId, String id, Set<RoomDetails> details) {
this.id = id;
this.nodeId = nodeId;
this.details = details;
}
public @NotNull String getNodeId() {
return nodeId;
}
public @NotNull String getId() {
return id;
}
public @NotNull Set<RoomDetails> getDetails() {
return details;
}
@Contract("_, _ -> new")
public static @NotNull FindRoomById just(@NotNull NodeIdentifier nodeId, @NotNull RoomIdentifier roomId) {
return FindRoomById.builder()
.setNodeId(nodeId)
.setRoomId(roomId)
.build();
}
@Contract("_, _ -> new")
public static @NotNull FindRoomById withAllDetails(@NotNull NodeIdentifier nodeId, @NotNull RoomIdentifier roomId) {
return FindRoomById.builder()
.setNodeId(nodeId)
.setRoomId(roomId)
.setDetails(Arrays.stream(RoomDetails.values()).collect(Collectors.toSet()))
.build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String nodeId = null;
private String roomId = null;
private Set<RoomDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setNodeId(@NotNull NodeIdentifier identifier) {
nodeId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setRoomId(@NotNull RoomIdentifier identifier) {
roomId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<RoomDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull RoomDetails detail) {
details.add(detail);
return this;
}
public @NotNull FindRoomById build() {
if (nodeId == null) {
throw new NullPointerException("Node id is null");
}
if (roomId == null) {
throw new NullPointerException("Room id is null");
}
return new FindRoomById(nodeId, roomId, Collections.unmodifiableSet(details));
}
}
}

View File

@ -1,82 +0,0 @@
package ru.dragonestia.picker.api.repository.query.room;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.room.RoomDetails;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class GetAllRooms {
private final String nodeId;
private final Set<RoomDetails> details;
private GetAllRooms(String nodeId, Set<RoomDetails> details) {
this.nodeId = nodeId;
this.details = details;
}
public @NotNull String getNodeId() {
return nodeId;
}
public @NotNull Set<RoomDetails> getDetails() {
return details;
}
@Contract("_ -> new")
public static @NotNull GetAllRooms just(@NotNull NodeIdentifier nodeIdentifier) {
return GetAllRooms.builder().setNodeId(nodeIdentifier).build();
}
@Contract("_ -> new")
public static @NotNull GetAllRooms withAllDetails(@NotNull NodeIdentifier nodeIdentifier) {
return GetAllRooms.builder()
.setNodeId(nodeIdentifier)
.setDetails(Arrays.stream(RoomDetails.values()).collect(Collectors.toSet()))
.build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String nodeId = null;
private Set<RoomDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setNodeId(@NotNull NodeIdentifier identifier) {
nodeId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<RoomDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull RoomDetails detail) {
details.add(detail);
return this;
}
public @NotNull GetAllRooms build() {
if (nodeId == null) {
throw new NullPointerException("Node id is null");
}
return new GetAllRooms(nodeId, Collections.unmodifiableSet(details));
}
}
}

View File

@ -1,70 +0,0 @@
package ru.dragonestia.picker.api.repository.query.room;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomIdentifier;
import java.util.Set;
import java.util.stream.Collectors;
public class RemoveRoomsByIds {
private final String nodeId;
private final Set<String> roomsIds;
private RemoveRoomsByIds(String nodeId, Set<String> roomIds) {
this.nodeId = nodeId;
this.roomsIds = roomIds;
}
public @NotNull String getNodeId() {
return nodeId;
}
public @NotNull Set<String> getRoomsIds() {
return roomsIds;
}
public static @NotNull RemoveRoomsByIds just(@NotNull NodeIdentifier nodeId, @NotNull RoomIdentifier roomId) {
return RemoveRoomsByIds.builder().setNodeId(nodeId).appendRoomId(roomId).build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String nodeId = null;
private Set<RoomIdentifier> roomsIds;
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setNodeId(@NotNull NodeIdentifier identifier) {
nodeId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setRoomsIds(@NotNull Set<RoomIdentifier> roomIds) {
this.roomsIds = roomIds;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendRoomId(@NotNull RoomIdentifier roomId) {
roomsIds.add(roomId);
return this;
}
public @NotNull RemoveRoomsByIds build() {
if (nodeId == null) {
throw new NullPointerException("Node id is null");
}
return new RemoveRoomsByIds(nodeId, roomsIds.stream().map(o -> o.getValue()).collect(Collectors.toSet()));
}
}
}

View File

@ -1,83 +0,0 @@
package ru.dragonestia.picker.api.repository.query.user;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.room.RoomDetails;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class FindRoomsLinkedWithUser {
private final String userId;
private final Set<RoomDetails> details;
private FindRoomsLinkedWithUser(String userId, Set<RoomDetails> details) {
this.userId = userId;
this.details = details;
}
public @NotNull String getUserId() {
return userId;
}
public @NotNull Set<RoomDetails> getDetails() {
return details;
}
@Contract("_ -> new")
public static @NotNull FindRoomsLinkedWithUser just(@NotNull EntityIdentifier identifier) {
return FindRoomsLinkedWithUser.builder()
.setUserId(identifier)
.build();
}
@Contract("_ -> new")
public static @NotNull FindRoomsLinkedWithUser withAllDetails(@NotNull EntityIdentifier identifier) {
return FindRoomsLinkedWithUser.builder()
.setUserId(identifier)
.setDetails(Arrays.stream(RoomDetails.values()).collect(Collectors.toSet()))
.build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String userId = null;
private Set<RoomDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setUserId(@NotNull EntityIdentifier identifier) {
userId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<RoomDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull RoomDetails detail) {
details.add(detail);
return this;
}
public @NotNull FindRoomsLinkedWithUser build() {
if (userId == null) {
throw new NullPointerException("User id is null");
}
return new FindRoomsLinkedWithUser(userId, details);
}
}
}

View File

@ -1,81 +0,0 @@
package ru.dragonestia.picker.api.repository.query.user;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.user.UserDetails;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
import java.util.*;
import java.util.stream.Collectors;
public class FindUserById {
private final String userId;
private final Set<UserDetails> details;
private FindUserById(String userId, Set<UserDetails> details) {
this.userId = userId;
this.details = details;
}
public @NotNull String getUserId() {
return userId;
}
public @NotNull Set<UserDetails> getDetails() {
return details;
}
@Contract("_ -> new")
public static @NotNull FindUserById just(@NotNull EntityIdentifier userId) {
return builder()
.setUserId(userId)
.build();
}
@Contract("_ -> new")
public static @NotNull FindUserById withAllDetails(@NotNull EntityIdentifier userId) {
return builder()
.setUserId(userId)
.setDetails(Arrays.stream(UserDetails.values()).collect(Collectors.toSet()))
.build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String userId = null;
private Set<UserDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setUserId(@NotNull EntityIdentifier identifier) {
userId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<UserDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull UserDetails detail) {
details.add(detail);
return this;
}
public @NotNull FindUserById build() {
if (userId == null) {
throw new NullPointerException("User id is null");
}
return new FindUserById(userId, Collections.unmodifiableSet(details));
}
}
}

View File

@ -1,101 +0,0 @@
package ru.dragonestia.picker.api.repository.query.user;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.user.UserDetails;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomIdentifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class GetAllUsersFromRoom {
private final String nodeId;
private final String roomId;
private final Set<UserDetails> details;
private GetAllUsersFromRoom(String nodeId, String roomId, Set<UserDetails> details) {
this.nodeId = nodeId;
this.roomId = roomId;
this.details = details;
}
public @NotNull String getNodeId() {
return nodeId;
}
public @NotNull String getRoomId() {
return roomId;
}
public @NotNull Set<UserDetails> getDetails() {
return details;
}
public static @NotNull GetAllUsersFromRoom just(@NotNull NodeIdentifier nodeId, @NotNull RoomIdentifier roomId) {
return GetAllUsersFromRoom.builder()
.setNodeId(nodeId)
.setRoomId(roomId)
.build();
}
public static @NotNull GetAllUsersFromRoom withAllDetails(@NotNull NodeIdentifier nodeId, @NotNull RoomIdentifier roomId) {
return GetAllUsersFromRoom.builder()
.setNodeId(nodeId)
.setRoomId(roomId)
.setDetails(Arrays.stream(UserDetails.values()).collect(Collectors.toSet()))
.build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String nodeId = null;
private String roomId = null;
private Set<UserDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setNodeId(@NotNull NodeIdentifier identifier) {
nodeId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setRoomId(@NotNull RoomIdentifier identifier) {
roomId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<UserDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull UserDetails detail) {
details.add(detail);
return this;
}
public @NotNull GetAllUsersFromRoom build() {
if (nodeId == null) {
throw new NullPointerException("Node id is null");
}
if (roomId == null) {
throw new NullPointerException("Room id is null");
}
return new GetAllUsersFromRoom(nodeId, roomId, Collections.unmodifiableSet(details));
}
}
}

View File

@ -1,100 +0,0 @@
package ru.dragonestia.picker.api.repository.query.user;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomIdentifier;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class LinkUsersWithRoom {
private final String nodeId;
private final String roomId;
private final Set<String> users;
private final boolean ignoreSlotLimitation;
private LinkUsersWithRoom(String nodeId, String roomId, Set<String> users, boolean ignoreSlotLimitation) {
this.nodeId = nodeId;
this.roomId = roomId;
this.users = users;
this.ignoreSlotLimitation = ignoreSlotLimitation;
}
public @NotNull String getNodeId() {
return nodeId;
}
public @NotNull String getRoomId() {
return roomId;
}
public @NotNull Set<String> getUsers() {
return users;
}
public boolean ignoreSlotLimitation() {
return ignoreSlotLimitation;
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String nodeId = null;
private String roomId = null;
private Set<EntityIdentifier> users = new HashSet<>();
private boolean ignoreSlotLimitation = false;
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setNodeId(@NotNull NodeIdentifier identifier) {
nodeId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setRoomId(@NotNull RoomIdentifier identifier) {
roomId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setUsers(@NotNull Set<EntityIdentifier> users) {
this.users = users;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendUser(@NotNull EntityIdentifier user) {
users.add(user);
return this;
}
@Contract("_ -> this")
public @NotNull Builder setIgnoreSlotLimitation(boolean value) {
ignoreSlotLimitation = value;
return this;
}
public @NotNull LinkUsersWithRoom build() {
if (nodeId == null) {
throw new NullPointerException("Node id is null");
}
if (roomId == null) {
throw new NullPointerException("Room id is null");
}
return new LinkUsersWithRoom(nodeId,
roomId,
users.stream().map(o -> o.getValue()).collect(Collectors.toSet()),
ignoreSlotLimitation);
}
}
}

View File

@ -1,84 +0,0 @@
package ru.dragonestia.picker.api.repository.query.user;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.model.user.UserDetails;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class SearchUsers {
private final String searchInput;
private final Set<UserDetails> details;
private SearchUsers(String searchInput, Set<UserDetails> details) {
this.searchInput = searchInput;
this.details = details;
}
public @NotNull String getSearchInput() {
return searchInput;
}
public @NotNull Set<UserDetails> getDetails() {
return details;
}
@Contract("_ -> new")
public static @NotNull SearchUsers just(@NotNull EntityIdentifier searchInput) {
return SearchUsers.builder()
.setSearchInput(searchInput)
.build();
}
@Contract("_ -> new")
public static @NotNull SearchUsers withAllDetails(@NotNull EntityIdentifier searchInput) {
return SearchUsers.builder()
.setSearchInput(searchInput)
.setDetails(Arrays.stream(UserDetails.values()).collect(Collectors.toSet()))
.build();
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String searchInput = null;
private Set<UserDetails> details = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setSearchInput(@NotNull EntityIdentifier input) {
searchInput = input.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setDetails(@NotNull Set<UserDetails> details) {
this.details = details;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendDetail(@NotNull UserDetails detail) {
details.add(detail);
return this;
}
public @NotNull SearchUsers build() {
if (searchInput == null) {
throw new NullPointerException("SearchInput is null");
}
return new SearchUsers(searchInput, Collections.unmodifiableSet(details));
}
}
}

View File

@ -1,86 +0,0 @@
package ru.dragonestia.picker.api.repository.query.user;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.repository.type.NodeIdentifier;
import ru.dragonestia.picker.api.repository.type.RoomIdentifier;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
public class UnlinkUsersFromRoom {
private final String nodeId;
private final String roomId;
private final Set<String> users;
private UnlinkUsersFromRoom(String nodeId, String roomId, Set<String> users) {
this.nodeId = nodeId;
this.roomId = roomId;
this.users = users;
}
public @NotNull String getNodeId() {
return nodeId;
}
public @NotNull String getRoomId() {
return roomId;
}
public @NotNull Set<String> getUsers() {
return users;
}
public static @NotNull Builder builder() {
return new Builder();
}
public static class Builder {
private String nodeId = null;
private String roomId = null;
private Set<EntityIdentifier> users = new HashSet<>();
private Builder() {}
@Contract("_ -> this")
public @NotNull Builder setNodeId(@NotNull NodeIdentifier identifier) {
nodeId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setRoomId(@NotNull RoomIdentifier identifier) {
roomId = identifier.getValue();
return this;
}
@Contract("_ -> this")
public @NotNull Builder setUsers(@NotNull Set<EntityIdentifier> users) {
this.users = users;
return this;
}
@Contract("_ -> this")
public @NotNull Builder appendUser(@NotNull EntityIdentifier user) {
users.add(user);
return this;
}
public @NotNull UnlinkUsersFromRoom build() {
if (nodeId == null) {
throw new NullPointerException("Node id is null");
}
if (roomId == null) {
throw new NullPointerException("Room id is null");
}
return new UnlinkUsersFromRoom(nodeId,
roomId,
users.stream().map(o -> o.getValue()).collect(Collectors.toSet()));
}
}
}

View File

@ -1,7 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import ru.dragonestia.picker.api.model.account.ResponseAccount;
import java.util.List;
public record AllAccountsResponse(List<ResponseAccount> accounts) {}

View File

@ -1,13 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Map;
@Schema(
name = "ErrorResponse",
description = "Response with error info"
)
public record ErrorResponse(String errorId,
String message,
Map<String, String> details) {}

View File

@ -1,9 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(title = "Link users with room", hidden = true)
public record LinkUsersWithRoomResponse(
@Schema(description = "Number of users in room", example = "15") int usedSlots,
@Schema(description = "Maximum number of users in room", example = "20") int totalSlots
) {}

View File

@ -1,9 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.room.ShortResponseRoom;
import java.util.List;
@Schema(title = "Linked rooms with user", hidden = true)
public record LinkedRoomsWithUserResponse(List<ShortResponseRoom> rooms) {}

View File

@ -1,7 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.node.ResponseNode;
@Schema(title = "Node details", hidden = true)
public record NodeDetailsResponse(ResponseNode node) {}

View File

@ -1,9 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.node.ResponseNode;
import java.util.List;
@Schema(title = "List of nodes", hidden = true)
public record NodeListResponse(List<ResponseNode> nodes) {}

View File

@ -1,16 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Set;
@Schema(title = "Result of picking room", hidden = true)
public record PickedRoomResponse(
@Schema(description = "Node identifier", example = "test-node") String nodeId,
@Schema(description = "Room identifier", example = "test-room") String roomId,
@Schema(description = "Payload", example = "Hello world!") String payload,
@Schema(description = "Max slots in room", example = "25") int slots,
@Schema(description = "Used slots in room", example = "5") int usedSlots,
@Schema(description = "Locked for picking?", example = "false") boolean locked,
@Schema(description = "User identifiers") Set<String> users
) {}

View File

@ -0,0 +1,69 @@
package ru.dragonestia.picker.api.repository.response;
import ru.dragonestia.picker.api.model.account.Account;
import ru.dragonestia.picker.api.model.account.AccountId;
import ru.dragonestia.picker.api.model.account.Permission;
import ru.dragonestia.picker.api.model.entity.EntityId;
import ru.dragonestia.picker.api.model.instance.Instance;
import ru.dragonestia.picker.api.model.instance.InstanceId;
import ru.dragonestia.picker.api.model.instance.type.PickingMethod;
import ru.dragonestia.picker.api.model.room.Room;
import ru.dragonestia.picker.api.model.room.RoomId;
import java.util.List;
public final class ResponseObject {
private ResponseObject() {}
public static class RInstance {
private String id;
private PickingMethod method;
private boolean persist;
public Instance convert() {
return new Instance(InstanceId.of(id), method, persist);
}
}
public static class RRoom {
private String id;
private String instanceId;
private int slots;
private boolean locked;
private boolean persist;
private String payload;
public Room covert() {
return new Room(RoomId.of(id), InstanceId.of(instanceId), slots, locked, payload, persist);
}
}
public static class PickedRoom {
private RRoom room;
private List<String> entities;
private transient Room cachedRoom;
private transient List<EntityId> cachedEntities;
public Room getRoom() {
if (cachedRoom == null) cachedRoom = room.covert();
return cachedRoom;
}
public List<EntityId> getEntities() {
if (cachedEntities == null) cachedEntities = entities.stream().map(EntityId::of).toList();
return cachedEntities;
}
}
public static class RAccount {
private String id;
private List<String> permissions;
private boolean locked;
public Account convert() {
return new Account(AccountId.of(id), permissions.stream().map(Permission::valueOf).toList(), locked);
}
}
}

View File

@ -1,7 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.room.ResponseRoom;
@Schema(title = "Room info", hidden = true)
public record RoomInfoResponse(ResponseRoom room) {}

View File

@ -1,12 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.room.ShortResponseRoom;
import java.util.List;
@Schema(title = "Room list", hidden = true)
public record RoomListResponse(
@Schema(description = "Node identifier", example = "test-node") String node,
List<ShortResponseRoom> rooms
) {}

View File

@ -1,8 +1,3 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(title = "Server info", hidden = true)
public record RoomPickerInfoResponse(
@Schema(description = "RoomPicker server version", example = "0.0.1") String version
) {}
public record RoomPickerInfoResponse(String version) {}

View File

@ -1,13 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.user.ResponseUser;
import java.util.List;
@Schema(title = "Users inside room", hidden = true)
public record RoomUserListResponse(
@Schema(description = "Number of users in room", example = "15") int slots,
@Schema(description = "Maximum number of users in room", example = "20") int usedSlots,
@Schema(description = "Users") List<ResponseUser> users
) {}

View File

@ -1,9 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.user.ResponseUser;
import java.util.List;
@Schema(title = "Search user", hidden = true)
public record SearchUserResponse(List<ResponseUser> users) {}

View File

@ -1,7 +0,0 @@
package ru.dragonestia.picker.api.repository.response;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.user.ResponseUser;
@Schema(title = "User details", hidden = true)
public record UserDetailsResponse(ResponseUser user) {}

View File

@ -1,24 +0,0 @@
package ru.dragonestia.picker.api.repository.type;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.util.IdentifierValidator;
import java.security.InvalidParameterException;
public class EntityIdentifier extends ValueObject<String> {
private EntityIdentifier(String value) {
super(value);
}
@Override
protected void validate(String value) {
if (IdentifierValidator.forUser(value)) return;
throw new InvalidParameterException("Invalid user identifier");
}
public static @NotNull EntityIdentifier of(@NotNull String identifier) {
return new EntityIdentifier(identifier);
}
}

View File

@ -1,24 +0,0 @@
package ru.dragonestia.picker.api.repository.type;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.util.IdentifierValidator;
import java.security.InvalidParameterException;
public final class NodeIdentifier extends ValueObject<String> {
private NodeIdentifier(String value) {
super(value);
}
@Override
protected void validate(String value) {
if (IdentifierValidator.forNode(value)) return;
throw new InvalidParameterException("Invalid node identifier");
}
public static NodeIdentifier of(@NotNull String value) {
return new NodeIdentifier(value);
}
}

View File

@ -1,24 +0,0 @@
package ru.dragonestia.picker.api.repository.type;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.util.IdentifierValidator;
import java.security.InvalidParameterException;
public class RoomIdentifier extends ValueObject<String> {
private RoomIdentifier(String value) {
super(value);
}
@Override
protected void validate(String value) {
if(IdentifierValidator.forRoom(value)) return;
throw new InvalidParameterException("Invalid room identifier");
}
public static RoomIdentifier of(@NotNull String value) {
return new RoomIdentifier(value);
}
}

View File

@ -1,34 +0,0 @@
package ru.dragonestia.picker.api.repository.type;
import org.jetbrains.annotations.NotNull;
public record RoomPath(@NotNull NodeIdentifier nodeIdentifier, @NotNull RoomIdentifier roomIdentifier) {
public @NotNull String getNodeId() {
return nodeIdentifier.getValue();
}
public @NotNull String getRoomId() {
return roomIdentifier.getValue();
}
@Override
public String toString() {
return nodeIdentifier.getValue() + "/" + roomIdentifier.getValue();
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof RoomPath other) {
return toString().equals(other.toString());
}
return false;
}
}

View File

@ -1,18 +0,0 @@
package ru.dragonestia.picker.api.repository.type;
abstract class ValueObject<T> {
private final T value;
ValueObject(T value) {
validate(value);
this.value = value;
}
protected abstract void validate(T value);
public T getValue() {
return value;
}
}

View File

@ -1,20 +0,0 @@
package ru.dragonestia.picker.api.util;
import org.jetbrains.annotations.NotNull;
public class IdentifierValidator {
private IdentifierValidator() {}
public static boolean forNode(@NotNull String nodeId) {
return nodeId.matches("^(?!-)[a-z\\d-]{0,31}[a-z\\d](?!-)$");
}
public static boolean forRoom(@NotNull String roomId) {
return roomId.matches("^(?!-)[a-z\\d-]{0,35}[a-z\\d](?!-)$");
}
public static boolean forUser(@NotNull String username) {
return username.matches("^[aA-zZ\\d-.\\s:@_;]{1,64}$");
}
}

View File

@ -1,51 +0,0 @@
package util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import ru.dragonestia.picker.api.util.IdentifierValidator;
import java.util.UUID;
public class IdentifierValidatorTests {
@Test
void test_forNodes() {
Assertions.assertFalse(IdentifierValidator.forNode(""));
Assertions.assertTrue(IdentifierValidator.forNode("a"));
Assertions.assertTrue(IdentifierValidator.forNode("aboba123"));
Assertions.assertFalse(IdentifierValidator.forNode("Aboba123"));
Assertions.assertTrue(IdentifierValidator.forNode("node-identifier"));
Assertions.assertFalse(IdentifierValidator.forNode("node identifier"));
Assertions.assertFalse(IdentifierValidator.forNode("-"));
Assertions.assertFalse(IdentifierValidator.forNode("-a"));
Assertions.assertFalse(IdentifierValidator.forNode("a-"));
Assertions.assertTrue(IdentifierValidator.forNode("a".repeat(32)));
Assertions.assertFalse(IdentifierValidator.forNode("a".repeat(33)));
}
@Test
void test_forRooms() {
Assertions.assertFalse(IdentifierValidator.forRoom(""));
Assertions.assertTrue(IdentifierValidator.forRoom("a"));
Assertions.assertTrue(IdentifierValidator.forRoom("aboba123"));
Assertions.assertFalse(IdentifierValidator.forRoom("Aboba123"));
Assertions.assertTrue(IdentifierValidator.forRoom("node-identifier"));
Assertions.assertFalse(IdentifierValidator.forRoom("node identifier"));
Assertions.assertFalse(IdentifierValidator.forRoom("-"));
Assertions.assertFalse(IdentifierValidator.forRoom("-a"));
Assertions.assertFalse(IdentifierValidator.forRoom("a-"));
Assertions.assertTrue(IdentifierValidator.forRoom("a".repeat(36)));
Assertions.assertFalse(IdentifierValidator.forRoom("a".repeat(37)));
}
@Test
void test_forUsers() {
Assertions.assertFalse(IdentifierValidator.forUser(""));
Assertions.assertTrue(IdentifierValidator.forUser("a"));
Assertions.assertTrue(IdentifierValidator.forUser("a".repeat(64)));
Assertions.assertFalse(IdentifierValidator.forUser("a".repeat(65)));
Assertions.assertTrue(IdentifierValidator.forUser("aboba"));
Assertions.assertTrue(IdentifierValidator.forUser("AboBa-AAA @aaa:aaa_aa;aa.aaa99"));
Assertions.assertTrue(IdentifierValidator.forUser(UUID.randomUUID().toString()));
}
}

View File

@ -6,15 +6,15 @@ 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.NodeRepositoryImpl;
import ru.dragonestia.picker.api.impl.repository.InstanceRepositoryImpl;
import ru.dragonestia.picker.api.impl.repository.RoomRepositoryImpl;
import ru.dragonestia.picker.api.impl.repository.UserRepositoryImpl;
import ru.dragonestia.picker.api.impl.repository.EntityRepositoryImpl;
import ru.dragonestia.picker.api.impl.util.RestTemplate;
import ru.dragonestia.picker.api.impl.util.type.HttpMethod;
import ru.dragonestia.picker.api.repository.AccountRepository;
import ru.dragonestia.picker.api.repository.NodeRepository;
import ru.dragonestia.picker.api.repository.InstanceRepository;
import ru.dragonestia.picker.api.repository.RoomRepository;
import ru.dragonestia.picker.api.repository.UserRepository;
import ru.dragonestia.picker.api.repository.EntityRepository;
import ru.dragonestia.picker.api.repository.response.RoomPickerInfoResponse;
public class RoomPickerClient {
@ -23,9 +23,9 @@ public class RoomPickerClient {
private final String username;
private final String password;
private final RestTemplate restTemplate;
private final NodeRepository nodeRepository;
private final InstanceRepository instanceRepository;
private final RoomRepository roomRepository;
private final UserRepository userRepository;
private final EntityRepository entityRepository;
private final AccountRepository accountRepository;
public RoomPickerClient(@NotNull String url, @NotNull String username, @NotNull String password) {
@ -33,9 +33,9 @@ public class RoomPickerClient {
this.username = username;
this.password = password;
this.restTemplate = new RestTemplate(this);
this.nodeRepository = new NodeRepositoryImpl(this);
this.instanceRepository = new InstanceRepositoryImpl(this);
this.roomRepository = new RoomRepositoryImpl(this);
this.userRepository = new UserRepositoryImpl(this);
this.entityRepository = new EntityRepositoryImpl(this);
this.accountRepository = new AccountRepositoryImpl(this);
}
@ -51,16 +51,16 @@ public class RoomPickerClient {
.addHeader("Authorization", Credentials.basic(username, password));
}
public @NotNull NodeRepository getNodeRepository() {
return nodeRepository;
public @NotNull InstanceRepository getNodeRepository() {
return instanceRepository;
}
public @NotNull RoomRepository getRoomRepository() {
return roomRepository;
}
public @NotNull UserRepository getUserRepository() {
return userRepository;
public @NotNull EntityRepository getUserRepository() {
return entityRepository;
}
public @NotNull AccountRepository getAccountRepository() {

View File

@ -8,7 +8,7 @@ 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.UserRepository;
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;
@ -17,12 +17,12 @@ import ru.dragonestia.picker.api.repository.response.UserDetailsResponse;
import java.util.List;
public class UserRepositoryImpl implements UserRepository {
public class EntityRepositoryImpl implements EntityRepository {
private final RestTemplate rest;
@Internal
public UserRepositoryImpl(RoomPickerClient client) {
public EntityRepositoryImpl(RoomPickerClient client) {
rest = client.getRestTemplate();
}

View File

@ -9,7 +9,7 @@ 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.NodeRepository;
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;
@ -23,12 +23,12 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
public class NodeRepositoryImpl implements NodeRepository {
public class InstanceRepositoryImpl implements InstanceRepository {
private final RestTemplate rest;
@Internal
public NodeRepositoryImpl(RoomPickerClient client) {
public InstanceRepositoryImpl(RoomPickerClient client) {
rest = client.getRestTemplate();
}

View File

@ -16,7 +16,7 @@ import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import ru.dragonestia.picker.api.model.node.INode;
import ru.dragonestia.picker.api.model.node.NodeDetails;
import ru.dragonestia.picker.api.repository.NodeRepository;
import ru.dragonestia.picker.api.repository.InstanceRepository;
import ru.dragonestia.picker.api.repository.query.node.GetAllNodes;
import java.util.Comparator;
@ -24,14 +24,14 @@ import java.util.List;
public class NodeList extends VerticalLayout implements RefreshableTable {
private final NodeRepository nodeRepository;
private final InstanceRepository instanceRepository;
private final Grid<INode> nodesGrid;
private final TextField searchField;
private List<INode> cachedNodes;
public NodeList(NodeRepository nodeRepository) {
public NodeList(InstanceRepository instanceRepository) {
super();
this.nodeRepository = nodeRepository;
this.instanceRepository = instanceRepository;
add(new H2("Nodes"));
add(searchField = createSearchField());
@ -139,13 +139,13 @@ public class NodeList extends VerticalLayout implements RefreshableTable {
}
private void removeNode(INode node) {
nodeRepository.removeNode(node);
instanceRepository.removeNode(node);
refresh();
}
@Override
public void refresh() {
cachedNodes = nodeRepository.allNodes(GetAllNodes.WITH_ALL_DETAILS);
cachedNodes = instanceRepository.allNodes(GetAllNodes.WITH_ALL_DETAILS);
applySearch(searchField.getValue());
}

View File

@ -12,7 +12,7 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import ru.dragonestia.picker.api.model.room.IRoom;
import ru.dragonestia.picker.api.model.user.IUser;
import ru.dragonestia.picker.api.model.user.UserDetails;
import ru.dragonestia.picker.api.repository.UserRepository;
import ru.dragonestia.picker.api.repository.EntityRepository;
import ru.dragonestia.picker.api.repository.query.user.GetAllUsersFromRoom;
import ru.dragonestia.picker.api.repository.query.user.UnlinkUsersFromRoom;
@ -24,16 +24,16 @@ import java.util.stream.Collectors;
public class UserList extends VerticalLayout implements RefreshableTable {
private final IRoom room;
private final UserRepository userRepository;
private final EntityRepository entityRepository;
private final Button buttonRemove;
private final Grid<IUser> usersGrid;
private final Span totalUsers = new Span();
private final Span occupancy = new Span();
private List<IUser> cachedUsers = new ArrayList<>();
public UserList(IRoom room, UserRepository userRepository) {
public UserList(IRoom room, EntityRepository entityRepository) {
this.room = room;
this.userRepository = userRepository;
this.entityRepository = entityRepository;
buttonRemove = createButtonRemove();
add(usersGrid = createUsersGrid());
@ -49,7 +49,7 @@ public class UserList extends VerticalLayout implements RefreshableTable {
button.addClickListener(event -> {
var users = usersGrid.getSelectedItems();
if (users.isEmpty()) return;
userRepository.unlinkUsersFromRoom(UnlinkUsersFromRoom.builder()
entityRepository.unlinkUsersFromRoom(UnlinkUsersFromRoom.builder()
.setNodeId(room.getNodeIdentifierObject())
.setRoomId(room.getIdentifierObject())
.setUsers(users.stream().map(IUser::getIdentifierObject).collect(Collectors.toSet()))
@ -121,7 +121,7 @@ public class UserList extends VerticalLayout implements RefreshableTable {
@Override
public void refresh() {
cachedUsers = userRepository.getAllUsersFormRoom(GetAllUsersFromRoom.withAllDetails(room.getNodeIdentifierObject(), room.getIdentifierObject()))
cachedUsers = entityRepository.getAllUsersFormRoom(GetAllUsersFromRoom.withAllDetails(room.getNodeIdentifierObject(), room.getIdentifierObject()))
.stream().map(user -> (IUser) user).toList();
usersGrid.setItems(cachedUsers);
totalUsers.setText("Total users: " + cachedUsers.size());

View File

@ -8,7 +8,7 @@ import com.vaadin.flow.router.RouteAlias;
import jakarta.annotation.security.RolesAllowed;
import org.springframework.beans.factory.annotation.Autowired;
import ru.dragonestia.picker.api.exception.ApiException;
import ru.dragonestia.picker.api.repository.NodeRepository;
import ru.dragonestia.picker.api.repository.InstanceRepository;
import ru.dragonestia.picker.cp.component.NavPath;
import ru.dragonestia.picker.cp.component.NodeList;
import ru.dragonestia.picker.cp.component.RegisterNode;
@ -20,12 +20,12 @@ import ru.dragonestia.picker.cp.service.SecurityService;
@Route(value = "/instances", layout = MainLayout.class)
public class NodesPage extends VerticalLayout {
private final NodeRepository nodeRepository;
private final InstanceRepository instanceRepository;
private final NodeList nodeList;
@Autowired
public NodesPage(SecurityService securityService) {
this.nodeRepository = securityService.getAuthenticatedAccount().getClient().getNodeRepository();
this.instanceRepository = securityService.getAuthenticatedAccount().getClient().getNodeRepository();
add(NavPath.rootNodes());
@ -40,7 +40,7 @@ public class NodesPage extends VerticalLayout {
protected RegisterNode createRegisterNodeElement() {
return new RegisterNode(nodeDefinition -> {
try {
nodeRepository.saveNode(nodeDefinition);
instanceRepository.saveNode(nodeDefinition);
return new RegisterNode.Response(false, "");
} catch (ApiException ex) {
return new RegisterNode.Response(true, ex.getMessage());
@ -51,6 +51,6 @@ public class NodesPage extends VerticalLayout {
}
protected NodeList createNodeListElement() {
return new NodeList(nodeRepository);
return new NodeList(instanceRepository);
}
}

View File

@ -17,7 +17,7 @@ import jakarta.annotation.security.RolesAllowed;
import org.springframework.beans.factory.annotation.Autowired;
import ru.dragonestia.picker.api.model.user.IUser;
import ru.dragonestia.picker.api.model.user.UserDetails;
import ru.dragonestia.picker.api.repository.UserRepository;
import ru.dragonestia.picker.api.repository.EntityRepository;
import ru.dragonestia.picker.api.repository.query.user.SearchUsers;
import ru.dragonestia.picker.api.repository.type.EntityIdentifier;
import ru.dragonestia.picker.cp.component.RefreshableTable;
@ -32,7 +32,7 @@ import java.util.Objects;
@Route(value = "/users", layout = MainLayout.class)
public class UserSearchPage extends VerticalLayout implements RefreshableTable {
private final UserRepository userRepository;
private final EntityRepository entityRepository;
private final TextField fieldUsername;
private final Grid<IUser> userGrid;
private final Span foundUsers;
@ -40,7 +40,7 @@ public class UserSearchPage extends VerticalLayout implements RefreshableTable {
@Autowired
public UserSearchPage(SecurityService securityService) {
this.userRepository = securityService.getAuthenticatedAccount().getClient().getUserRepository();
this.entityRepository = securityService.getAuthenticatedAccount().getClient().getUserRepository();
foundUsers = new Span();
add(fieldUsername = createUsernameInputField());
@ -98,7 +98,7 @@ public class UserSearchPage extends VerticalLayout implements RefreshableTable {
userGrid.setItems();
}
userGrid.setItems(cachedUsers = userRepository.searchUsers(SearchUsers.withAllDetails(EntityIdentifier.of(input)))
userGrid.setItems(cachedUsers = entityRepository.searchUsers(SearchUsers.withAllDetails(EntityIdentifier.of(input)))
.stream().map(user -> (IUser) user).toList());
}

View File

@ -2,9 +2,9 @@ package ru.dragonestia.picker.cp.util;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.picker.api.impl.RoomPickerClient;
import ru.dragonestia.picker.api.repository.NodeRepository;
import ru.dragonestia.picker.api.repository.InstanceRepository;
import ru.dragonestia.picker.api.repository.RoomRepository;
import ru.dragonestia.picker.api.repository.UserRepository;
import ru.dragonestia.picker.api.repository.EntityRepository;
public class AdminRoomPickerClient extends RoomPickerClient {
@ -13,7 +13,7 @@ public class AdminRoomPickerClient extends RoomPickerClient {
}
@Override
public @NotNull NodeRepository getNodeRepository() {
public @NotNull InstanceRepository getNodeRepository() {
throw new UnsupportedOperationException();
}
@ -23,7 +23,7 @@ public class AdminRoomPickerClient extends RoomPickerClient {
}
@Override
public @NotNull UserRepository getUserRepository() {
public @NotNull EntityRepository getUserRepository() {
throw new UnsupportedOperationException();
}
}

View File

@ -11,8 +11,8 @@ configurations {
}
dependencies {
//implementation project(":client-api")
developmentOnly("org.springframework.boot:spring-boot-devtools")
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'

View File

@ -2,6 +2,7 @@ package ru.dragonestia.picker.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import ru.dragonestia.picker.controller.response.ResponseObject;
import ru.dragonestia.picker.model.entity.EntityId;
import ru.dragonestia.picker.service.EntityService;
@ -22,15 +23,15 @@ public class EntityController {
}
@GetMapping("/target/rooms")
List<String> find(@RequestParam String id) {
List<ResponseObject.Room> find(@RequestParam String id) {
return entityService.getEntityRooms(EntityId.of(id)).stream()
.map(room -> room.getId().getValue())
.map(ResponseObject.Room::of)
.toList();
}
@GetMapping("/list/rooms")
Map<String, List<String>> roomsOf(@RequestParam List<String> id) {
var map = new HashMap<String, List<String>>();
Map<String, List<ResponseObject.Room>> roomsOf(@RequestParam List<String> id) {
var map = new HashMap<String, List<ResponseObject.Room>>();
for (var userId: id) {
map.put(userId, find(userId));
}