Refactor controllers and exception handling (#8)
* Refactored exceptions and responses * Extracted exceptions to external api library * Refactored exceptions and extracted models to external library * Fixed test after refactor * Extracted validator to api library * Implemented session error handler * Upgraded notifications * Implemented global exception handler for control panel
This commit is contained in:
parent
01518f4054
commit
7566f24d36
16
api/build.gradle
Normal file
16
api/build.gradle
Normal file
@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation platform('org.junit:junit-bom:5.9.1')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package ru.dragonestia.picker.api.exception;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class ApiException extends RuntimeException {
|
||||
|
||||
public abstract String getErrorId();
|
||||
|
||||
public abstract void appendDetailsToErrorResponse(Map<String, String> details);
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
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(InvalidNodeIdentifierException.ERROR_ID, InvalidNodeIdentifierException::new);
|
||||
factory.put(InvalidRoomIdentifierException.ERROR_ID, InvalidRoomIdentifierException::new);
|
||||
factory.put(InvalidUsernamesException.ERROR_ID, InvalidUsernamesException::new);
|
||||
factory.put(NodeAlreadyExistException.ERROR_ID, NodeAlreadyExistException::new);
|
||||
factory.put(NodeNotFoundException.ERROR_ID, NodeNotFoundException::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);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
public static ApiException of(ErrorResponse errorResponse) {
|
||||
return factory.get(errorResponse.errorId()).apply(errorResponse);
|
||||
}
|
||||
|
||||
private interface Constructor extends Function<ErrorResponse, ApiException> {}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package ru.dragonestia.picker.api.exception;
|
||||
|
||||
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class InvalidNodeIdentifierException extends ApiException {
|
||||
|
||||
public static final String ERROR_ID = "err.node.invalid_identifier";
|
||||
|
||||
private final String nodeId;
|
||||
|
||||
public InvalidNodeIdentifierException(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public InvalidNodeIdentifierException(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());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
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()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package ru.dragonestia.picker.api.exception;
|
||||
|
||||
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package ru.dragonestia.picker.api.exception;
|
||||
|
||||
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class NodeAlreadyExistException extends ApiException {
|
||||
|
||||
public static final String ERROR_ID = "err.node.already_exists";
|
||||
|
||||
private final String nodeId;
|
||||
|
||||
public NodeAlreadyExistException(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public NodeAlreadyExistException(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());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package ru.dragonestia.picker.api.exception;
|
||||
|
||||
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class NodeNotFoundException extends ApiException {
|
||||
|
||||
public static final String ERROR_ID = "err.node.not_found";
|
||||
|
||||
private final String nodeId;
|
||||
|
||||
public NodeNotFoundException(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public NodeNotFoundException(ErrorResponse errorResponse) {
|
||||
this(errorResponse.details().get("node"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Node '" + 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("node", getNodeId());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package ru.dragonestia.picker.api.exception;
|
||||
|
||||
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
39
api/src/main/java/ru/dragonestia/picker/api/model/Node.java
Normal file
39
api/src/main/java/ru/dragonestia/picker/api/model/Node.java
Normal file
@ -0,0 +1,39 @@
|
||||
package ru.dragonestia.picker.api.model;
|
||||
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
|
||||
public class Node {
|
||||
|
||||
private String id;
|
||||
private PickingMode mode;
|
||||
|
||||
private Node() {}
|
||||
|
||||
public Node(String id, PickingMode mode) {
|
||||
this.id = id;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public PickingMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
@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 Node other) {
|
||||
return id.equals(other.id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
73
api/src/main/java/ru/dragonestia/picker/api/model/Room.java
Normal file
73
api/src/main/java/ru/dragonestia/picker/api/model/Room.java
Normal file
@ -0,0 +1,73 @@
|
||||
package ru.dragonestia.picker.api.model;
|
||||
|
||||
import java.beans.Transient;
|
||||
|
||||
public class Room {
|
||||
|
||||
public final static int INFINITE_SLOTS = -1;
|
||||
|
||||
private String id;
|
||||
private String nodeId;
|
||||
private int slots;
|
||||
private String payload;
|
||||
private boolean locked = false;
|
||||
|
||||
private Room() {}
|
||||
|
||||
public Room(String id, String nodeId, int slots, String payload) {
|
||||
this.id = id;
|
||||
this.nodeId = nodeId;
|
||||
this.slots = slots;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public Room(String id, Node node, int limit, String payload) {
|
||||
this(id, node.getId(), limit, payload);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public int getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
public String getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public void setLocked(boolean value) {
|
||||
locked = value;
|
||||
}
|
||||
|
||||
@Transient
|
||||
public boolean isUnlimited() {
|
||||
return slots == INFINITE_SLOTS;
|
||||
}
|
||||
|
||||
@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 Room other) {
|
||||
return id.equals(other.id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public record Short(String id, int slots, boolean locked) {}
|
||||
}
|
||||
@ -1,8 +1,18 @@
|
||||
package ru.dragonestia.picker.cp.model;
|
||||
package ru.dragonestia.picker.api.model;
|
||||
|
||||
import lombok.NonNull;
|
||||
public class User {
|
||||
|
||||
public record User(@NonNull String id) {
|
||||
private String id;
|
||||
|
||||
private User() {}
|
||||
|
||||
public User(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
@ -1,14 +1,17 @@
|
||||
package ru.dragonestia.picker.cp.model.type;
|
||||
package ru.dragonestia.picker.api.model.type;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum PickingMode {
|
||||
SEQUENTIAL_FILLING("Sequential filling"),
|
||||
ROUND_ROBIN("Round Robin"),
|
||||
LEAST_PICKED("Least Picked");
|
||||
|
||||
private final String name;
|
||||
|
||||
PickingMode(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package ru.dragonestia.picker.api.repository;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface NodeRepository {
|
||||
|
||||
void register(Node node) throws InvalidNodeIdentifierException, NodeAlreadyExistException;
|
||||
|
||||
List<Node> all();
|
||||
|
||||
Optional<Node> find(String nodeId);
|
||||
|
||||
void remove(String nodeId);
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package ru.dragonestia.picker.api.repository;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.*;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RoomRepository {
|
||||
|
||||
void register(Room room) throws NodeNotFoundException, InvalidRoomIdentifierException, RoomAlreadyExistException;
|
||||
|
||||
void remove(Room room) throws NodeNotFoundException;
|
||||
|
||||
void remove(Node node, Room.Short room) throws NodeNotFoundException;
|
||||
|
||||
List<Room.Short> all(Node node) throws NodeNotFoundException;
|
||||
|
||||
Optional<Room> find(Node node, String roomId) throws NodeNotFoundException;
|
||||
|
||||
void lock(Room room, boolean value) throws NodeNotFoundException, RoomNotFoundException;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package ru.dragonestia.picker.api.repository;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
||||
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
import ru.dragonestia.picker.api.model.User;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface UserRepository {
|
||||
|
||||
void linkWithRoom(Room room, Collection<User> users, boolean force) throws NodeNotFoundException, RoomNotFoundException, RoomAreFullException;
|
||||
|
||||
void unlinkFromRoom(Room room, Collection<User> users) throws NodeNotFoundException, RoomNotFoundException;
|
||||
|
||||
List<User> all(Room room) throws NodeNotFoundException, RoomNotFoundException;
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.dragonestia.picker.api.repository.response;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record ErrorResponse(String errorId, String message, Map<String, String> details) {}
|
||||
@ -0,0 +1,3 @@
|
||||
package ru.dragonestia.picker.api.repository.response;
|
||||
|
||||
public record LinkUsersWithRoomResponse(int usedSlots, int totalSlots) {}
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.dragonestia.picker.api.repository.response;
|
||||
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
|
||||
public record NodeDetailsResponse(Node node) {}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.dragonestia.picker.api.repository.response;
|
||||
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record NodeListResponse(List<Node> nodes) {}
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.dragonestia.picker.api.repository.response;
|
||||
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
|
||||
public record RoomInfoResponse(Room room) {}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.dragonestia.picker.api.repository.response;
|
||||
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record RoomListResponse(String node, List<Room.Short> rooms) {}
|
||||
@ -1,6 +1,6 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
package ru.dragonestia.picker.api.repository.response;
|
||||
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.api.model.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
package ru.dragonestia.picker.api.utils;
|
||||
|
||||
public class ValidateIdentifier {
|
||||
|
||||
private ValidateIdentifier() {}
|
||||
|
||||
public static boolean forNode(String nodeId) {
|
||||
return nodeId.matches("^[a-z\\d-]+$");
|
||||
}
|
||||
|
||||
public static boolean forRoom(String roomId) {
|
||||
return roomId.matches("^[a-z\\d-]+$");
|
||||
}
|
||||
|
||||
public static boolean forUser(String username) {
|
||||
return username.matches("^[aA-zZ\\d-.\\s:@_;]+$");
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":api")
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
|
||||
|
||||
@ -7,11 +7,11 @@ import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.interceptor.DebugInterceptor;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.type.SlotLimit;
|
||||
import ru.dragonestia.picker.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
package ru.dragonestia.picker.controller;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import ru.dragonestia.picker.api.exception.*;
|
||||
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class ExceptionHandlerController {
|
||||
|
||||
@ExceptionHandler(NodeNotFoundException.class)
|
||||
ResponseEntity<?> nodeNotFound(NodeNotFoundException ex) {
|
||||
return create(404, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(RoomNotFoundException.class)
|
||||
ResponseEntity<?> roomNotFound(RoomNotFoundException ex) {
|
||||
return create(404, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidUsernamesException.class)
|
||||
ResponseEntity<?> invalidUsernames(InvalidUsernamesException ex) {
|
||||
return create(400, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidNodeIdentifierException.class)
|
||||
ResponseEntity<?> invalidNodeIdentifier(InvalidNodeIdentifierException ex) {
|
||||
return create(400, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidRoomIdentifierException.class)
|
||||
ResponseEntity<?> invalidRoomIdentifier(InvalidRoomIdentifierException ex) {
|
||||
return create(400, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(NodeAlreadyExistException.class)
|
||||
ResponseEntity<?> nodeAlreadyExists(NodeAlreadyExistException ex) {
|
||||
return create(400, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(RoomAlreadyExistException.class)
|
||||
ResponseEntity<?> roomAlreadyExists(RoomAlreadyExistException ex) {
|
||||
return create(400, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(RoomAreFullException.class)
|
||||
ResponseEntity<?> roomAreFull(RoomAreFullException ex) {
|
||||
return create(400, ex);
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoRoomsAvailableException.class)
|
||||
ResponseEntity<?> noRoomsAvailable(NoRoomsAvailableException ex) {
|
||||
return create(400, ex);
|
||||
}
|
||||
|
||||
private ResponseEntity<ErrorResponse> create(int code, ApiException ex) {
|
||||
var details = new HashMap<String, String>();
|
||||
ex.appendDetailsToErrorResponse(details);
|
||||
|
||||
return ResponseEntity.status(code).body(new ErrorResponse(ex.getErrorId(), ex.getMessage(), details));
|
||||
}
|
||||
}
|
||||
@ -1,22 +1,18 @@
|
||||
package ru.dragonestia.picker.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.dragonestia.picker.controller.response.NodeDetailsResponse;
|
||||
import ru.dragonestia.picker.controller.response.NodeListResponse;
|
||||
import ru.dragonestia.picker.controller.response.NodeRegisterResponse;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.api.repository.response.NodeDetailsResponse;
|
||||
import ru.dragonestia.picker.api.repository.response.NodeListResponse;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.service.NodeService;
|
||||
import ru.dragonestia.picker.service.RoomService;
|
||||
import ru.dragonestia.picker.util.NamingValidator;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Optional;
|
||||
import java.util.Arrays;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/nodes")
|
||||
@ -25,47 +21,35 @@ public class NodeController {
|
||||
|
||||
private final NodeService nodeService;
|
||||
private final RoomService roomService;
|
||||
private final NamingValidator namingValidator;
|
||||
|
||||
@GetMapping
|
||||
NodeListResponse allNodes() {
|
||||
return new NodeListResponse(nodeService.all());
|
||||
return new NodeListResponse(nodeService.all().stream().map(Node::toResponseObject).toList());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
NodeRegisterResponse registerNode(@RequestParam(name = "nodeId") String nodeId,
|
||||
ResponseEntity<?> registerNode(@RequestParam(name = "nodeId") String nodeId,
|
||||
@RequestParam(name = "method") PickingMode method) {
|
||||
|
||||
try {
|
||||
nodeService.create(new Node(nodeId, method));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return new NodeRegisterResponse(false, ex.getMessage());
|
||||
} catch (Error error) {
|
||||
new NodeRegisterResponse(false, error.getMessage());
|
||||
}
|
||||
|
||||
return new NodeRegisterResponse(true, "");
|
||||
nodeService.create(new Node(nodeId, method));
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@GetMapping("/{nodeId}")
|
||||
ResponseEntity<NodeDetailsResponse> nodeDetails(@PathVariable("nodeId") String nodeId) {
|
||||
if (!NamingValidator.validateNodeId(nodeId)) {
|
||||
return new ResponseEntity<>(HttpStatusCode.valueOf(404));
|
||||
}
|
||||
namingValidator.validateNodeId(nodeId);
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
return nodeOpt.map(node -> ResponseEntity.ok(new NodeDetailsResponse(node)))
|
||||
.orElseGet(() -> new ResponseEntity<>(HttpStatusCode.valueOf(404)));
|
||||
return nodeService.find(nodeId)
|
||||
.map(node -> ResponseEntity.ok(new NodeDetailsResponse(node.toResponseObject())))
|
||||
.orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{nodeId}")
|
||||
ResponseEntity<?> removeNode(@PathVariable("nodeId") String nodeId) {
|
||||
if (!NamingValidator.validateNodeId(nodeId)) {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
nodeOpt.ifPresent(nodeService::remove);
|
||||
namingValidator.validateNodeId(nodeId);
|
||||
|
||||
nodeService.find(nodeId).ifPresent(nodeService::remove);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@ -73,30 +57,11 @@ public class NodeController {
|
||||
ResponseEntity<?> pickRoom(@PathVariable("nodeId") String nodeId,
|
||||
@RequestParam(name = "userIds") String userIds) {
|
||||
|
||||
if (!NamingValidator.validateNodeId(nodeId)) {
|
||||
return new ResponseEntity<>(HttpStatusCode.valueOf(404));
|
||||
}
|
||||
namingValidator.validateNodeId(nodeId);
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
if (nodeOpt.isEmpty()) {
|
||||
return new ResponseEntity<>(HttpStatusCode.valueOf(404));
|
||||
}
|
||||
|
||||
var node = nodeOpt.get();
|
||||
|
||||
var list = new LinkedList<User>();
|
||||
for (var username: userIds.split(",")) { // TODO: create warnings about invalid usernames
|
||||
if (!NamingValidator.validateUserId(username)) continue;
|
||||
|
||||
list.add(new User(username));
|
||||
}
|
||||
|
||||
Room room;
|
||||
try {
|
||||
room = roomService.pickAvailable(node, list);
|
||||
} catch (RuntimeException ex) {
|
||||
return new ResponseEntity<>(HttpStatusCode.valueOf(409));
|
||||
}
|
||||
var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
var users = namingValidator.validateUserIds(Arrays.stream(userIds.split(",")).toList());
|
||||
var room = roomService.pickAvailable(node, users);
|
||||
|
||||
return ResponseEntity.ok(room); // TODO: make other json schema
|
||||
}
|
||||
|
||||
@ -4,17 +4,16 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.dragonestia.picker.controller.response.RoomInfoResponse;
|
||||
import ru.dragonestia.picker.controller.response.RoomListResponse;
|
||||
import ru.dragonestia.picker.controller.response.RoomRegisterResponse;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
||||
import ru.dragonestia.picker.api.repository.response.RoomInfoResponse;
|
||||
import ru.dragonestia.picker.api.repository.response.RoomListResponse;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.type.SlotLimit;
|
||||
import ru.dragonestia.picker.service.RoomService;
|
||||
import ru.dragonestia.picker.service.NodeService;
|
||||
import ru.dragonestia.picker.util.NamingValidator;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Log4j2
|
||||
@RestController
|
||||
@RequestMapping("/nodes/{nodeId}/rooms")
|
||||
@ -23,49 +22,40 @@ public class RoomController {
|
||||
|
||||
private final NodeService nodeService;
|
||||
private final RoomService roomService;
|
||||
private final NamingValidator namingValidator;
|
||||
|
||||
@GetMapping
|
||||
ResponseEntity<RoomListResponse> all(@PathVariable(name = "nodeId") String nodeId) {
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
return nodeOpt.map(node -> ResponseEntity.ok(new RoomListResponse(nodeId,
|
||||
roomService.all(node).stream()
|
||||
.map(room -> new RoomListResponse.RoomDTO(room.getId(), room.getSlots().getSlots(), room.isLocked()))
|
||||
.toList()
|
||||
))).orElseGet(() -> ResponseEntity.notFound().build());
|
||||
|
||||
return nodeService.find(nodeId)
|
||||
.map(node -> ResponseEntity.ok(new RoomListResponse(nodeId,
|
||||
roomService.all(node).stream()
|
||||
.map(room -> new ru.dragonestia.picker.api.model.Room.Short(room.getId(), room.getSlots().getSlots(), room.isLocked()))
|
||||
.toList()
|
||||
))).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
ResponseEntity<RoomRegisterResponse> register(@PathVariable(name = "nodeId") String nodeId,
|
||||
@RequestParam(name = "roomId") String roomId,
|
||||
@RequestParam(name = "slots") int slots,
|
||||
@RequestParam(name = "payload") String payload,
|
||||
@RequestParam(name = "locked", defaultValue = "false") boolean locked) {
|
||||
ResponseEntity<?> register(@PathVariable(name = "nodeId") String nodeId,
|
||||
@RequestParam(name = "roomId") String roomId,
|
||||
@RequestParam(name = "slots") int slots,
|
||||
@RequestParam(name = "payload") String payload,
|
||||
@RequestParam(name = "locked", defaultValue = "false") boolean locked) {
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
|
||||
if (nodeOpt.isEmpty()) {
|
||||
return ResponseEntity.status(404)
|
||||
.body(new RoomRegisterResponse(false, "Node does not exist"));
|
||||
}
|
||||
|
||||
var room = Room.create(roomId, nodeOpt.get(), SlotLimit.of(slots), payload);
|
||||
var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
var room = Room.create(roomId, node, SlotLimit.of(slots), payload);
|
||||
room.setLocked(locked);
|
||||
try {
|
||||
roomService.create(room);
|
||||
return ResponseEntity.ok(new RoomRegisterResponse(true, ""));
|
||||
} catch (Error error) {
|
||||
return ResponseEntity.status(400).body(new RoomRegisterResponse(false, error.getMessage()));
|
||||
} catch (Exception ex) {
|
||||
return ResponseEntity.status(500).body(new RoomRegisterResponse(false, ex.getMessage()));
|
||||
}
|
||||
roomService.create(room);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{roomId}")
|
||||
ResponseEntity<?> remove(@PathVariable("nodeId") String nodeId,
|
||||
@PathVariable("roomId") String roomId) {
|
||||
if (!NamingValidator.validateNodeId(nodeId) || !NamingValidator.validateRoomId(roomId)) {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
namingValidator.validateNodeId(nodeId);
|
||||
namingValidator.validateRoomId(nodeId, roomId);
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
nodeOpt.flatMap(node -> roomService.find(node, roomId))
|
||||
@ -77,40 +67,26 @@ public class RoomController {
|
||||
@GetMapping("/{roomId}")
|
||||
ResponseEntity<RoomInfoResponse> info(@PathVariable("nodeId") String nodeId,
|
||||
@PathVariable("roomId") String roomId) {
|
||||
if (!NamingValidator.validateNodeId(nodeId) || !NamingValidator.validateRoomId(roomId)) {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
if (nodeOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
namingValidator.validateNodeId(nodeId);
|
||||
namingValidator.validateRoomId(nodeId, roomId);
|
||||
|
||||
var roomOpt = roomService.find(Objects.requireNonNull(nodeOpt.get()), roomId);
|
||||
return roomOpt.map(room -> ResponseEntity.ok(new RoomInfoResponse(room)))
|
||||
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||
var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
return roomService.find(node, roomId)
|
||||
.map(room -> ResponseEntity.ok(new RoomInfoResponse(room.toResponseObject())))
|
||||
.orElseThrow(() -> new RoomNotFoundException(nodeId, roomId));
|
||||
}
|
||||
|
||||
@PostMapping("/{roomId}/lock")
|
||||
@PutMapping("/{roomId}/lock")
|
||||
ResponseEntity<Boolean> lockBucket(@PathVariable("nodeId") String nodeId,
|
||||
@PathVariable("roomId") String roomId,
|
||||
@RequestParam(name = "newState") boolean value) {
|
||||
@PathVariable("roomId") String roomId,
|
||||
@RequestParam(name = "newState") boolean value) {
|
||||
|
||||
if (!NamingValidator.validateNodeId(nodeId) || !NamingValidator.validateRoomId(roomId)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
namingValidator.validateNodeId(nodeId);
|
||||
namingValidator.validateRoomId(nodeId, roomId);
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
if (nodeOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
var roomOpt = roomService.find(Objects.requireNonNull(nodeOpt.get()), roomId);
|
||||
if (roomOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
var room = roomOpt.get();
|
||||
var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
var room = roomService.find(node, roomId).orElseThrow(() -> new RoomNotFoundException(nodeId, roomId));
|
||||
room.setLocked(value);
|
||||
return ResponseEntity.ok(true);
|
||||
}
|
||||
|
||||
@ -3,8 +3,10 @@ package ru.dragonestia.picker.controller;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.dragonestia.picker.controller.response.RoomUserListResponse;
|
||||
import ru.dragonestia.picker.controller.response.LinkUsersWithRoomResponse;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
||||
import ru.dragonestia.picker.api.repository.response.LinkUsersWithRoomResponse;
|
||||
import ru.dragonestia.picker.api.repository.response.RoomUserListResponse;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
@ -13,8 +15,7 @@ import ru.dragonestia.picker.service.NodeService;
|
||||
import ru.dragonestia.picker.service.UserService;
|
||||
import ru.dragonestia.picker.util.NamingValidator;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
import java.util.Arrays;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@ -24,21 +25,15 @@ public class UserRoomController {
|
||||
private final NodeService nodeService;
|
||||
private final RoomService roomService;
|
||||
private final UserService userService;
|
||||
private final NamingValidator namingValidator;
|
||||
|
||||
@GetMapping
|
||||
ResponseEntity<RoomUserListResponse> usersInsideRoom(@PathVariable(name = "nodeId") String nodeId,
|
||||
@PathVariable(name = "roomId") String bucketId) {
|
||||
|
||||
Room room;
|
||||
try {
|
||||
var temp = getNodeAndRoom(nodeId, bucketId);
|
||||
room = temp.room();
|
||||
} catch (Error error) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
@PathVariable(name = "roomId") String roomId) {
|
||||
|
||||
var room = getNodeAndRoom(nodeId, roomId).room();
|
||||
var users = userService.getRoomUsers(room);
|
||||
return ResponseEntity.ok(new RoomUserListResponse(room.getSlots().getSlots(), users.size(), users));
|
||||
return ResponseEntity.ok(new RoomUserListResponse(room.getSlots().getSlots(), users.size(), users.stream().map(User::toResponseObject).toList()));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ -47,28 +42,10 @@ public class UserRoomController {
|
||||
@RequestParam(name = "userIds") String userIds,
|
||||
@RequestParam(name = "force") boolean force) {
|
||||
|
||||
Room room;
|
||||
try {
|
||||
var temp = getNodeAndRoom(nodeId, roomId);
|
||||
room = temp.room();
|
||||
} catch (Error error) {
|
||||
return ResponseEntity.status(404).body(new LinkUsersWithRoomResponse(false, error.getMessage(), -1, -1));
|
||||
}
|
||||
|
||||
var list = new LinkedList<User>();
|
||||
for (var username: userIds.split(",")) { // TODO: create warnings about invalid usernames
|
||||
if (!NamingValidator.validateUserId(username)) continue;
|
||||
|
||||
list.add(new User(username));
|
||||
}
|
||||
|
||||
try {
|
||||
int usedSlots = userService.linkUsersWithRoom(room, list, force);
|
||||
|
||||
return ResponseEntity.ok(new LinkUsersWithRoomResponse(true, "Success", usedSlots, room.getSlots().getSlots()));
|
||||
} catch (Error error) {
|
||||
return ResponseEntity.status(400).body(new LinkUsersWithRoomResponse(false, error.getMessage(), -1, -1));
|
||||
}
|
||||
var room = getNodeAndRoom(nodeId, roomId).room();
|
||||
var users = namingValidator.validateUserIds(Arrays.stream(userIds.split(",")).toList());
|
||||
var usedSlots = userService.linkUsersWithRoom(room, users, force);
|
||||
return ResponseEntity.ok(new LinkUsersWithRoomResponse(usedSlots, room.getSlots().getSlots()));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@ -76,43 +53,21 @@ public class UserRoomController {
|
||||
@PathVariable(name = "roomId") String roomId,
|
||||
@RequestParam(name = "userIds") String userIds) {
|
||||
|
||||
Room room;
|
||||
try {
|
||||
var temp = getNodeAndRoom(nodeId, roomId);
|
||||
room = temp.room();
|
||||
|
||||
var list = new LinkedList<User>();
|
||||
for (var username: userIds.split(",")) { // TODO: create warnings about invalid usernames
|
||||
if (!NamingValidator.validateUserId(username)) continue;
|
||||
|
||||
list.add(new User(username));
|
||||
}
|
||||
|
||||
userService.unlinkUsersFromRoom(room, list);
|
||||
} catch (Error error) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
var room = getNodeAndRoom(nodeId, roomId).room();
|
||||
var users = namingValidator.validateUserIds(Arrays.stream(userIds.split(",")).toList());
|
||||
userService.unlinkUsersFromRoom(room, users);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
private record NodeAndRoom(Node node, Room room) {}
|
||||
|
||||
private NodeAndRoom getNodeAndRoom(String nodeId, String roomId) {
|
||||
if (!NamingValidator.validateNodeId(nodeId) || !NamingValidator.validateRoomId(roomId)) {
|
||||
throw new Error();
|
||||
}
|
||||
namingValidator.validateNodeId(nodeId);
|
||||
namingValidator.validateRoomId(nodeId, roomId);
|
||||
|
||||
var nodeOpt = nodeService.find(nodeId);
|
||||
if (nodeOpt.isEmpty()) {
|
||||
throw new Error();
|
||||
}
|
||||
var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
var room = roomService.find(node, roomId).orElseThrow(() -> new RoomNotFoundException(nodeId, roomId));
|
||||
|
||||
var roomOpt = roomService.find(Objects.requireNonNull(nodeOpt.get()), roomId);
|
||||
if (roomOpt.isEmpty()) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return new NodeAndRoom(nodeOpt.get(), roomOpt.get());
|
||||
return new NodeAndRoom(node, room);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
|
||||
public record LinkUsersWithRoomResponse(boolean success, String message, int usedSlots, int totalSlots) {}
|
||||
@ -1,5 +0,0 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
|
||||
public record NodeDetailsResponse(Node node) {}
|
||||
@ -1,7 +0,0 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record NodeListResponse(List<Node> nodes) {}
|
||||
@ -1,3 +0,0 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
|
||||
public record NodeRegisterResponse(boolean success, String message) {}
|
||||
@ -1,5 +0,0 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
|
||||
public record RoomInfoResponse(Room room) {}
|
||||
@ -1,8 +0,0 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record RoomListResponse(String node, List<RoomDTO> rooms) {
|
||||
|
||||
public record RoomDTO(String id, int slots, boolean locked) {}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
package ru.dragonestia.picker.controller.response;
|
||||
|
||||
public record RoomRegisterResponse(boolean success, String message) {}
|
||||
@ -1,7 +1,7 @@
|
||||
package ru.dragonestia.picker.model;
|
||||
|
||||
import lombok.NonNull;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
|
||||
public record Node(@NonNull String id, @NonNull PickingMode mode) {
|
||||
|
||||
@ -19,4 +19,8 @@ public record Node(@NonNull String id, @NonNull PickingMode mode) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ru.dragonestia.picker.api.model.Node toResponseObject() {
|
||||
return new ru.dragonestia.picker.api.model.Node(id, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,4 +43,10 @@ public class Room {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ru.dragonestia.picker.api.model.Room toResponseObject() {
|
||||
var result = new ru.dragonestia.picker.api.model.Room(id, nodeId, slots.getSlots(), payload);
|
||||
result.setLocked(locked);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,4 +18,8 @@ public record User(@NonNull String id) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ru.dragonestia.picker.api.model.User toResponseObject() {
|
||||
return new ru.dragonestia.picker.api.model.User(id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
package ru.dragonestia.picker.model.type;
|
||||
|
||||
public enum PickingMode {
|
||||
SEQUENTIAL_FILLING,
|
||||
ROUND_ROBIN,
|
||||
LEAST_PICKED,
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package ru.dragonestia.picker.repository;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
@ -7,7 +8,7 @@ import java.util.Optional;
|
||||
|
||||
public interface NodeRepository {
|
||||
|
||||
void create(Node node);
|
||||
void create(Node node) throws NodeAlreadyExistException;
|
||||
|
||||
void delete(Node node);
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package ru.dragonestia.picker.repository;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.RoomAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
@ -10,7 +11,7 @@ import java.util.Optional;
|
||||
|
||||
public interface RoomRepository {
|
||||
|
||||
void create(Room room);
|
||||
void create(Room room) throws RoomAlreadyExistException;
|
||||
|
||||
void remove(Room room);
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package ru.dragonestia.picker.repository;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
|
||||
@ -9,7 +10,7 @@ import java.util.Map;
|
||||
|
||||
public interface UserRepository {
|
||||
|
||||
Map<User, Boolean> linkWithRoom(Room room, Collection<User> users, boolean force);
|
||||
Map<User, Boolean> linkWithRoom(Room room, Collection<User> users, boolean force) throws RoomAreFullException;
|
||||
|
||||
int unlinkWithRoom(Room room, Collection<User> users);
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package ru.dragonestia.picker.repository.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
@ -22,10 +23,10 @@ public class NodeRepositoryImpl implements NodeRepository {
|
||||
private final Map<String, Node> nodeMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void create(Node node) {
|
||||
public void create(Node node) throws NodeAlreadyExistException {
|
||||
synchronized (nodeMap) {
|
||||
if (nodeMap.containsKey(node.id())) {
|
||||
throw new IllegalArgumentException("Node with id '" + node.id() + "' already exists");
|
||||
throw new NodeAlreadyExistException(node.id());
|
||||
}
|
||||
|
||||
nodeMap.put(node.id(), node);
|
||||
|
||||
@ -2,9 +2,9 @@ package ru.dragonestia.picker.repository.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.repository.UserRepository;
|
||||
import ru.dragonestia.picker.repository.impl.picker.*;
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package ru.dragonestia.picker.repository.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.dragonestia.picker.api.exception.RoomAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
@ -21,7 +22,7 @@ public class RoomRepositoryImpl implements RoomRepository {
|
||||
private final Map<Node, Rooms> node2roomsMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void create(Room room) {
|
||||
public void create(Room room) throws RoomAlreadyExistException {
|
||||
var nodeId = room.getNodeId();
|
||||
|
||||
synchronized (node2roomsMap) {
|
||||
@ -35,7 +36,7 @@ public class RoomRepositoryImpl implements RoomRepository {
|
||||
|
||||
var rooms = node2roomsMap.get(node.get());
|
||||
if (rooms.containsKey(room.getId())) {
|
||||
throw new IllegalArgumentException("Room already exists");
|
||||
throw new RoomAlreadyExistException(room.getNodeId(), room.getId());
|
||||
}
|
||||
rooms.put(room.getId(), new RoomContainer(room, new AtomicInteger(0)));
|
||||
pickerRepository.find(room.getNodeId()).add(room);
|
||||
|
||||
@ -2,10 +2,9 @@ package ru.dragonestia.picker.repository.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.repository.UserRepository;
|
||||
import ru.dragonestia.picker.repository.impl.cache.NodeId2PickerModeCache;
|
||||
import ru.dragonestia.picker.repository.impl.picker.LeastPickedPicker;
|
||||
@ -23,7 +22,7 @@ public class UserRepositoryImpl implements UserRepository {
|
||||
private final Map<NodeRoomPath, Set<User>> roomUsers = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Map<User, Boolean> linkWithRoom(Room room, Collection<User> users, boolean force) {
|
||||
public Map<User, Boolean> linkWithRoom(Room room, Collection<User> users, boolean force) throws RoomAreFullException {
|
||||
var result = new HashMap<User, Boolean>();
|
||||
|
||||
synchronized (usersMap) {
|
||||
@ -39,7 +38,7 @@ public class UserRepositoryImpl implements UserRepository {
|
||||
}
|
||||
|
||||
if (room.getSlots().getSlots() < usersSet.size() + users.size()) {
|
||||
throw new Error("Room are full");
|
||||
throw new RoomAreFullException(room.getNodeId(), room.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package ru.dragonestia.picker.repository.impl.picker;
|
||||
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.repository.UserRepository;
|
||||
import ru.dragonestia.picker.repository.impl.collection.DynamicSortedMap;
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package ru.dragonestia.picker.repository.impl.picker;
|
||||
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
|
||||
public interface RoomPicker extends Picker<Room, User> {
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package ru.dragonestia.picker.repository.impl.picker;
|
||||
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.repository.UserRepository;
|
||||
import ru.dragonestia.picker.repository.impl.collection.QueuedLinkedList;
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package ru.dragonestia.picker.repository.impl.picker;
|
||||
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.repository.UserRepository;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package ru.dragonestia.picker.service;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
@ -7,7 +9,7 @@ import java.util.Optional;
|
||||
|
||||
public interface NodeService {
|
||||
|
||||
void create(Node node);
|
||||
void create(Node node) throws InvalidNodeIdentifierException, NodeAlreadyExistException;
|
||||
|
||||
void remove(Node node);
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package ru.dragonestia.picker.service;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.InvalidRoomIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.RoomAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
@ -9,7 +11,7 @@ import java.util.Optional;
|
||||
|
||||
public interface RoomService {
|
||||
|
||||
void create(Room room);
|
||||
void create(Room room) throws InvalidRoomIdentifierException, RoomAlreadyExistException;
|
||||
|
||||
void remove(Room room);
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package ru.dragonestia.picker.service;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
|
||||
@ -10,7 +11,7 @@ public interface UserService {
|
||||
|
||||
List<Room> getUserRooms(User user);
|
||||
|
||||
int linkUsersWithRoom(Room room, Collection<User> users, boolean force);
|
||||
int linkUsersWithRoom(Room room, Collection<User> users, boolean force) throws RoomAreFullException;
|
||||
|
||||
void unlinkUsersFromRoom(Room room, Collection<User> users);
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@ package ru.dragonestia.picker.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.service.NodeService;
|
||||
@ -15,13 +17,11 @@ import java.util.Optional;
|
||||
public class NodeServiceImpl implements NodeService {
|
||||
|
||||
private final NodeRepository nodeRepository;
|
||||
private final NamingValidator namingValidator;
|
||||
|
||||
@Override
|
||||
public void create(Node node) {
|
||||
if (!NamingValidator.validateNodeId(node.id())) {
|
||||
throw new Error("Invalid node id format");
|
||||
}
|
||||
|
||||
public void create(Node node) throws InvalidNodeIdentifierException, NodeAlreadyExistException {
|
||||
namingValidator.validateNodeId(node.id());
|
||||
nodeRepository.create(node);
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@ package ru.dragonestia.picker.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.dragonestia.picker.api.exception.InvalidRoomIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.RoomAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
@ -17,13 +19,11 @@ import java.util.Optional;
|
||||
public class RoomServiceImpl implements RoomService {
|
||||
|
||||
private final RoomRepository roomRepository;
|
||||
private final NamingValidator namingValidator;
|
||||
|
||||
@Override
|
||||
public void create(Room room) {
|
||||
if (!NamingValidator.validateRoomId(room.getId())) {
|
||||
throw new Error("Invalid room id format");
|
||||
}
|
||||
|
||||
public void create(Room room) throws InvalidRoomIdentifierException, RoomAlreadyExistException {
|
||||
namingValidator.validateRoomId(room.getNodeId(), room.getId());
|
||||
roomRepository.create(room);
|
||||
}
|
||||
|
||||
|
||||
@ -1,19 +1,51 @@
|
||||
package ru.dragonestia.picker.util;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.InvalidRoomIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.InvalidUsernamesException;
|
||||
import ru.dragonestia.picker.api.utils.ValidateIdentifier;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
|
||||
@UtilityClass
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class NamingValidator {
|
||||
|
||||
public boolean validateNodeId(String input) {
|
||||
return input.matches("^[a-z\\d-]+$");
|
||||
public void validateNodeId(String input) throws InvalidNodeIdentifierException {
|
||||
if (ValidateIdentifier.forNode(input)) return;
|
||||
|
||||
throw new InvalidNodeIdentifierException(input);
|
||||
}
|
||||
|
||||
public boolean validateRoomId(String input) {
|
||||
return input.matches("^[a-z\\d-]+$");
|
||||
public void validateRoomId(String nodeId, String input) throws InvalidRoomIdentifierException {
|
||||
if (ValidateIdentifier.forRoom(input)) return;
|
||||
|
||||
throw new InvalidRoomIdentifierException(nodeId, input);
|
||||
}
|
||||
|
||||
public boolean validateUserId(String input) {
|
||||
return input.matches("^[aA-zZ\\d-.\\s:/@%?!~$)(+=_|;*]+$");
|
||||
private boolean validateUserId(String input) {
|
||||
return ValidateIdentifier.forUser(input);
|
||||
}
|
||||
|
||||
public List<User> validateUserIds(List<String> input) throws InvalidUsernamesException {
|
||||
var users = new LinkedList<User>();
|
||||
var invalid = new LinkedList<String>();
|
||||
|
||||
for (var username: input) {
|
||||
if (validateUserId(username)) {
|
||||
users.add(new User(username));
|
||||
continue;
|
||||
}
|
||||
|
||||
invalid.add(username);
|
||||
}
|
||||
|
||||
if (!invalid.isEmpty()) {
|
||||
throw new InvalidUsernamesException(input, invalid);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,10 @@ package ru.dragonestia.picker.config;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.model.type.SlotLimit;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.repository.RoomRepository;
|
||||
|
||||
@ -23,8 +23,11 @@ ext {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":api")
|
||||
|
||||
implementation 'com.vaadin:vaadin-spring-boot-starter'
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
||||
@ -9,18 +9,15 @@ import com.vaadin.flow.component.html.Div;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import lombok.Getter;
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.User;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
import ru.dragonestia.picker.api.model.User;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class AddUsers extends Details {
|
||||
|
||||
@ -79,8 +76,7 @@ public class AddUsers extends Details {
|
||||
try {
|
||||
onCommit.accept(readAllUsers(), ignoreSlots.getValue());
|
||||
} catch (Error error) {
|
||||
Notification.show(error.getMessage(), 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
Notifications.error(error.getMessage());
|
||||
}
|
||||
|
||||
clear();
|
||||
|
||||
@ -10,7 +10,7 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
|
||||
public class NavPath extends HorizontalLayout{
|
||||
|
||||
public NavPath(Point root, Point... points) {
|
||||
private NavPath(Point root, Point... points) {
|
||||
setWidth("100%");
|
||||
setAlignItems(Alignment.CENTER);
|
||||
getStyle().set("background-color", "#F3F3F3")
|
||||
@ -58,5 +58,20 @@ public class NavPath extends HorizontalLayout{
|
||||
return button;
|
||||
}
|
||||
|
||||
public record Point(String name, String uri) {}
|
||||
public static NavPath rootNodes() {
|
||||
return new NavPath(new NavPath.Point("Nodes", "/nodes"));
|
||||
}
|
||||
|
||||
public static NavPath toNode(String nodeId) {
|
||||
return new NavPath(new NavPath.Point("Nodes", "/nodes"),
|
||||
new NavPath.Point(nodeId, "/nodes/" + nodeId));
|
||||
}
|
||||
|
||||
public static NavPath toRoom(String nodeId, String roomId) {
|
||||
return new NavPath(new NavPath.Point("Nodes", "/nodes"),
|
||||
new NavPath.Point(nodeId, "/nodes/" + nodeId),
|
||||
new NavPath.Point(roomId, "/nodes/" + nodeId + "/rooms/" + roomId));
|
||||
}
|
||||
|
||||
private record Point(String name, String uri) {}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package ru.dragonestia.picker.cp.component;
|
||||
|
||||
import com.vaadin.flow.component.Html;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.dialog.Dialog;
|
||||
@ -8,13 +9,11 @@ import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import lombok.Setter;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@ -51,14 +50,14 @@ public class NodeList extends VerticalLayout {
|
||||
var temp = input.trim();
|
||||
|
||||
nodesGrid.setItems(cachedNodes.stream()
|
||||
.filter(node -> node.id().startsWith(temp))
|
||||
.filter(node -> node.getId().startsWith(temp))
|
||||
.toList());
|
||||
}
|
||||
|
||||
private Grid<Node> createGrid() {
|
||||
var grid = new Grid<>(Node.class, false);
|
||||
grid.addColumn(Node::id).setHeader("Identifier");
|
||||
grid.addColumn(node -> node.mode().getName()).setHeader("Mode");
|
||||
grid.addColumn(Node::getId).setHeader("Identifier");
|
||||
grid.addColumn(node -> node.getMode().getName()).setHeader("Mode");
|
||||
grid.addComponentColumn(this::createManageButtons).setHeader("Manage");
|
||||
return grid;
|
||||
}
|
||||
@ -84,29 +83,28 @@ public class NodeList extends VerticalLayout {
|
||||
}
|
||||
|
||||
private void clickDetailsButton(Node node) {
|
||||
getUI().ifPresent(ui -> ui.navigate("/nodes/" + node.id()));
|
||||
getUI().ifPresent(ui -> ui.navigate("/nodes/" + node.getId()));
|
||||
}
|
||||
|
||||
private void clickRemoveButton(Node node) {
|
||||
var dialog = new Dialog("Confirm node deletion");
|
||||
dialog.add(new Paragraph("Confirm that you want to delete node. Enter '" + node.id() + "' to field below and confirm."));
|
||||
dialog.add(new Html("<p>Confirm that you want to delete node. Enter <b><u>" + node.getId() + "</u></b> to field below and confirm.</p>"));
|
||||
|
||||
var inputField = new TextField();
|
||||
inputField.setWidth("100%");
|
||||
dialog.add(inputField);
|
||||
|
||||
{ // confirm
|
||||
var button = new Button("Confirm");
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
button.addClickListener(event -> {
|
||||
if (!node.id().equals(inputField.getValue())) {
|
||||
Notification.show("Invalid input", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
if (!node.getId().equals(inputField.getValue())) {
|
||||
Notifications.error("Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
removeNode(node);
|
||||
Notification.show("Node '" + node.id() + "' was successfully removed!", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
Notifications.success("Node <b>" + node.getId() + "</b> was successfully removed!");
|
||||
dialog.close();
|
||||
});
|
||||
|
||||
@ -129,7 +127,7 @@ public class NodeList extends VerticalLayout {
|
||||
|
||||
private void removeNode(Node node) {
|
||||
if (removeMethod != null) {
|
||||
removeMethod.accept(node.id());
|
||||
removeMethod.accept(node.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
package ru.dragonestia.picker.cp.component;
|
||||
|
||||
import com.vaadin.flow.component.Html;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class Notifications {
|
||||
|
||||
public Notification success(String text) {
|
||||
var notification = create(VaadinIcon.CHECK_CIRCLE, text);
|
||||
notification.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
notification.open();
|
||||
return notification;
|
||||
}
|
||||
|
||||
public Notification warn(String text) {
|
||||
var notification = create(VaadinIcon.WARNING, text);
|
||||
notification.addThemeVariants(NotificationVariant.LUMO_WARNING);
|
||||
notification.open();
|
||||
return notification;
|
||||
}
|
||||
|
||||
public Notification error(String text) {
|
||||
var notification = create(VaadinIcon.WARNING, text);
|
||||
notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
notification.open();
|
||||
return notification;
|
||||
}
|
||||
|
||||
private Notification create(VaadinIcon icon, String text) {
|
||||
var layout = new HorizontalLayout();
|
||||
layout.add(new Icon(icon));
|
||||
layout.add(new Html("<span>" + text + "</span>"));
|
||||
|
||||
var notification = new Notification();
|
||||
notification.setDuration(5000);
|
||||
notification.setPosition(Notification.Position.TOP_END);
|
||||
|
||||
var closeButton = new Button(VaadinIcon.CLOSE_SMALL.create(), event -> notification.close());
|
||||
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
|
||||
layout.add(closeButton);
|
||||
|
||||
notification.add(layout);
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,6 @@ import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.details.Details;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
|
||||
import com.vaadin.flow.component.radiobutton.RadioGroupVariant;
|
||||
@ -16,8 +14,8 @@ import com.vaadin.flow.component.textfield.Autocomplete;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.cp.model.type.PickingMode;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
import ru.dragonestia.picker.api.model.type.PickingMode;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -91,8 +89,7 @@ public class RegisterNode extends Details {
|
||||
error = "Invalid node id format";
|
||||
}
|
||||
|
||||
Notification.show(error, 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
Notifications.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -100,13 +97,11 @@ public class RegisterNode extends Details {
|
||||
var response = onSubmit.apply(node);
|
||||
clear();
|
||||
if (response.error()) {
|
||||
Notification.show(response.reason(), 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
Notifications.error(response.reason());
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.show("Node was successfully registered", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
Notifications.success("Node was successfully registered");
|
||||
}
|
||||
|
||||
public record Response(boolean error, @Nullable String reason) {}
|
||||
|
||||
@ -6,16 +6,13 @@ import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.checkbox.Checkbox;
|
||||
import com.vaadin.flow.component.details.Details;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.Autocomplete;
|
||||
import com.vaadin.flow.component.textfield.TextArea;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import org.springframework.lang.Nullable;
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.cp.model.type.SlotLimit;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -45,7 +42,7 @@ public class RegisterRoom extends Details {
|
||||
private TextField createNodeIdentifierField() {
|
||||
var field = new TextField("Node identifier");
|
||||
field.setMinWidth(20, Unit.REM);
|
||||
field.setValue(node.id());
|
||||
field.setValue(node.getId());
|
||||
field.setReadOnly(true);
|
||||
return field;
|
||||
}
|
||||
@ -106,23 +103,20 @@ public class RegisterRoom extends Details {
|
||||
error = "Invalid room id format";
|
||||
}
|
||||
|
||||
Notification.show(error, 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
Notifications.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
var room = Room.create(nodeIdentifier, node, SlotLimit.unlimited(), payloadField.getValue());
|
||||
var room = new Room(nodeIdentifier, node, Room.INFINITE_SLOTS, payloadField.getValue());
|
||||
room.setLocked(lockedField.getValue());
|
||||
var response = onSubmit.apply(room);
|
||||
clear();
|
||||
if (response.error()) {
|
||||
Notification.show(response.reason(), 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
Notifications.error(response.reason());
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.show("Room was successfully registered", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
Notifications.success("Room was successfully registered");
|
||||
}
|
||||
|
||||
public record Response(boolean error, @Nullable String reason) {}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package ru.dragonestia.picker.cp.component;
|
||||
|
||||
import com.vaadin.flow.component.Html;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.dialog.Dialog;
|
||||
@ -10,13 +11,11 @@ import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import lombok.Setter;
|
||||
import ru.dragonestia.picker.cp.model.dto.RoomDTO;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@ -24,12 +23,12 @@ import java.util.function.Consumer;
|
||||
public class RoomList extends VerticalLayout {
|
||||
|
||||
private final String nodeIdentifier;
|
||||
private final Grid<RoomDTO> roomsGrid;
|
||||
private final Grid<Room.Short> roomsGrid;
|
||||
private final TextField searchField;
|
||||
private List<RoomDTO> cachedRooms;
|
||||
@Setter private Consumer<RoomDTO> removeMethod;
|
||||
private List<Room.Short> cachedRooms;
|
||||
@Setter private Consumer<Room.Short> removeMethod;
|
||||
|
||||
public RoomList(String nodeIdentifier, List<RoomDTO> buckets) {
|
||||
public RoomList(String nodeIdentifier, List<Room.Short> buckets) {
|
||||
this.nodeIdentifier = nodeIdentifier;
|
||||
cachedRooms = buckets;
|
||||
|
||||
@ -57,9 +56,9 @@ public class RoomList extends VerticalLayout {
|
||||
.toList());
|
||||
}
|
||||
|
||||
private Grid<RoomDTO> createGrid() {
|
||||
var grid = new Grid<>(RoomDTO.class, false);
|
||||
grid.addColumn(RoomDTO::id).setHeader("Identifier");
|
||||
private Grid<Room.Short> createGrid() {
|
||||
var grid = new Grid<>(Room.Short.class, false);
|
||||
grid.addColumn(Room.Short::id).setHeader("Identifier");
|
||||
grid.addComponentColumn(room -> {
|
||||
var result = new Span();
|
||||
if (room.slots() == -1) {
|
||||
@ -84,7 +83,7 @@ public class RoomList extends VerticalLayout {
|
||||
return grid;
|
||||
}
|
||||
|
||||
private HorizontalLayout createManageButtons(RoomDTO room) {
|
||||
private HorizontalLayout createManageButtons(Room.Short room) {
|
||||
var layout = new HorizontalLayout();
|
||||
|
||||
{
|
||||
@ -104,18 +103,19 @@ public class RoomList extends VerticalLayout {
|
||||
return layout;
|
||||
}
|
||||
|
||||
private void clickDetailsButton(RoomDTO bucket) {
|
||||
private void clickDetailsButton(Room.Short bucket) {
|
||||
getUI().ifPresent(ui -> {
|
||||
ui.navigate("/nodes/" + nodeIdentifier +
|
||||
"/rooms/" + bucket.id());
|
||||
});
|
||||
}
|
||||
|
||||
private void clickRemoveButton(RoomDTO bucket) {
|
||||
private void clickRemoveButton(Room.Short bucket) {
|
||||
var dialog = new Dialog("Confirm bucket deletion");
|
||||
dialog.add(new Paragraph("Confirm that you want to delete bucket. Enter '" + bucket.id() + "' to field below and confirm."));
|
||||
dialog.add(new Html("<p>Confirm that you want to delete bucket. Enter <b><u>" + bucket.id() + "</u></b> to field below and confirm.</p>"));
|
||||
|
||||
var inputField = new TextField();
|
||||
inputField.setWidth("100%");
|
||||
dialog.add(inputField);
|
||||
|
||||
{ // confirm
|
||||
@ -123,14 +123,12 @@ public class RoomList extends VerticalLayout {
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
button.addClickListener(event -> {
|
||||
if (!bucket.id().equals(inputField.getValue())) {
|
||||
Notification.show("Invalid input", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
Notifications.error("Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
removeBucket(bucket);
|
||||
Notification.show("Bucket '" + bucket.id() + "' was successfully removed!", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
Notifications.success("Bucket <b>" + bucket.id() + "</b> was successfully removed!");
|
||||
dialog.close();
|
||||
});
|
||||
|
||||
@ -146,12 +144,12 @@ public class RoomList extends VerticalLayout {
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
public void update(List<RoomDTO> buckets) {
|
||||
public void update(List<Room.Short> buckets) {
|
||||
cachedRooms = buckets;
|
||||
applySearch(searchField.getValue());
|
||||
}
|
||||
|
||||
private void removeBucket(RoomDTO bucket) {
|
||||
private void removeBucket(Room.Short bucket) {
|
||||
if (removeMethod != null) {
|
||||
removeMethod.accept(bucket);
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@ import com.vaadin.flow.component.grid.ColumnTextAlign;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.User;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
import ru.dragonestia.picker.api.model.User;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -28,7 +28,7 @@ public class UserList extends VerticalLayout {
|
||||
|
||||
private Grid<User> createUsersGrid() {
|
||||
var grid = new Grid<User>();
|
||||
grid.addColumn(User::id).setHeader("User Identifier").setFooter(totalUsers);
|
||||
grid.addColumn(User::getId).setHeader("User Identifier").setFooter(totalUsers);
|
||||
grid.addColumn(user -> 0).setTextAlign(ColumnTextAlign.CENTER).setHeader("Linked with rooms") // TODO
|
||||
.setFooter(occupancy);
|
||||
grid.addComponentColumn(user -> new Span("buttons")).setHeader("Manage"); // TODO
|
||||
@ -39,6 +39,12 @@ public class UserList extends VerticalLayout {
|
||||
cachedUsers = users;
|
||||
usersGrid.setItems(users);
|
||||
totalUsers.setText("Total users: " + users.size());
|
||||
occupancy.setText("Occupancy: %s".formatted(room.getUsingPercentage(users.size())));
|
||||
occupancy.setText("Occupancy: %s".formatted(getUsingPercentage(room, users.size())));
|
||||
}
|
||||
|
||||
private String getUsingPercentage(Room room, int usedSlots) {
|
||||
if (room.isUnlimited()) return "0%";
|
||||
double percent = usedSlots / (double) room.getSlots() * 100;
|
||||
return ((int) percent) + "%";
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ public class RestApiConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
Supplier<RestTemplate> restTemplate(@Autowired RestTemplateBuilder builder) {
|
||||
Supplier<RestTemplate> restTemplateSupplier(@Autowired RestTemplateBuilder builder) {
|
||||
return builder::build;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
package ru.dragonestia.picker.cp.error;
|
||||
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.server.Command;
|
||||
import com.vaadin.flow.server.ErrorEvent;
|
||||
import com.vaadin.flow.server.ErrorHandler;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import ru.dragonestia.picker.api.exception.ApiException;
|
||||
import ru.dragonestia.picker.cp.component.Notifications;
|
||||
|
||||
@Log4j2
|
||||
public class ApplicationErrorHandler implements ErrorHandler {
|
||||
|
||||
@Override
|
||||
public void error(ErrorEvent errorEvent) {
|
||||
if (UI.getCurrent() == null) {
|
||||
log.throwing(errorEvent.getThrowable());
|
||||
return;
|
||||
}
|
||||
|
||||
if (errorEvent.getThrowable() instanceof ApiException ex) {
|
||||
execute(() -> {
|
||||
Notifications.error(ex.getMessage());
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
execute(() -> {
|
||||
Notifications.error("Internal server error");
|
||||
});
|
||||
log.throwing(errorEvent.getThrowable());
|
||||
}
|
||||
|
||||
private void execute(Command command) {
|
||||
UI.getCurrent().access(command);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package ru.dragonestia.picker.cp.listener;
|
||||
|
||||
import com.vaadin.flow.server.ServiceInitEvent;
|
||||
import com.vaadin.flow.server.VaadinServiceInitListener;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import ru.dragonestia.picker.cp.error.ApplicationErrorHandler;
|
||||
|
||||
@Log4j2
|
||||
@SpringComponent
|
||||
public class VaadinEventListener implements VaadinServiceInitListener {
|
||||
|
||||
@Override
|
||||
public void serviceInit(ServiceInitEvent event) {
|
||||
event.getSource().addSessionInitListener(e -> {
|
||||
e.getSession().setErrorHandler(new ApplicationErrorHandler());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.model;
|
||||
|
||||
import lombok.NonNull;
|
||||
import ru.dragonestia.picker.cp.model.type.PickingMode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public record Node(@NonNull String id, @NonNull PickingMode mode) implements Serializable {
|
||||
|
||||
@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 Node other) {
|
||||
return id.equals(other.id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import ru.dragonestia.picker.cp.model.type.SlotLimit;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
@Getter
|
||||
public class Room {
|
||||
|
||||
private final String id;
|
||||
private final String nodeId;
|
||||
private final SlotLimit slots;
|
||||
private final String payload;
|
||||
private boolean locked = false;
|
||||
|
||||
@JsonCreator
|
||||
private Room(@JsonProperty("id") String id,
|
||||
@JsonProperty("nodeIdentifier") String nodeId,
|
||||
@JsonProperty("slots") SlotLimit slots,
|
||||
@JsonProperty("payload") String payload,
|
||||
@JsonProperty("locked") boolean locked) {
|
||||
|
||||
this.id = id;
|
||||
this.nodeId = nodeId;
|
||||
this.slots = slots;
|
||||
this.payload = payload;
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
public static Room create(String roomId, Node node, SlotLimit limit, String payload) {
|
||||
return new Room(roomId, node.id(), limit, payload, false);
|
||||
}
|
||||
|
||||
public void setLocked(boolean value) {
|
||||
locked = value;
|
||||
}
|
||||
|
||||
public boolean isAvailable(int usedSlots, int requiredSlots) {
|
||||
if (locked) return false;
|
||||
if (slots.isUnlimited()) return true;
|
||||
return slots.slots() >= usedSlots + requiredSlots;
|
||||
}
|
||||
|
||||
@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 Room other) {
|
||||
return id.equals(other.id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public URI createApiURI() {
|
||||
return URI.create("/nodes/" + nodeId + "/rooms/" + id);
|
||||
}
|
||||
|
||||
public String getUsingPercentage(int used) {
|
||||
if (getSlots().isUnlimited()) return "0%";
|
||||
double percent = used / (double) getSlots().slots() * 100;
|
||||
return ((int) percent) + "%";
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.model.dto;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public record RoomDTO(String id, int slots, boolean locked) {
|
||||
|
||||
public URI uriAPI(Node node) {
|
||||
return uriAPI(node.id());
|
||||
}
|
||||
|
||||
public URI uriAPI(String nodeId) {
|
||||
return URI.create("/nodes/" + nodeId + "/rooms/" + id);
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.model.type;
|
||||
|
||||
import java.beans.Transient;
|
||||
|
||||
public record SlotLimit(int slots) {
|
||||
|
||||
private final static int UNLIMITED_VALUE = -1;
|
||||
|
||||
public static SlotLimit unlimited() {
|
||||
return new SlotLimit(UNLIMITED_VALUE);
|
||||
}
|
||||
|
||||
public static SlotLimit of(int slots) {
|
||||
return new SlotLimit(slots);
|
||||
}
|
||||
|
||||
@Transient
|
||||
public boolean isUnlimited() {
|
||||
return slots == UNLIMITED_VALUE;
|
||||
}
|
||||
}
|
||||
@ -3,10 +3,11 @@ package ru.dragonestia.picker.cp.page;
|
||||
import com.vaadin.flow.component.html.H1;
|
||||
import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.router.*;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
|
||||
@Route("/")
|
||||
public class HomePage extends VerticalLayout {
|
||||
public class HomePage extends VerticalLayout implements BeforeEnterObserver {
|
||||
|
||||
public HomePage() {
|
||||
super();
|
||||
@ -14,4 +15,9 @@ public class HomePage extends VerticalLayout {
|
||||
add(new H1("Hello world!"));
|
||||
add(new Paragraph("Hello world!"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
|
||||
throw new NodeNotFoundException("gdfsg");
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,72 +4,54 @@ import com.vaadin.flow.component.Html;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Hr;
|
||||
import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
import ru.dragonestia.picker.api.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.api.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.cp.component.Notifications;
|
||||
import ru.dragonestia.picker.cp.component.RoomList;
|
||||
import ru.dragonestia.picker.cp.component.NavPath;
|
||||
import ru.dragonestia.picker.cp.component.RegisterRoom;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.cp.model.dto.RoomDTO;
|
||||
import ru.dragonestia.picker.cp.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.cp.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.cp.util.RouteParamsExtractor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@PageTitle("Rooms")
|
||||
@Route("/nodes/:nodeId")
|
||||
public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserver {
|
||||
|
||||
private final NodeRepository nodeRepository;
|
||||
private final RoomRepository roomRepository;
|
||||
private final RouteParamsExtractor paramsExtractor;
|
||||
|
||||
private Node node;
|
||||
private RegisterRoom registerRoom;
|
||||
private RoomList roomList;
|
||||
|
||||
public NodeDetailsPage(@Autowired NodeRepository nodeRepository,
|
||||
@Autowired RoomRepository roomRepository) {
|
||||
|
||||
this.nodeRepository = nodeRepository;
|
||||
this.roomRepository = roomRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeEnter(BeforeEnterEvent event) {
|
||||
var nodeIdOpt = event.getRouteParameters().get("nodeId");
|
||||
if (nodeIdOpt.isEmpty()) {
|
||||
getUI().ifPresent(ui -> ui.navigate("/nodes"));
|
||||
return;
|
||||
}
|
||||
var nodeId = nodeIdOpt.get();
|
||||
add(new NavPath(new NavPath.Point("Nodes", "/nodes"), new NavPath.Point(nodeId, "/nodes/" + nodeId)));
|
||||
|
||||
var nodeOpt = nodeRepository.find(nodeId);
|
||||
if (nodeOpt.isEmpty()) {
|
||||
add(new H2("Error 404"));
|
||||
add(new Paragraph("Node not found"));
|
||||
Notification.show("Node '" + nodeId + "' does not exist", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
return;
|
||||
}
|
||||
node = nodeOpt.get();
|
||||
node = paramsExtractor.extractNodeId(event);
|
||||
|
||||
initComponents(node, roomRepository.all(node));
|
||||
}
|
||||
|
||||
private void initComponents(Node node, List<RoomDTO> rooms) {
|
||||
private void initComponents(Node node, List<Room.Short> rooms) {
|
||||
add(NavPath.toNode(node.getId()));
|
||||
printNodeDetails(node);
|
||||
add(new Hr());
|
||||
add(registerRoom = new RegisterRoom(node, (bucket) -> {
|
||||
add(registerRoom = new RegisterRoom(node, (room) -> {
|
||||
try {
|
||||
roomRepository.register(bucket);
|
||||
roomRepository.register(room);
|
||||
return new RegisterRoom.Response(false, null);
|
||||
} catch (Error error) {
|
||||
return new RegisterRoom.Response(true, error.getMessage());
|
||||
@ -78,9 +60,9 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
}
|
||||
}));
|
||||
add(new Hr());
|
||||
add(roomList = new RoomList(node.id(), rooms));
|
||||
roomList.setRemoveMethod(bucket -> {
|
||||
roomRepository.remove(node, bucket);
|
||||
add(roomList = new RoomList(node.getId(), rooms));
|
||||
roomList.setRemoveMethod(room -> {
|
||||
roomRepository.remove(node, room);
|
||||
roomList.update(roomRepository.all(node));
|
||||
});
|
||||
}
|
||||
@ -89,8 +71,8 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
add(new H2("Node details"));
|
||||
|
||||
var layout = new VerticalLayout();
|
||||
layout.add(new Html("<span>Identifier: <b>" + node.id() + "</b></span>"));
|
||||
layout.add(new Html("<span>Mode: <b>" + node.mode().getName() + "</b></span>"));
|
||||
layout.add(new Html("<span>Identifier: <b>" + node.getId() + "</b></span>"));
|
||||
layout.add(new Html("<span>Mode: <b>" + node.getMode().getName() + "</b></span>"));
|
||||
|
||||
add(layout);
|
||||
}
|
||||
|
||||
@ -7,10 +7,11 @@ import com.vaadin.flow.router.Route;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
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.cp.component.NavPath;
|
||||
import ru.dragonestia.picker.cp.component.NodeList;
|
||||
import ru.dragonestia.picker.cp.component.RegisterNode;
|
||||
import ru.dragonestia.picker.cp.repository.NodeRepository;
|
||||
|
||||
@Log4j2
|
||||
@Getter
|
||||
@ -26,7 +27,7 @@ public class NodesPage extends VerticalLayout {
|
||||
super();
|
||||
this.nodeRepository = nodeRepository;
|
||||
|
||||
add(new NavPath(new NavPath.Point("Nodes", "/nodes")));
|
||||
add(NavPath.rootNodes());
|
||||
add(registerNode = createRegisterNodeElement());
|
||||
add(new Hr());
|
||||
add(nodeList = createNodeListElement());
|
||||
@ -41,10 +42,7 @@ public class NodesPage extends VerticalLayout {
|
||||
try {
|
||||
nodeRepository.register(node);
|
||||
return new RegisterNode.Response(false, "");
|
||||
} catch (Error ex) {
|
||||
return new RegisterNode.Response(true, ex.getMessage());
|
||||
} catch (RuntimeException ex) {
|
||||
log.throwing(ex);
|
||||
} catch (ApiException ex) {
|
||||
return new RegisterNode.Response(true, ex.getMessage());
|
||||
} finally {
|
||||
nodeList.update(nodeRepository.all());
|
||||
|
||||
@ -8,28 +8,30 @@ import com.vaadin.flow.component.html.Hr;
|
||||
import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextArea;
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
import ru.dragonestia.picker.api.model.User;
|
||||
import ru.dragonestia.picker.api.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.api.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.api.repository.UserRepository;
|
||||
import ru.dragonestia.picker.cp.component.AddUsers;
|
||||
import ru.dragonestia.picker.cp.component.NavPath;
|
||||
import ru.dragonestia.picker.cp.component.Notifications;
|
||||
import ru.dragonestia.picker.cp.component.UserList;
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.cp.model.User;
|
||||
import ru.dragonestia.picker.cp.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.cp.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.cp.repository.UserRepository;
|
||||
import ru.dragonestia.picker.cp.util.RouteParamsExtractor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@PageTitle("Room details")
|
||||
@Route("/nodes/:nodeId/rooms/:roomId")
|
||||
public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserver {
|
||||
@ -37,6 +39,8 @@ public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
private final NodeRepository nodeRepository;
|
||||
private final RoomRepository roomRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final RouteParamsExtractor paramsExtractor;
|
||||
|
||||
private Node node;
|
||||
private Room room;
|
||||
private AddUsers addUsers;
|
||||
@ -44,57 +48,16 @@ public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
private Button lockRoomButton;
|
||||
private VerticalLayout roomInfo;
|
||||
|
||||
@Autowired
|
||||
public RoomDetailsPage(NodeRepository nodeRepository, RoomRepository roomRepository, UserRepository userRepository) {
|
||||
this.nodeRepository = nodeRepository;
|
||||
this.roomRepository = roomRepository;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeEnter(BeforeEnterEvent event) {
|
||||
var nodeIdOpt = event.getRouteParameters().get("nodeId");
|
||||
if (nodeIdOpt.isEmpty()) {
|
||||
getUI().ifPresent(ui -> ui.navigate("/nodes"));
|
||||
return;
|
||||
}
|
||||
|
||||
var roomIdOpt = event.getRouteParameters().get("roomId");
|
||||
if (roomIdOpt.isEmpty()) {
|
||||
getUI().ifPresent(ui -> ui.navigate("/rooms/" + nodeIdOpt.get()));
|
||||
return;
|
||||
}
|
||||
|
||||
var nodeId = nodeIdOpt.get();
|
||||
var roomId = roomIdOpt.get();
|
||||
add(new NavPath(new NavPath.Point("Nodes", "/nodes"),
|
||||
new NavPath.Point(nodeId, "/nodes/" + nodeId),
|
||||
new NavPath.Point(roomId, "/nodes/" + nodeId + "/rooms/" + roomId)));
|
||||
|
||||
var nodeOpt = nodeRepository.find(nodeId);
|
||||
if (nodeOpt.isEmpty()) {
|
||||
add(new H2("Error 404"));
|
||||
add(new Paragraph("Node not found!"));
|
||||
Notification.show("Node '" + nodeId + "' does not exist", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
return;
|
||||
}
|
||||
node = nodeOpt.get();
|
||||
|
||||
var bucketOpt = roomRepository.find(node, roomId);
|
||||
if (bucketOpt.isEmpty()) {
|
||||
add(new H2("Error 404"));
|
||||
add(new Paragraph("Room not found!"));
|
||||
Notification.show("Room '" + nodeId + "' does not exist", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
return;
|
||||
}
|
||||
room = bucketOpt.get();
|
||||
node = paramsExtractor.extractNodeId(event);
|
||||
room = paramsExtractor.extractRoomId(event, node);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
add(NavPath.toRoom(node.getId(), room.getId()));
|
||||
add(new H2("Room details"));
|
||||
printRoomDetails();
|
||||
add(new Hr());
|
||||
@ -108,7 +71,7 @@ public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
roomInfo.removeAll();
|
||||
roomInfo.add(new Html("<span>Node identifier: <b>" + room.getNodeId() + "</b></span>"));
|
||||
roomInfo.add(new Html("<span>Room identifier: <b>" + room.getId() + "</b></span>"));
|
||||
roomInfo.add(new Html("<span>Slots: <b>" + (room.getSlots().isUnlimited()? "Unlimited" : room.getSlots().slots()) + "</b></span>"));
|
||||
roomInfo.add(new Html("<span>Slots: <b>" + (room.isUnlimited()? "Unlimited" : room.getSlots()) + "</b></span>"));
|
||||
roomInfo.add(new Html("<span>Locked: <b>" + (room.isLocked()? "Yes" : "No") + "</b></span>"));
|
||||
}
|
||||
|
||||
@ -139,20 +102,13 @@ public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
|
||||
private void changeBucketLockedState() {
|
||||
var newValue = !room.isLocked();
|
||||
try {
|
||||
roomRepository.lock(room, newValue);
|
||||
} catch (Error error) {
|
||||
Notification.show(error.getMessage(), 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
return;
|
||||
}
|
||||
roomRepository.lock(room, newValue);
|
||||
|
||||
room.setLocked(newValue);
|
||||
setLockRoomButtonState();
|
||||
updateRoomInfo();
|
||||
|
||||
Notification.show("Success", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
Notifications.success("Success");
|
||||
}
|
||||
|
||||
private void appendUsers(Room room, Collection<User> users, boolean ignoreLimitation) {
|
||||
@ -160,7 +116,7 @@ public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
|
||||
var newUsers = users.stream()
|
||||
.filter(user -> {
|
||||
if (user.id().matches("^[aA-zZ\\d-.\\s:/@%?!~$)(+=_|;*]+$")) {
|
||||
if (user.getId().matches("^[aA-zZ\\d-.\\s:/@%?!~$)(+=_|;*]+$")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -173,15 +129,12 @@ public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserv
|
||||
|
||||
if (validationFail.get()) {
|
||||
if (newUsers.isEmpty()) {
|
||||
Notification.show("All users entered were added because they do not comply with the rule for writing the user identifier", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
Notifications.error("All users entered were added because they do not comply with the rule for writing the user identifier");
|
||||
} else {
|
||||
Notification.show("Not all users entered were added because they do not comply with the rule for writing the user identifier", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_WARNING);
|
||||
Notifications.warn("Not all users entered were added because they do not comply with the rule for writing the user identifier");
|
||||
}
|
||||
} else {
|
||||
Notification.show("Success", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
Notifications.success("Success");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package ru.dragonestia.picker.cp.page.plug;
|
||||
|
||||
import com.vaadin.flow.component.Html;
|
||||
import com.vaadin.flow.component.html.H1;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import ru.dragonestia.picker.cp.component.NavPath;
|
||||
|
||||
public abstract class ErrorPlug extends VerticalLayout {
|
||||
|
||||
public void init(NavPath path, String title, String description) {
|
||||
add(path);
|
||||
add(new H1(title));
|
||||
add(new Html("<p>" + description + "</p>"));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package ru.dragonestia.picker.cp.page.plug;
|
||||
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.ErrorParameter;
|
||||
import com.vaadin.flow.router.HasErrorParameter;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
|
||||
import ru.dragonestia.picker.cp.component.NavPath;
|
||||
|
||||
public class InvalidNodeIdentifierPlug extends ErrorPlug implements HasErrorParameter<InvalidNodeIdentifierException> {
|
||||
|
||||
@Override
|
||||
public int setErrorParameter(BeforeEnterEvent beforeEnterEvent, ErrorParameter<InvalidNodeIdentifierException> errorParameter) {
|
||||
var ex = errorParameter.getException();
|
||||
var nodeId = ex.getNodeId();
|
||||
|
||||
init(NavPath.toNode(nodeId), "Error 400", ex.getMessage());
|
||||
return HttpServletResponse.SC_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.dragonestia.picker.cp.page.plug;
|
||||
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.ErrorParameter;
|
||||
import com.vaadin.flow.router.HasErrorParameter;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import ru.dragonestia.picker.api.exception.InvalidRoomIdentifierException;
|
||||
import ru.dragonestia.picker.cp.component.NavPath;
|
||||
|
||||
public class InvalidRoomIdentifierPlug extends ErrorPlug implements HasErrorParameter<InvalidRoomIdentifierException> {
|
||||
|
||||
@Override
|
||||
public int setErrorParameter(BeforeEnterEvent event, ErrorParameter<InvalidRoomIdentifierException> errorParameter) {
|
||||
var ex = errorParameter.getException();
|
||||
var nodeId = ex.getNodeId();
|
||||
var roomId = ex.getRoomId();
|
||||
|
||||
init(NavPath.toRoom(nodeId, roomId), "Error 400", ex.getMessage());
|
||||
return HttpServletResponse.SC_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package ru.dragonestia.picker.cp.page.plug;
|
||||
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.ErrorParameter;
|
||||
import com.vaadin.flow.router.HasErrorParameter;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.cp.component.NavPath;
|
||||
|
||||
public class NodeNotFoundPlug extends ErrorPlug implements HasErrorParameter<NodeNotFoundException> {
|
||||
|
||||
@Override
|
||||
public int setErrorParameter(BeforeEnterEvent beforeEnterEvent, ErrorParameter<NodeNotFoundException> errorParameter) {
|
||||
var ex = errorParameter.getException();
|
||||
var nodeId = ex.getNodeId();
|
||||
|
||||
init(NavPath.toNode(nodeId), "Error 404", ex.getMessage());
|
||||
return HttpServletResponse.SC_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.dragonestia.picker.cp.page.plug;
|
||||
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.ErrorParameter;
|
||||
import com.vaadin.flow.router.HasErrorParameter;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
||||
import ru.dragonestia.picker.cp.component.NavPath;
|
||||
|
||||
public class RoomNotFoundPlug extends ErrorPlug implements HasErrorParameter<RoomNotFoundException> {
|
||||
|
||||
@Override
|
||||
public int setErrorParameter(BeforeEnterEvent beforeEnterEvent, ErrorParameter<RoomNotFoundException> errorParameter) {
|
||||
var ex = errorParameter.getException();
|
||||
var nodeId = ex.getNodeId();
|
||||
var roomId = ex.getRoomId();
|
||||
|
||||
init(NavPath.toRoom(nodeId, roomId), "Error 404", ex.getMessage());
|
||||
return HttpServletResponse.SC_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface NodeRepository {
|
||||
|
||||
void register(Node node);
|
||||
|
||||
List<Node> all();
|
||||
|
||||
Optional<Node> find(String nodeId);
|
||||
|
||||
void remove(String nodeId);
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.cp.model.dto.RoomDTO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RoomRepository {
|
||||
|
||||
List<RoomDTO> all(Node node);
|
||||
|
||||
void register(Room room);
|
||||
|
||||
void remove(Room room);
|
||||
|
||||
void remove(Node node, RoomDTO bucket);
|
||||
|
||||
Optional<Room> find(Node node, String roomId);
|
||||
|
||||
void lock(Room room, boolean value);
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.User;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface UserRepository {
|
||||
|
||||
void linkWithRoom(Room room, Collection<User> users, boolean force);
|
||||
|
||||
void unlinkFromRoom(Room room, Collection<User> users);
|
||||
|
||||
List<User> all(Room room);
|
||||
}
|
||||
@ -3,13 +3,15 @@ package ru.dragonestia.picker.cp.repository.impl;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.cp.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.NodeDetailsResponse;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.NodeListResponse;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.NodeRegisterResponse;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.api.repository.response.NodeDetailsResponse;
|
||||
import ru.dragonestia.picker.api.repository.response.NodeListResponse;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
import ru.dragonestia.picker.api.repository.NodeRepository;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -21,41 +23,30 @@ public class NodeRepositoryImpl implements NodeRepository {
|
||||
private final RestUtil rest;
|
||||
|
||||
@Override
|
||||
public void register(Node node) {
|
||||
NodeRegisterResponse response;
|
||||
try {
|
||||
response = rest.post(URI.create("nodes"),
|
||||
NodeRegisterResponse.class,
|
||||
params -> {
|
||||
params.put("nodeId", node.id());
|
||||
params.put("method", node.mode().name());
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Internal error", ex);
|
||||
}
|
||||
|
||||
if (!response.success()) {
|
||||
throw new Error(response.message());
|
||||
}
|
||||
public void register(Node node) throws InvalidNodeIdentifierException, NodeAlreadyExistException {
|
||||
rest.query("nodes", HttpMethod.POST, params -> {
|
||||
params.put("nodeId", node.getId());
|
||||
params.put("method", node.getMode().name());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> all() {
|
||||
return rest.get(URI.create("nodes"), NodeListResponse.class).nodes();
|
||||
return rest.query("nodes", HttpMethod.GET, NodeListResponse.class, params -> {}).nodes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Node> find(String nodeId) {
|
||||
try {
|
||||
var response = rest.get(URI.create("nodes/" + nodeId), NodeDetailsResponse.class);
|
||||
var response = rest.query("nodes/" + nodeId, HttpMethod.GET, NodeDetailsResponse.class, params -> {});
|
||||
return Optional.of(response.node());
|
||||
} catch (Exception ex) {
|
||||
} catch (NodeNotFoundException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String nodeId) {
|
||||
rest.delete(URI.create("nodes/" + nodeId), params -> {});
|
||||
rest.query("nodes/" + nodeId, HttpMethod.DELETE, params -> {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
package ru.dragonestia.picker.cp.repository.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import ru.dragonestia.picker.api.exception.ExceptionFactory;
|
||||
import ru.dragonestia.picker.api.repository.response.ErrorResponse;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -18,67 +18,41 @@ import java.util.function.Supplier;
|
||||
public class RestUtil {
|
||||
|
||||
private final URI serverUrl;
|
||||
private final Supplier<RestTemplate> restTemplate;
|
||||
private final Supplier<RestTemplate> restTemplateSupplier;
|
||||
|
||||
public <T> T get(URI uri, Class<T> responseType) {
|
||||
var template = restTemplate.get();
|
||||
return Objects.requireNonNull(template.getForObject(serverUrl.resolve(uri), responseType));
|
||||
public void query(String uri, HttpMethod method) {
|
||||
query(uri, method, ParamsConsumer.NONE);
|
||||
}
|
||||
|
||||
public <T> ResponseEntity<T> getEntity(URI uri, Class<T> responseType) {
|
||||
var template = restTemplate.get();
|
||||
return template.getForEntity(serverUrl.resolve(uri), responseType);
|
||||
}
|
||||
|
||||
public <T> T get(URI uri, Class<T> responseType, Consumer<Map<String, String>> paramsConsumer) {
|
||||
public void query(String uri, HttpMethod method, ParamsConsumer paramsConsumer) {
|
||||
var params = new HashMap<String, String>();
|
||||
paramsConsumer.accept(params);
|
||||
|
||||
var template = restTemplate.get();
|
||||
return Objects.requireNonNull(template.getForObject(buildPath(uri, params.keySet()),
|
||||
responseType,
|
||||
params));
|
||||
var template = restTemplateSupplier.get();
|
||||
try {
|
||||
template.exchange(buildPath(uri, params.keySet()), method, null, String.class, params);
|
||||
} catch (HttpClientErrorException ex) {
|
||||
throw ExceptionFactory.of(Objects.requireNonNull(ex.getResponseBodyAs(ErrorResponse.class)));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T post(URI uri, Class<T> responseType, Consumer<Map<String, String>> paramsConsumer) {
|
||||
public <T> T query(String uri, HttpMethod method, Class<T> clazz) {
|
||||
return query(uri, method, clazz, ParamsConsumer.NONE);
|
||||
}
|
||||
|
||||
public <T> T query(String uri, HttpMethod method, Class<T> clazz, ParamsConsumer paramsConsumer) {
|
||||
var params = new HashMap<String, String>();
|
||||
paramsConsumer.accept(params);
|
||||
|
||||
var template = restTemplate.get();
|
||||
return Objects.requireNonNull(template.postForObject(buildPath(uri, params.keySet()),
|
||||
null,
|
||||
responseType,
|
||||
params));
|
||||
var template = restTemplateSupplier.get();
|
||||
try {
|
||||
return template.exchange(buildPath(uri, params.keySet()), method, null, clazz, params).getBody();
|
||||
} catch (HttpClientErrorException ex) {
|
||||
throw ExceptionFactory.of(Objects.requireNonNull(ex.getResponseBodyAs(ErrorResponse.class)));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> ResponseEntity<T> postEntity(URI uri, Class<T> responseType, Consumer<Map<String, String>> paramsConsumer) {
|
||||
var params = new HashMap<String, String>();
|
||||
paramsConsumer.accept(params);
|
||||
|
||||
var template = restTemplate.get();
|
||||
return template.postForEntity(buildPath(uri, params.keySet()),
|
||||
null,
|
||||
responseType,
|
||||
params);
|
||||
}
|
||||
|
||||
public void put(URI uri, Consumer<Map<String, String>> paramsConsumer) {
|
||||
var params = new HashMap<String, String>();
|
||||
paramsConsumer.accept(params);
|
||||
|
||||
var template = restTemplate.get();
|
||||
template.put(buildPath(uri, params.keySet()), params);
|
||||
}
|
||||
|
||||
public void delete(URI uri, Consumer<Map<String, String>> paramsConsumer) {
|
||||
var params = new HashMap<String, String>();
|
||||
paramsConsumer.accept(params);
|
||||
|
||||
var template = restTemplate.get();
|
||||
template.delete(buildPath(uri, params.keySet()), params);
|
||||
}
|
||||
|
||||
private String buildPath(URI uri, Collection<String> paramKeys) {
|
||||
private String buildPath(String uri, Collection<String> paramKeys) {
|
||||
var path = new StringBuilder(serverUrl.resolve(uri) + "?");
|
||||
int left = paramKeys.size();
|
||||
for (var key: paramKeys) {
|
||||
@ -92,4 +66,9 @@ public class RestUtil {
|
||||
}
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
public interface ParamsConsumer extends Consumer<Map<String, String>> {
|
||||
|
||||
ParamsConsumer NONE = map -> {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,18 +3,18 @@ package ru.dragonestia.picker.cp.repository.impl;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
import ru.dragonestia.picker.cp.model.dto.RoomDTO;
|
||||
import ru.dragonestia.picker.cp.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.RoomInfoResponse;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.RoomListResponse;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.RoomRegisterResponse;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import ru.dragonestia.picker.api.exception.InvalidRoomIdentifierException;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.api.exception.RoomAlreadyExistException;
|
||||
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
||||
import ru.dragonestia.picker.api.model.Node;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
import ru.dragonestia.picker.api.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.api.repository.response.RoomInfoResponse;
|
||||
import ru.dragonestia.picker.api.repository.response.RoomListResponse;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@Log4j2
|
||||
@ -25,76 +25,44 @@ public class RoomRepositoryImpl implements RoomRepository {
|
||||
private final RestUtil rest;
|
||||
|
||||
@Override
|
||||
public List<RoomDTO> all(Node node) {
|
||||
var entity = rest.getEntity(URI.create("/nodes/" + node.id() + "/rooms"),
|
||||
RoomListResponse.class);
|
||||
|
||||
if (entity.getStatusCode().value() == 404) {
|
||||
throw new Error("Node with identifier '" + node.id() + "' does not exists'");
|
||||
}
|
||||
|
||||
if (!entity.hasBody()) {
|
||||
throw new Error("Room list did not present");
|
||||
}
|
||||
|
||||
return Objects.requireNonNull(entity.getBody()).rooms();
|
||||
public void register(Room room) throws NodeNotFoundException, InvalidRoomIdentifierException, RoomAlreadyExistException {
|
||||
rest.query("/nodes/" + room.getNodeId() + "/rooms", HttpMethod.POST, params -> {
|
||||
params.put("roomId", room.getId());
|
||||
params.put("slots", Integer.toString(room.getSlots()));
|
||||
params.put("payload", room.getPayload());
|
||||
params.put("locked", Boolean.toString(room.isLocked()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Room room) {
|
||||
public void remove(Room room) throws NodeNotFoundException {
|
||||
rest.query("/nodes/" + room.getNodeId() + "/rooms/" + room.getId(), HttpMethod.DELETE, params -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Node node, Room.Short room) throws NodeNotFoundException {
|
||||
rest.query("/nodes/" + node.getId() + "/rooms/" + room.id(), HttpMethod.DELETE, params -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Room.Short> all(Node node) throws NodeNotFoundException {
|
||||
return rest.query("/nodes/" + node.getId() + "/rooms", HttpMethod.GET, RoomListResponse.class, params -> {}).rooms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Room> find(Node node, String roomId) throws NodeNotFoundException {
|
||||
try {
|
||||
var response = rest.post(URI.create("/nodes/" + room.getNodeId() + "/rooms"),
|
||||
RoomRegisterResponse.class,
|
||||
params -> {
|
||||
params.put("roomId", room.getId());
|
||||
params.put("slots", Integer.toString(room.getSlots().slots()));
|
||||
params.put("payload", room.getPayload());
|
||||
params.put("locked", Boolean.toString(room.isLocked()));
|
||||
});
|
||||
|
||||
if (response.success()) return;
|
||||
throw new Error(response.message());
|
||||
} catch (HttpClientErrorException ex) {
|
||||
var response = ex.getResponseBodyAs(RoomRegisterResponse.class);
|
||||
|
||||
if (response != null) {
|
||||
throw new Error(response.message());
|
||||
}
|
||||
|
||||
log.throwing(ex);
|
||||
throw new Error("Internal error. Check logs");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Room room) {
|
||||
rest.delete(URI.create("/nodes/" + room.getNodeId() + "/rooms/" + room.getId()), params -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Node node, RoomDTO room) {
|
||||
rest.delete(URI.create("/nodes/" + node.id() + "/rooms/" + room.id()), params -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Room> find(Node node, String roomId) {
|
||||
try {
|
||||
var response = rest.get(URI.create("/nodes/" + node.id() + "/rooms/" + roomId), RoomInfoResponse.class, map -> {});
|
||||
var response = rest.query("/nodes/" + node.getId() + "/rooms/" + roomId, HttpMethod.GET, RoomInfoResponse.class, map -> {});
|
||||
return Optional.of(response.room());
|
||||
} catch (Exception ex) {
|
||||
} catch (RoomNotFoundException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock(Room room, boolean value) {
|
||||
try {
|
||||
rest.post(URI.create(room.createApiURI() + "/lock"), Boolean.class, params -> {
|
||||
params.put("newState", Boolean.toString(value));
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
log.throwing(ex);
|
||||
throw new Error("Error when changing room locked state");
|
||||
}
|
||||
public void lock(Room room, boolean value) throws NodeNotFoundException, RoomNotFoundException {
|
||||
rest.query("/nodes/%s/rooms/%s/lock".formatted(room.getNodeId(), room.getId()), HttpMethod.PUT, params -> {
|
||||
params.put("newState", Boolean.toString(value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,13 +3,15 @@ package ru.dragonestia.picker.cp.repository.impl;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
import ru.dragonestia.picker.cp.model.User;
|
||||
import ru.dragonestia.picker.cp.repository.UserRepository;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.LinkUsersWithRoomResponse;
|
||||
import ru.dragonestia.picker.cp.repository.impl.response.RoomUserListResponse;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
||||
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
||||
import ru.dragonestia.picker.api.model.Room;
|
||||
import ru.dragonestia.picker.api.model.User;
|
||||
import ru.dragonestia.picker.api.repository.UserRepository;
|
||||
import ru.dragonestia.picker.api.repository.response.RoomUserListResponse;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@ -21,47 +23,29 @@ public class UserRepositoryImpl implements UserRepository {
|
||||
private final RestUtil rest;
|
||||
|
||||
@Override
|
||||
public void linkWithRoom(Room room, Collection<User> users, boolean force) {
|
||||
try {
|
||||
var response = rest.post(URI.create("/nodes/%s/rooms/%s/users".formatted(room.getNodeId(), room.getId())),
|
||||
LinkUsersWithRoomResponse.class,
|
||||
params -> {
|
||||
params.put("userIds", String.join(",", users.stream().map(User::id).toList()));
|
||||
params.put("force", Boolean.toString(force));
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.success()) {
|
||||
throw new Error(response.message());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.throwing(ex);
|
||||
throw new Error("Internal error");
|
||||
}
|
||||
public void linkWithRoom(Room room, Collection<User> users, boolean force) throws NodeNotFoundException, RoomNotFoundException, RoomAreFullException {
|
||||
rest.query("/nodes/%s/rooms/%s/users".formatted(room.getNodeId(), room.getId()),
|
||||
HttpMethod.POST,
|
||||
params -> {
|
||||
params.put("userIds", String.join(",", users.stream().map(User::getId).toList()));
|
||||
params.put("force", Boolean.toString(force));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlinkFromRoom(Room room, Collection<User> users) {
|
||||
try {
|
||||
rest.delete(URI.create("/nodes/%s/rooms/%s/users".formatted(room.getNodeId(), room.getId())),
|
||||
params -> params.put("userIds", String.join(",", users.stream().map(User::id).toList())));
|
||||
} catch (Exception ex) {
|
||||
log.throwing(ex);
|
||||
throw new Error("Internal error");
|
||||
}
|
||||
public void unlinkFromRoom(Room room, Collection<User> users) throws NodeNotFoundException, RoomNotFoundException {
|
||||
rest.query("/nodes/%s/rooms/%s/users".formatted(room.getNodeId(), room.getId()),
|
||||
HttpMethod.DELETE,
|
||||
params -> {
|
||||
params.put("userIds", String.join(",", users.stream().map(User::getId).toList()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> all(Room room) {
|
||||
try {
|
||||
var response = rest.get(URI.create("/nodes/%s/rooms/%s/users".formatted(room.getNodeId(), room.getId())),
|
||||
RoomUserListResponse.class,
|
||||
params -> {});
|
||||
|
||||
return response.users();
|
||||
} catch (Exception ex) {
|
||||
log.throwing(ex);
|
||||
throw new Error("Internal error");
|
||||
}
|
||||
public List<User> all(Room room) throws NodeNotFoundException, RoomNotFoundException {
|
||||
return rest.query("/nodes/%s/rooms/%s/users".formatted(room.getNodeId(), room.getId()),
|
||||
HttpMethod.GET,
|
||||
RoomUserListResponse.class,
|
||||
params -> {}).users();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository.impl.response;
|
||||
|
||||
public record LinkUsersWithRoomResponse(boolean success, String message, int usedSlots, int totalSlots) {}
|
||||
@ -1,5 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository.impl.response;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
|
||||
public record NodeDetailsResponse(Node node) {}
|
||||
@ -1,7 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository.impl.response;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record NodeListResponse(List<Node> nodes) {}
|
||||
@ -1,3 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository.impl.response;
|
||||
|
||||
public record NodeRegisterResponse(boolean success, String message) {}
|
||||
@ -1,5 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository.impl.response;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.Room;
|
||||
|
||||
public record RoomInfoResponse(Room room) {}
|
||||
@ -1,7 +0,0 @@
|
||||
package ru.dragonestia.picker.cp.repository.impl.response;
|
||||
|
||||
import ru.dragonestia.picker.cp.model.dto.RoomDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record RoomListResponse(String node, List<RoomDTO> rooms) {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user