Renamed Bucket to Room

This commit is contained in:
Andrey Terentev 2023-12-27 22:06:09 +07:00
parent a8348dcd3d
commit 3de835cc6f
69 changed files with 999 additions and 998 deletions

View File

@ -59,8 +59,9 @@ For use api methods from main application you need add http headers with usernam
and password (`Room_Picker-Password`).
Example:
```http request
GET http://localhost:8080/nodes/hub/buckets/test-3/users
GET http://localhost:8080/nodes/hub/rooms/test-3/users
RoomPicker-User: admin
RoomPicker-Password: admin
```

View File

@ -6,8 +6,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LoadBalancerApplication {
public record Test(String text) {}
public static void main(String[] args) {
SpringApplication.run(LoadBalancerApplication.class, args);
}

View File

@ -8,12 +8,12 @@ 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.interceptor.DebugInterceptor;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.model.type.LoadBalancingMethod;
import ru.dragonestia.picker.model.type.PickingMode;
import ru.dragonestia.picker.model.type.SlotLimit;
import ru.dragonestia.picker.repository.BucketRepository;
import ru.dragonestia.picker.repository.RoomRepository;
import ru.dragonestia.picker.repository.NodeRepository;
import ru.dragonestia.picker.repository.UserRepository;
@ -27,7 +27,7 @@ import java.util.UUID;
public class TestConfig implements WebMvcConfigurer {
private final NodeRepository nodeRepository;
private final BucketRepository bucketRepository;
private final RoomRepository roomRepository;
private final UserRepository userRepository;
private final Random rand = new Random(0);
@ -39,29 +39,29 @@ public class TestConfig implements WebMvcConfigurer {
@Bean
void createNodes() {
createNodeWithContent(new Node("game-servers", LoadBalancingMethod.ROUND_ROBIN));
createNodeWithContent(new Node("game-lobbies", LoadBalancingMethod.LEAST_PICKED));
createNodeWithContent(new Node("hub", LoadBalancingMethod.SEQUENTIAL_FILLING));
createNodeWithContent(new Node("game-servers", PickingMode.ROUND_ROBIN));
createNodeWithContent(new Node("game-lobbies", PickingMode.LEAST_PICKED));
createNodeWithContent(new Node("hub", PickingMode.SEQUENTIAL_FILLING));
}
private void createNodeWithContent(Node node) {
nodeRepository.createNode(node);
nodeRepository.create(node);
for (int i = 1; i <= 5; i++) {
var slots = 5 * i;
var bucket = Bucket.create("test-" + i, node, SlotLimit.of(slots), "Some payload");
bucketRepository.createBucket(bucket);
var room = Room.create("test-" + i, node, SlotLimit.of(slots), "Some payload");
roomRepository.create(room);
for (int j = 0, n = rand.nextInt(slots + 1); j < n; j++) {
var user = new User("test-user-" + rand.nextInt(20));
userRepository.linkWithBucket(bucket, List.of(user), false);
userRepository.linkWithRoom(room, List.of(user), false);
}
}
for (int i = 0; i < 5; i++) {
var bucket = Bucket.create(randomUUID().toString(), node, SlotLimit.unlimited(), "Some payload");
bucket.setLocked((i & 1) == 0);
bucketRepository.createBucket(bucket);
var room = Room.create(randomUUID().toString(), node, SlotLimit.unlimited(), "Some payload");
room.setLocked((i & 1) == 0);
roomRepository.create(room);
}
}

View File

@ -1,117 +0,0 @@
package ru.dragonestia.picker.controller;
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.BucketInfoResponse;
import ru.dragonestia.picker.controller.response.BucketListResponse;
import ru.dragonestia.picker.controller.response.BucketRegisterResponse;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.type.SlotLimit;
import ru.dragonestia.picker.service.BucketService;
import ru.dragonestia.picker.service.NodeService;
import ru.dragonestia.picker.util.NamingValidator;
import java.util.Objects;
@Log4j2
@RestController
@RequestMapping("/nodes/{nodeIdentifier}/buckets")
@RequiredArgsConstructor
public class BucketController {
private final NodeService nodeService;
private final BucketService bucketService;
@GetMapping
ResponseEntity<BucketListResponse> allBuckets(@PathVariable(name = "nodeIdentifier") String nodeId) {
var nodeOpt = nodeService.findNode(nodeId);
return nodeOpt.map(node -> ResponseEntity.ok(new BucketListResponse(nodeId,
bucketService.allBuckets(node).stream()
.map(bucket -> new BucketListResponse.BucketDTO(bucket.getIdentifier(), bucket.getSlots().getSlots(), bucket.isLocked()))
.toList()
))).orElseGet(() -> ResponseEntity.notFound().build());
}
@PostMapping
ResponseEntity<BucketRegisterResponse> registerBucket(@PathVariable(name = "nodeIdentifier") String nodeId,
@RequestParam(name = "identifier") String bucketId,
@RequestParam(name = "slots") int slots,
@RequestParam(name = "payload") String payload,
@RequestParam(name = "locked", defaultValue = "false") boolean locked) {
var nodeOpt = nodeService.findNode(nodeId);
if (nodeOpt.isEmpty()) {
return ResponseEntity.status(404)
.body(new BucketRegisterResponse(false, "Node does not exist"));
}
var bucket = Bucket.create(bucketId, nodeOpt.get(), SlotLimit.of(slots), payload);
bucket.setLocked(locked);
try {
bucketService.createBucket(bucket);
return ResponseEntity.ok(new BucketRegisterResponse(true, ""));
} catch (Error error) {
return ResponseEntity.status(400).body(new BucketRegisterResponse(false, error.getMessage()));
} catch (Exception ex) {
return ResponseEntity.status(500).body(new BucketRegisterResponse(false, ex.getMessage()));
}
}
@DeleteMapping("/{identifier}")
ResponseEntity<?> removeBucket(@PathVariable("nodeIdentifier") String nodeId,
@PathVariable("identifier") String bucketId) {
if (!NamingValidator.validateNodeIdentifier(nodeId) || !NamingValidator.validateBucketIdentifier(bucketId)) {
return ResponseEntity.ok().build();
}
var nodeOpt = nodeService.findNode(nodeId);
nodeOpt.flatMap(node -> bucketService.findBucket(node, bucketId))
.ifPresent(bucketService::removeBucket);
return ResponseEntity.ok().build();
}
@GetMapping("/{identifier}")
ResponseEntity<BucketInfoResponse> info(@PathVariable("nodeIdentifier") String nodeId,
@PathVariable("identifier") String bucketId) {
if (!NamingValidator.validateNodeIdentifier(nodeId) || !NamingValidator.validateBucketIdentifier(bucketId)) {
return ResponseEntity.ok().build();
}
var nodeOpt = nodeService.findNode(nodeId);
if (nodeOpt.isEmpty()) {
return ResponseEntity.notFound().build();
}
var bucketOpt = bucketService.findBucket(Objects.requireNonNull(nodeOpt.get()), bucketId);
return bucketOpt.map(bucket -> ResponseEntity.ok(new BucketInfoResponse(bucket)))
.orElseGet(() -> ResponseEntity.notFound().build());
}
@PostMapping("/{identifier}/lock")
ResponseEntity<Boolean> lockBucket(@PathVariable("nodeIdentifier") String nodeId,
@PathVariable("identifier") String bucketId,
@RequestParam(name = "state") boolean value) {
if (!NamingValidator.validateNodeIdentifier(nodeId) || !NamingValidator.validateBucketIdentifier(bucketId)) {
return ResponseEntity.notFound().build();
}
var nodeOpt = nodeService.findNode(nodeId);
if (nodeOpt.isEmpty()) {
return ResponseEntity.notFound().build();
}
var bucketOpt = bucketService.findBucket(Objects.requireNonNull(nodeOpt.get()), bucketId);
if (bucketOpt.isEmpty()) {
return ResponseEntity.notFound().build();
}
var bucket = bucketOpt.get();
bucket.setLocked(value);
return ResponseEntity.ok(true);
}
}

View File

@ -8,7 +8,7 @@ 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.model.Node;
import ru.dragonestia.picker.model.type.LoadBalancingMethod;
import ru.dragonestia.picker.model.type.PickingMode;
import ru.dragonestia.picker.service.NodeService;
import ru.dragonestia.picker.util.NamingValidator;
@ -21,15 +21,15 @@ public class NodeController {
@GetMapping
NodeListResponse allNodes() {
return new NodeListResponse(nodeService.allNodes());
return new NodeListResponse(nodeService.all());
}
@PostMapping
NodeRegisterResponse registerNode(@RequestParam(name = "identifier") String identifier,
@RequestParam(name = "method") LoadBalancingMethod method) {
NodeRegisterResponse registerNode(@RequestParam(name = "nodeId") String nodeId,
@RequestParam(name = "method") PickingMode method) {
try {
nodeService.createNode(new Node(identifier, method));
nodeService.create(new Node(nodeId, method));
} catch (IllegalArgumentException ex) {
return new NodeRegisterResponse(false, ex.getMessage());
} catch (Error error) {
@ -39,25 +39,25 @@ public class NodeController {
return new NodeRegisterResponse(true, "");
}
@GetMapping("/{identifier}")
ResponseEntity<NodeDetailsResponse> nodeDetails(@PathVariable("identifier") String identifier) {
if (!NamingValidator.validateNodeIdentifier(identifier)) {
@GetMapping("/{nodeId}")
ResponseEntity<NodeDetailsResponse> nodeDetails(@PathVariable("nodeId") String nodeId) {
if (!NamingValidator.validateNodeId(nodeId)) {
return new ResponseEntity<>(HttpStatusCode.valueOf(404));
}
var nodeOpt = nodeService.findNode(identifier);
var nodeOpt = nodeService.find(nodeId);
return nodeOpt.map(node -> ResponseEntity.ok(new NodeDetailsResponse(node)))
.orElseGet(() -> new ResponseEntity<>(HttpStatusCode.valueOf(404)));
}
@DeleteMapping("/{identifier}")
ResponseEntity<?> removeNode(@PathVariable("identifier") String identifier) {
if (!NamingValidator.validateNodeIdentifier(identifier)) {
@DeleteMapping("/{nodeId}")
ResponseEntity<?> removeNode(@PathVariable("nodeId") String nodeId) {
if (!NamingValidator.validateNodeId(nodeId)) {
return ResponseEntity.ok().build();
}
var nodeOpt = nodeService.findNode(identifier);
nodeOpt.ifPresent(nodeService::removeNode);
var nodeOpt = nodeService.find(nodeId);
nodeOpt.ifPresent(nodeService::remove);
return ResponseEntity.ok().build();
}

View File

@ -0,0 +1,117 @@
package ru.dragonestia.picker.controller;
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.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")
@RequiredArgsConstructor
public class RoomController {
private final NodeService nodeService;
private final RoomService roomService;
@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());
}
@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) {
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);
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()));
}
}
@DeleteMapping("/{roomId}")
ResponseEntity<?> remove(@PathVariable("nodeId") String nodeId,
@PathVariable("roomId") String roomId) {
if (!NamingValidator.validateNodeId(nodeId) || !NamingValidator.validateRoomId(roomId)) {
return ResponseEntity.ok().build();
}
var nodeOpt = nodeService.find(nodeId);
nodeOpt.flatMap(node -> roomService.find(node, roomId))
.ifPresent(roomService::remove);
return ResponseEntity.ok().build();
}
@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();
}
var roomOpt = roomService.find(Objects.requireNonNull(nodeOpt.get()), roomId);
return roomOpt.map(room -> ResponseEntity.ok(new RoomInfoResponse(room)))
.orElseGet(() -> ResponseEntity.notFound().build());
}
@PostMapping("/{roomId}/lock")
ResponseEntity<Boolean> lockBucket(@PathVariable("nodeId") String nodeId,
@PathVariable("roomId") String roomId,
@RequestParam(name = "newState") boolean value) {
if (!NamingValidator.validateNodeId(nodeId) || !NamingValidator.validateRoomId(roomId)) {
return ResponseEntity.notFound().build();
}
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();
room.setLocked(value);
return ResponseEntity.ok(true);
}
}

View File

@ -1,111 +0,0 @@
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.BucketUserListResponse;
import ru.dragonestia.picker.controller.response.LinkUsersWithBucketResponse;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.service.BucketService;
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;
@RequiredArgsConstructor
@RestController
@RequestMapping("/nodes/{nodeIdentifier}/buckets/{bucketIdentifier}/users")
public class UserBucketController {
private final NodeService nodeService;
private final BucketService bucketService;
private final UserService userService;
@GetMapping
ResponseEntity<BucketUserListResponse> usersInsideBucket(@PathVariable(name = "nodeIdentifier") String nodeId,
@PathVariable(name = "bucketIdentifier") String bucketId) {
Bucket bucket;
try {
var temp = getNodeAndBucket(nodeId, bucketId);
bucket = temp.bucket();
} catch (Error error) {
return ResponseEntity.notFound().build();
}
var users = userService.getBucketUsers(bucket);
return ResponseEntity.ok(new BucketUserListResponse(bucket.getSlots().getSlots(), users.size(), users));
}
@PostMapping
ResponseEntity<LinkUsersWithBucketResponse> linkUserWithBucket(@PathVariable(name = "nodeIdentifier") String nodeId,
@PathVariable(name = "bucketIdentifier") String bucketId,
@RequestParam(name = "users") String userIds,
@RequestParam(name = "force") boolean force) {
Bucket bucket;
try {
var temp = getNodeAndBucket(nodeId, bucketId);
bucket = temp.bucket();
} catch (Error error) {
return ResponseEntity.status(404).body(new LinkUsersWithBucketResponse(false, error.getMessage()));
}
var list = new LinkedList<User>();
for (var username: userIds.split(",")) {
if (!NamingValidator.validateUserIdentifier(username)) continue;
list.add(new User(username));
}
try {
userService.linkUsersWithBucket(bucket, list, force);
} catch (Error error) {
return ResponseEntity.status(400).body(new LinkUsersWithBucketResponse(false, error.getMessage()));
}
return ResponseEntity.ok(new LinkUsersWithBucketResponse(true, "Success"));
}
@DeleteMapping
ResponseEntity<?> unlinkUsersForBucket(@PathVariable(name = "nodeIdentifier") String nodeId,
@PathVariable(name = "bucketIdentifier") String bucketId,
@RequestParam(name = "users") String userIdentifiers) {
Node node;
Bucket bucket;
try {
var temp = getNodeAndBucket(nodeId, bucketId);
node = temp.node();
bucket = temp.bucket();
} catch (Error error) {
return ResponseEntity.notFound().build();
}
return null;
}
private record NodeAndBucket(Node node, Bucket bucket) {}
private NodeAndBucket getNodeAndBucket(String nodeId, String bucketId) {
if (!NamingValidator.validateNodeIdentifier(nodeId) || !NamingValidator.validateBucketIdentifier(bucketId)) {
throw new Error();
}
var nodeOpt = nodeService.findNode(nodeId);
if (nodeOpt.isEmpty()) {
throw new Error();
}
var bucketOpt = bucketService.findBucket(Objects.requireNonNull(nodeOpt.get()), bucketId);
if (bucketOpt.isEmpty()) {
throw new Error();
}
return new NodeAndBucket(nodeOpt.get(), bucketOpt.get());
}
}

View File

@ -0,0 +1,111 @@
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.model.Room;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.service.RoomService;
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;
@RequiredArgsConstructor
@RestController
@RequestMapping("/nodes/{nodeId}/rooms/{roomId}/users")
public class UserRoomController {
private final NodeService nodeService;
private final RoomService roomService;
private final UserService userService;
@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();
}
var users = userService.getRoomUsers(room);
return ResponseEntity.ok(new RoomUserListResponse(room.getSlots().getSlots(), users.size(), users));
}
@PostMapping
ResponseEntity<LinkUsersWithRoomResponse> linkUserWithRoom(@PathVariable(name = "nodeId") String nodeId,
@PathVariable(name = "roomId") String roomId,
@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()));
}
var list = new LinkedList<User>();
for (var username: userIds.split(",")) {
if (!NamingValidator.validateUserId(username)) continue;
list.add(new User(username));
}
try {
userService.linkUsersWithRoom(room, list, force);
} catch (Error error) {
return ResponseEntity.status(400).body(new LinkUsersWithRoomResponse(false, error.getMessage()));
}
return ResponseEntity.ok(new LinkUsersWithRoomResponse(true, "Success"));
}
@DeleteMapping
ResponseEntity<?> unlinkUsersForBucket(@PathVariable(name = "nodeId") String nodeId,
@PathVariable(name = "roomId") String roomId,
@RequestParam(name = "userIds") String userIds) {
Node node;
Room room;
try {
var temp = getNodeAndRoom(nodeId, roomId);
node = temp.node();
room = temp.room();
} catch (Error error) {
return ResponseEntity.notFound().build();
}
return null;
}
private record NodeAndRoom(Node node, Room room) {}
private NodeAndRoom getNodeAndRoom(String nodeId, String roomId) {
if (!NamingValidator.validateNodeId(nodeId) || !NamingValidator.validateRoomId(roomId)) {
throw new Error();
}
var nodeOpt = nodeService.find(nodeId);
if (nodeOpt.isEmpty()) {
throw new Error();
}
var roomOpt = roomService.find(Objects.requireNonNull(nodeOpt.get()), roomId);
if (roomOpt.isEmpty()) {
throw new Error();
}
return new NodeAndRoom(nodeOpt.get(), roomOpt.get());
}
}

View File

@ -1,5 +0,0 @@
package ru.dragonestia.picker.controller.response;
import ru.dragonestia.picker.model.Bucket;
public record BucketInfoResponse(Bucket bucket) {}

View File

@ -1,8 +0,0 @@
package ru.dragonestia.picker.controller.response;
import java.util.List;
public record BucketListResponse(String node, List<BucketDTO> buckets) {
public record BucketDTO(String identifier, int slots, boolean locked) {}
}

View File

@ -1,3 +0,0 @@
package ru.dragonestia.picker.controller.response;
public record BucketRegisterResponse(boolean success, String message) {}

View File

@ -1,3 +0,0 @@
package ru.dragonestia.picker.controller.response;
public record LinkUsersWithBucketResponse(boolean success, String message) {}

View File

@ -0,0 +1,3 @@
package ru.dragonestia.picker.controller.response;
public record LinkUsersWithRoomResponse(boolean success, String message) {}

View File

@ -0,0 +1,5 @@
package ru.dragonestia.picker.controller.response;
import ru.dragonestia.picker.model.Room;
public record RoomInfoResponse(Room room) {}

View File

@ -0,0 +1,8 @@
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) {}
}

View File

@ -0,0 +1,3 @@
package ru.dragonestia.picker.controller.response;
public record RoomRegisterResponse(boolean success, String message) {}

View File

@ -4,4 +4,4 @@ import ru.dragonestia.picker.model.User;
import java.util.List;
public record BucketUserListResponse(int slots, int usedSlots, List<User> users) {}
public record RoomUserListResponse(int slots, int usedSlots, List<User> users) {}

View File

@ -1,13 +1,13 @@
package ru.dragonestia.picker.model;
import lombok.NonNull;
import ru.dragonestia.picker.model.type.LoadBalancingMethod;
import ru.dragonestia.picker.model.type.PickingMode;
public record Node(@NonNull String identifier, @NonNull LoadBalancingMethod method) {
public record Node(@NonNull String id, @NonNull PickingMode mode) {
@Override
public int hashCode() {
return identifier.hashCode();
return id.hashCode();
}
@Override
@ -15,7 +15,7 @@ public record Node(@NonNull String identifier, @NonNull LoadBalancingMethod meth
if (object == this) return true;
if (object == null) return false;
if (object instanceof Node other) {
return identifier.equals(other.identifier);
return id.equals(other.id);
}
return false;
}

View File

@ -7,16 +7,16 @@ import ru.dragonestia.picker.model.type.SlotLimit;
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Bucket {
public class Room {
private final String identifier;
private final String nodeIdentifier;
private final String id;
private final String nodeId;
private final SlotLimit slots;
private final String payload;
private boolean locked = false;
public static Bucket create(String identifier, Node node, SlotLimit limit, String payload) {
return new Bucket(identifier, node.identifier(), limit, payload);
public static Room create(String roomId, Node node, SlotLimit limit, String payload) {
return new Room(roomId, node.id(), limit, payload);
}
public void setLocked(boolean value) {
@ -31,15 +31,15 @@ public class Bucket {
@Override
public int hashCode() {
return identifier.hashCode();
return id.hashCode();
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof Bucket other) {
return identifier.equals(other.identifier);
if (object instanceof Room other) {
return id.equals(other.id);
}
return false;
}

View File

@ -2,11 +2,11 @@ package ru.dragonestia.picker.model;
import lombok.NonNull;
public record User(@NonNull String identifier) {
public record User(@NonNull String id) {
@Override
public int hashCode() {
return identifier.hashCode();
return id.hashCode();
}
@Override
@ -14,7 +14,7 @@ public record User(@NonNull String identifier) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof User other) {
return identifier.equals(other.identifier);
return id.equals(other.id);
}
return false;
}

View File

@ -1,6 +1,6 @@
package ru.dragonestia.picker.model.type;
public enum LoadBalancingMethod {
public enum PickingMode {
SEQUENTIAL_FILLING,
ROUND_ROBIN,
LEAST_PICKED,

View File

@ -1,32 +0,0 @@
package ru.dragonestia.picker.repository;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
public interface BucketRepository {
void createBucket(Bucket bucket);
void removeBucket(Bucket bucket);
Optional<Bucket> findBucket(Node node, String identifier);
List<Bucket> all(Node node);
default int countAvailableBuckets(Node node) {
return countAvailableBuckets(node, 0);
}
int countAvailableBuckets(Node node, int requiredSlots);
Optional<Bucket> pickFreeBucket(Node node, Collection<User> users);
void onCreateNode(Node node);
void onRemoveNode(Node node);
}

View File

@ -7,11 +7,11 @@ import java.util.Optional;
public interface NodeRepository {
void createNode(Node node);
void create(Node node);
void deleteNode(Node node);
void delete(Node node);
Optional<Node> findNode(String nodeId);
Optional<Node> find(String nodeId);
List<Node> all();
}

View File

@ -0,0 +1,32 @@
package ru.dragonestia.picker.repository;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
public interface RoomRepository {
void create(Room room);
void remove(Room room);
Optional<Room> find(Node node, String identifier);
List<Room> all(Node node);
default int countAvailable(Node node) {
return countAvailable(node, 0);
}
int countAvailable(Node node, int requiredSlots);
Optional<Room> pickFree(Node node, Collection<User> users);
void onCreateNode(Node node);
void onRemoveNode(Node node);
}

View File

@ -1,6 +1,6 @@
package ru.dragonestia.picker.repository;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User;
import java.util.Collection;
@ -9,13 +9,13 @@ import java.util.Map;
public interface UserRepository {
Map<User, Boolean> linkWithBucket(Bucket bucket, Collection<User> users, boolean force);
Map<User, Boolean> linkWithRoom(Room room, Collection<User> users, boolean force);
int unlinkWithBucket(Bucket bucket, Collection<User> users);
int unlinkWithRoom(Room room, Collection<User> users);
List<Bucket> findAllLinkedUserBuckets(User user);
List<Room> findAllLinkedUserRooms(User user);
void onRemoveBucket(Bucket bucket);
void onRemoveRoom(Room room);
List<User> usersOf(Bucket bucket);
List<User> usersOf(Room room);
}

View File

@ -1,131 +0,0 @@
package ru.dragonestia.picker.repository.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.BucketRepository;
import ru.dragonestia.picker.repository.UserRepository;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@Repository
@RequiredArgsConstructor
public class BucketRepositoryImpl implements BucketRepository {
private final UserRepository userRepository;
private final Map<Node, Buckets> node2bucketsMap = new ConcurrentHashMap<>();
@Override
public void createBucket(Bucket bucket) {
var nodeId = bucket.getNodeIdentifier();
synchronized (node2bucketsMap) {
var node = node2bucketsMap.keySet().stream()
.filter(n -> bucket.getNodeIdentifier().equals(n.identifier()))
.findFirst();
if (node.isEmpty()) {
throw new IllegalArgumentException("Node '" + nodeId + "' does not exist");
}
var buckets = node2bucketsMap.get(node.get());
if (buckets.containsKey(bucket.getIdentifier())) {
throw new IllegalArgumentException("Bucket already exists");
}
buckets.put(bucket.getIdentifier(), new BucketContainer(bucket, new AtomicInteger(0)));
}
}
@Override
public void removeBucket(Bucket bucket) {
var nodeId = bucket.getNodeIdentifier();
var node = node2bucketsMap.keySet().stream()
.filter(n -> bucket.getNodeIdentifier().equals(n.identifier()))
.findFirst();
synchronized (node2bucketsMap) {
if (node.isEmpty()) {
throw new IllegalArgumentException("Node '" + nodeId + "' does not exist");
}
node2bucketsMap.get(node.get()).remove(bucket.getIdentifier());
}
userRepository.onRemoveBucket(bucket);
}
@Override
public Optional<Bucket> findBucket(Node node, String identifier) {
synchronized (node2bucketsMap) {
if (!node2bucketsMap.containsKey(node)) {
throw new IllegalArgumentException("Node '" + node.identifier() + "' does not exist");
}
var result = node2bucketsMap.get(node).getOrDefault(identifier, null);
return result == null? Optional.empty() : Optional.of(result.bucket());
}
}
@Override
public List<Bucket> all(Node node) {
synchronized (node2bucketsMap) {
return node2bucketsMap.get(node).values().stream().map(BucketContainer::bucket).toList();
}
}
@Override
public int countAvailableBuckets(Node node, int requiredSlots) {
return (int) node2bucketsMap.get(node).values().stream()
.filter(bucket -> bucket.isAvailable(requiredSlots))
.count();
}
@Override
public Optional<Bucket> pickFreeBucket(Node node, Collection<User> users) {
synchronized (node2bucketsMap) {
if (!node2bucketsMap.containsKey(node)) {
throw new IllegalArgumentException("Node '" + node.identifier() + "' does not exist");
}
var requiredSlots = users.size();
var container = node2bucketsMap.get(node).values().stream() // TODO: pick bucket with used node balancing method
.filter(b -> b.isAvailable(requiredSlots))
.findFirst();
if (container.isPresent()) {
var cont = container.get();
var addedUsers = userRepository.linkWithBucket(cont.bucket(), users, false);
cont.used().getAndAdd((int) addedUsers.values().stream().filter(Boolean.TRUE::equals).count());
}
return container.map(BucketContainer::bucket);
}
}
@Override
public void onCreateNode(Node node) {
synchronized (node2bucketsMap) {
node2bucketsMap.put(node, new Buckets());
}
}
@Override
public void onRemoveNode(Node node) {
synchronized (node2bucketsMap) {
node2bucketsMap.remove(node);
}
}
private record BucketContainer(Bucket bucket, AtomicInteger used) {
public boolean isAvailable(int requiredSlots) {
return bucket.isAvailable(used.get(), requiredSlots);
}
}
private static class Buckets extends LinkedHashMap<String, BucketContainer> {}
}

View File

@ -3,7 +3,7 @@ package ru.dragonestia.picker.repository.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.repository.BucketRepository;
import ru.dragonestia.picker.repository.RoomRepository;
import ru.dragonestia.picker.repository.NodeRepository;
import java.util.List;
@ -15,33 +15,33 @@ import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
public class NodeRepositoryImpl implements NodeRepository {
private final BucketRepository bucketRepository;
private final RoomRepository roomRepository;
private final Map<String, Node> nodeMap = new ConcurrentHashMap<>();
@Override
public void createNode(Node node) {
public void create(Node node) {
synchronized (nodeMap) {
if (nodeMap.containsKey(node.identifier())) {
throw new IllegalArgumentException("Node with id '" + node.identifier() + "' already exists");
if (nodeMap.containsKey(node.id())) {
throw new IllegalArgumentException("Node with id '" + node.id() + "' already exists");
}
nodeMap.put(node.identifier(), node);
nodeMap.put(node.id(), node);
}
bucketRepository.onCreateNode(node);
roomRepository.onCreateNode(node);
}
@Override
public void deleteNode(Node node) {
public void delete(Node node) {
synchronized (nodeMap) {
nodeMap.remove(node.identifier());
nodeMap.remove(node.id());
}
bucketRepository.onRemoveNode(node);
roomRepository.onRemoveNode(node);
}
@Override
public Optional<Node> findNode(String nodeId) {
public Optional<Node> find(String nodeId) {
synchronized (nodeMap) {
return nodeMap.containsKey(nodeId)? Optional.of(nodeMap.get(nodeId)) : Optional.empty();
}

View File

@ -0,0 +1,131 @@
package ru.dragonestia.picker.repository.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.RoomRepository;
import ru.dragonestia.picker.repository.UserRepository;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@Repository
@RequiredArgsConstructor
public class RoomRepositoryImpl implements RoomRepository {
private final UserRepository userRepository;
private final Map<Node, Rooms> node2roomsMap = new ConcurrentHashMap<>();
@Override
public void create(Room room) {
var nodeId = room.getNodeId();
synchronized (node2roomsMap) {
var node = node2roomsMap.keySet().stream()
.filter(n -> room.getNodeId().equals(n.id()))
.findFirst();
if (node.isEmpty()) {
throw new IllegalArgumentException("Node '" + nodeId + "' does not exist");
}
var rooms = node2roomsMap.get(node.get());
if (rooms.containsKey(room.getId())) {
throw new IllegalArgumentException("Room already exists");
}
rooms.put(room.getId(), new RoomContainer(room, new AtomicInteger(0)));
}
}
@Override
public void remove(Room room) {
var nodeId = room.getNodeId();
var node = node2roomsMap.keySet().stream()
.filter(n -> room.getNodeId().equals(n.id()))
.findFirst();
synchronized (node2roomsMap) {
if (node.isEmpty()) {
throw new IllegalArgumentException("Node '" + nodeId + "' does not exist");
}
node2roomsMap.get(node.get()).remove(room.getId());
}
userRepository.onRemoveRoom(room);
}
@Override
public Optional<Room> find(Node node, String identifier) {
synchronized (node2roomsMap) {
if (!node2roomsMap.containsKey(node)) {
throw new IllegalArgumentException("Node '" + node.id() + "' does not exist");
}
var result = node2roomsMap.get(node).getOrDefault(identifier, null);
return result == null? Optional.empty() : Optional.of(result.room());
}
}
@Override
public List<Room> all(Node node) {
synchronized (node2roomsMap) {
return node2roomsMap.get(node).values().stream().map(RoomContainer::room).toList();
}
}
@Override
public int countAvailable(Node node, int requiredSlots) {
return (int) node2roomsMap.get(node).values().stream()
.filter(bucket -> bucket.isAvailable(requiredSlots))
.count();
}
@Override
public Optional<Room> pickFree(Node node, Collection<User> users) {
synchronized (node2roomsMap) {
if (!node2roomsMap.containsKey(node)) {
throw new IllegalArgumentException("Node '" + node.id() + "' does not exist");
}
var requiredSlots = users.size();
var container = node2roomsMap.get(node).values().stream() // TODO: pick room with used node balancing mode
.filter(r -> r.isAvailable(requiredSlots))
.findFirst();
if (container.isPresent()) {
var cont = container.get();
var addedUsers = userRepository.linkWithRoom(cont.room(), users, false);
cont.used().getAndAdd((int) addedUsers.values().stream().filter(Boolean.TRUE::equals).count());
}
return container.map(RoomContainer::room);
}
}
@Override
public void onCreateNode(Node node) {
synchronized (node2roomsMap) {
node2roomsMap.put(node, new Rooms());
}
}
@Override
public void onRemoveNode(Node node) {
synchronized (node2roomsMap) {
node2roomsMap.remove(node);
}
}
private record RoomContainer(Room room, AtomicInteger used) {
public boolean isAvailable(int requiredSlots) {
return room.isAvailable(used.get(), requiredSlots);
}
}
private static class Rooms extends LinkedHashMap<String, RoomContainer> {}
}

View File

@ -1,7 +1,7 @@
package ru.dragonestia.picker.repository.impl;
import org.springframework.stereotype.Repository;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.UserRepository;
@ -12,51 +12,51 @@ import java.util.concurrent.atomic.AtomicInteger;
@Repository
public class UserRepositoryImpl implements UserRepository {
private final Map<User, Set<Bucket>> usersMap = new ConcurrentHashMap<>();
private final Map<NodeBucketPath, Set<User>> bucketUsers = new ConcurrentHashMap<>();
private final Map<User, Set<Room>> usersMap = new ConcurrentHashMap<>();
private final Map<NodeRoomPath, Set<User>> roomUsers = new ConcurrentHashMap<>();
@Override
public Map<User, Boolean> linkWithBucket(Bucket bucket, Collection<User> users, boolean force) {
public Map<User, Boolean> linkWithRoom(Room room, Collection<User> users, boolean force) {
var result = new HashMap<User, Boolean>();
synchronized (usersMap) {
var path = new NodeBucketPath(bucket.getNodeIdentifier(), bucket.getIdentifier());
var usersSet = bucketUsers.getOrDefault(path, new HashSet<>());
var path = new NodeRoomPath(room.getNodeId(), room.getId());
var usersSet = roomUsers.getOrDefault(path, new HashSet<>());
if (force || bucket.getSlots().isUnlimited()) {
if (force || room.getSlots().isUnlimited()) {
users.forEach(user -> result.put(user, true));
} else {
for (var user : users) {
var set = usersMap.getOrDefault(user, new HashSet<>());
result.put(user, !set.contains(bucket));
result.put(user, !set.contains(room));
}
if (bucket.getSlots().getSlots() < usersSet.size() + users.size()) {
throw new Error("Bucket are full");
if (room.getSlots().getSlots() < usersSet.size() + users.size()) {
throw new Error("Room are full");
}
}
for (var user: users) {
var set = usersMap.getOrDefault(user, new HashSet<>());
set.add(bucket);
set.add(room);
usersMap.put(user, set);
}
usersSet.addAll(users);
bucketUsers.put(path, usersSet);
roomUsers.put(path, usersSet);
}
return result;
}
@Override
public int unlinkWithBucket(Bucket bucket, Collection<User> users) {
public int unlinkWithRoom(Room room, Collection<User> users) {
var counter = new AtomicInteger();
synchronized (usersMap) {
usersMap.forEach((user, set) -> {
if (!set.contains(bucket)) return;
if (!set.contains(room)) return;
set.remove(bucket);
set.remove(room);
counter.incrementAndGet();
if (set.isEmpty()) {
@ -64,30 +64,30 @@ public class UserRepositoryImpl implements UserRepository {
}
});
var path = new NodeBucketPath(bucket.getNodeIdentifier(), bucket.getIdentifier());
var set = bucketUsers.getOrDefault(path, new HashSet<>());
var path = new NodeRoomPath(room.getNodeId(), room.getId());
var set = roomUsers.getOrDefault(path, new HashSet<>());
set.removeAll(users);
if (set.isEmpty()) {
bucketUsers.remove(path);
roomUsers.remove(path);
} else {
bucketUsers.put(path, set);
roomUsers.put(path, set);
}
}
return counter.get();
}
@Override
public List<Bucket> findAllLinkedUserBuckets(User user) {
public List<Room> findAllLinkedUserRooms(User user) {
synchronized (usersMap) {
return usersMap.getOrDefault(user, new HashSet<>()).stream().toList();
}
}
@Override
public void onRemoveBucket(Bucket bucket) {
public void onRemoveRoom(Room room) {
synchronized (usersMap) {
usersMap.forEach((user, set) -> {
set.remove(bucket);
set.remove(room);
if (set.isEmpty()) {
usersMap.remove(user);
}
@ -96,15 +96,15 @@ public class UserRepositoryImpl implements UserRepository {
}
@Override
public List<User> usersOf(Bucket bucket) {
public List<User> usersOf(Room room) {
synchronized (usersMap) {
return bucketUsers.getOrDefault(new NodeBucketPath(bucket.getNodeIdentifier(), bucket.getIdentifier()), new HashSet<>())
return roomUsers.getOrDefault(new NodeRoomPath(room.getNodeId(), room.getId()), new HashSet<>())
.stream()
.toList();
}
}
private record NodeBucketPath(String node, String bucket) {
private record NodeRoomPath(String node, String bucket) {
@Override
public int hashCode() {
@ -115,7 +115,7 @@ public class UserRepositoryImpl implements UserRepository {
public boolean equals(Object o) {
if (o == null) return false;
if (o == this) return true;
if (o instanceof NodeBucketPath other) {
if (o instanceof NodeRoomPath other) {
return other.node().equals(node()) && other.bucket().equals(bucket());
}
return false;

View File

@ -1,27 +0,0 @@
package ru.dragonestia.picker.service;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import java.util.List;
import java.util.Optional;
public interface BucketService {
void createBucket(Bucket bucket);
void removeBucket(Bucket bucket);
Optional<Bucket> findBucket(Node node, String identifier);
List<Bucket> allBuckets(Node node);
default int countAvailableBuckets(Node node) {
return countAvailableBuckets(node, 1);
}
int countAvailableBuckets(Node node, int requiredSlots);
Bucket pickAvailableBucket(Node node, List<User> users);
}

View File

@ -7,11 +7,11 @@ import java.util.Optional;
public interface NodeService {
void createNode(Node node);
void create(Node node);
void removeNode(Node node);
void remove(Node node);
List<Node> allNodes();
List<Node> all();
Optional<Node> findNode(String identifier);
Optional<Node> find(String nodeId);
}

View File

@ -0,0 +1,27 @@
package ru.dragonestia.picker.service;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import java.util.List;
import java.util.Optional;
public interface RoomService {
void create(Room room);
void remove(Room room);
Optional<Room> find(Node node, String roomId);
List<Room> all(Node node);
default int countAvailable(Node node) {
return countAvailable(node, 1);
}
int countAvailable(Node node, int requiredSlots);
Room pickAvailable(Node node, List<User> users);
}

View File

@ -1,6 +1,6 @@
package ru.dragonestia.picker.service;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User;
import java.util.Collection;
@ -8,11 +8,11 @@ import java.util.List;
public interface UserService {
List<Bucket> getUserBuckets(User user);
List<Room> getUserRooms(User user);
void linkUsersWithBucket(Bucket bucket, Collection<User> users, boolean force);
void linkUsersWithRoom(Room room, Collection<User> users, boolean force);
void unlinkUsersFromBucket(Bucket bucket, Collection<User> users);
void unlinkUsersFromRoom(Room room, Collection<User> users);
List<User> getBucketUsers(Bucket bucket);
List<User> getRoomUsers(Room room);
}

View File

@ -1,54 +0,0 @@
package ru.dragonestia.picker.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.BucketRepository;
import ru.dragonestia.picker.service.BucketService;
import ru.dragonestia.picker.util.NamingValidator;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class BucketServiceImpl implements BucketService {
private final BucketRepository bucketRepository;
@Override
public void createBucket(Bucket bucket) {
if (!NamingValidator.validateBucketIdentifier(bucket.getIdentifier())) {
throw new Error("Invalid bucket identifier format");
}
bucketRepository.createBucket(bucket);
}
@Override
public void removeBucket(Bucket bucket) {
bucketRepository.removeBucket(bucket);
}
@Override
public Optional<Bucket> findBucket(Node node, String identifier) {
return bucketRepository.findBucket(node, identifier);
}
@Override
public List<Bucket> allBuckets(Node node) {
return bucketRepository.all(node);
}
@Override
public int countAvailableBuckets(Node node, int requiredSlots) {
throw new RuntimeException("Not implemented");
}
@Override
public Bucket pickAvailableBucket(Node node, List<User> users) {
throw new RuntimeException("Not implemented");
}
}

View File

@ -17,26 +17,26 @@ public class NodeServiceImpl implements NodeService {
private final NodeRepository nodeRepository;
@Override
public void createNode(Node node) {
if (!NamingValidator.validateNodeIdentifier(node.identifier())) {
throw new Error("Invalid node identifier format");
public void create(Node node) {
if (!NamingValidator.validateNodeId(node.id())) {
throw new Error("Invalid node id format");
}
nodeRepository.createNode(node);
nodeRepository.create(node);
}
@Override
public void removeNode(Node node) {
nodeRepository.deleteNode(node);
public void remove(Node node) {
nodeRepository.delete(node);
}
@Override
public List<Node> allNodes() {
public List<Node> all() {
return nodeRepository.all();
}
@Override
public Optional<Node> findNode(String identifier) {
return nodeRepository.findNode(identifier);
public Optional<Node> find(String nodeId) {
return nodeRepository.find(nodeId);
}
}

View File

@ -0,0 +1,54 @@
package ru.dragonestia.picker.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.RoomRepository;
import ru.dragonestia.picker.service.RoomService;
import ru.dragonestia.picker.util.NamingValidator;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class RoomServiceImpl implements RoomService {
private final RoomRepository roomRepository;
@Override
public void create(Room room) {
if (!NamingValidator.validateRoomId(room.getId())) {
throw new Error("Invalid room id format");
}
roomRepository.create(room);
}
@Override
public void remove(Room room) {
roomRepository.remove(room);
}
@Override
public Optional<Room> find(Node node, String roomId) {
return roomRepository.find(node, roomId);
}
@Override
public List<Room> all(Node node) {
return roomRepository.all(node);
}
@Override
public int countAvailable(Node node, int requiredSlots) {
throw new RuntimeException("Not implemented");
}
@Override
public Room pickAvailable(Node node, List<User> users) {
throw new RuntimeException("Not implemented");
}
}

View File

@ -2,7 +2,7 @@ package ru.dragonestia.picker.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.UserRepository;
import ru.dragonestia.picker.service.UserService;
@ -17,22 +17,22 @@ public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Override
public List<Bucket> getUserBuckets(User user) {
return userRepository.findAllLinkedUserBuckets(user);
public List<Room> getUserRooms(User user) {
return userRepository.findAllLinkedUserRooms(user);
}
@Override
public void linkUsersWithBucket(Bucket bucket, Collection<User> users, boolean force) {
userRepository.linkWithBucket(bucket, users, force);
public void linkUsersWithRoom(Room room, Collection<User> users, boolean force) {
userRepository.linkWithRoom(room, users, force);
}
@Override
public void unlinkUsersFromBucket(Bucket bucket, Collection<User> users) {
userRepository.unlinkWithBucket(bucket, users);
public void unlinkUsersFromRoom(Room room, Collection<User> users) {
userRepository.unlinkWithRoom(room, users);
}
@Override
public List<User> getBucketUsers(Bucket bucket) {
return userRepository.usersOf(bucket);
public List<User> getRoomUsers(Room room) {
return userRepository.usersOf(room);
}
}

View File

@ -5,15 +5,15 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public class NamingValidator {
public boolean validateNodeIdentifier(String input) {
public boolean validateNodeId(String input) {
return input.matches("^[a-z\\d-]+$");
}
public boolean validateBucketIdentifier(String input) {
public boolean validateRoomId(String input) {
return input.matches("^[a-z\\d-]+$");
}
public boolean validateUserIdentifier(String input) {
public boolean validateUserId(String input) {
return input.matches("^[aA-zZ\\d-.\\s:/@%?!~$)(+=_|;*]+$");
}
}

View File

@ -1,39 +0,0 @@
package ru.dragonestia.picker.service;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import ru.dragonestia.picker.model.Bucket;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.type.LoadBalancingMethod;
import ru.dragonestia.picker.model.type.SlotLimit;
import java.util.HashMap;
public class BucketServiceTest {
private BucketService bucketService;
private HashMap<String, Bucket> bucketMap;
@BeforeEach
void setup() {
bucketMap = new HashMap<>();
bucketService = Mockito.mock(BucketService.class);
Mockito.doAnswer(invocation -> {
var bucket = invocation.getArgument(0, Bucket.class);
return null;
}).when(bucketService).createBucket(Mockito.any(Bucket.class));
}
Node createNode() {
return new Node("test-node", LoadBalancingMethod.ROUND_ROBIN);
}
@Test
void test() {
var node = createNode();
bucketService.createBucket(Bucket.create("test-bucket", node, SlotLimit.unlimited(), ""));
}
}

View File

@ -0,0 +1,39 @@
package ru.dragonestia.picker.service;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.model.type.PickingMode;
import ru.dragonestia.picker.model.type.SlotLimit;
import java.util.HashMap;
public class RoomServiceTest {
private RoomService roomService;
private HashMap<String, Room> roomMap;
@BeforeEach
void setup() {
roomMap = new HashMap<>();
roomService = Mockito.mock(RoomService.class);
Mockito.doAnswer(invocation -> {
var room = invocation.getArgument(0, Room.class);
return null;
}).when(roomService).create(Mockito.any(Room.class));
}
Node createNode() {
return new Node("test-node", PickingMode.ROUND_ROBIN);
}
@Test
void test() {
var node = createNode();
roomService.create(Room.create("test-room", node, SlotLimit.unlimited(), ""));
}
}

View File

@ -12,21 +12,21 @@ import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import lombok.Getter;
import ru.dragonestia.picker.cp.model.Bucket;
import ru.dragonestia.picker.cp.model.Room;
import ru.dragonestia.picker.cp.model.User;
import java.util.List;
public class AddUsers extends Details {
private final Bucket bucket;
private final Room room;
private final Checkbox ignoreSlots;
private final VerticalLayout usersLayout;
public AddUsers(Bucket bucket) {
public AddUsers(Room room) {
super(new H2("Add users"));
this.bucket = bucket;
this.room = room;
usersLayout = new VerticalLayout();
add(addUserToTransacionButton());
@ -84,8 +84,8 @@ public class AddUsers extends Details {
}
private TextField createUserIdentifierField(boolean canBeDeleted) {
var field = new TextField("User identifier");
field.setPlaceholder("example-user-identifier");
var field = new TextField("User id");
field.setPlaceholder("example-user-id");
field.setHelperText("It can be UUID, username, numeric ids, etc");
field.setMinWidth(20, Unit.REM);

View File

@ -51,14 +51,14 @@ public class NodeList extends VerticalLayout {
var temp = input.trim();
nodesGrid.setItems(cachedNodes.stream()
.filter(node -> node.identifier().startsWith(temp))
.filter(node -> node.id().startsWith(temp))
.toList());
}
private Grid<Node> createGrid() {
var grid = new Grid<>(Node.class, false);
grid.addColumn(Node::identifier).setHeader("Identifier");
grid.addColumn(node -> node.method().getName()).setHeader("Mode");
grid.addColumn(Node::id).setHeader("Identifier");
grid.addColumn(node -> node.mode().getName()).setHeader("Mode");
grid.addComponentColumn(this::createManageButtons).setHeader("Manage");
return grid;
}
@ -84,12 +84,12 @@ public class NodeList extends VerticalLayout {
}
private void clickDetailsButton(Node node) {
getUI().ifPresent(ui -> ui.navigate("/nodes/" + node.identifier()));
getUI().ifPresent(ui -> ui.navigate("/nodes/" + node.id()));
}
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.identifier() + "' to field below and confirm."));
dialog.add(new Paragraph("Confirm that you want to delete node. Enter '" + node.id() + "' to field below and confirm."));
var inputField = new TextField();
dialog.add(inputField);
@ -98,14 +98,14 @@ public class NodeList extends VerticalLayout {
var button = new Button("Confirm");
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
button.addClickListener(event -> {
if (!node.identifier().equals(inputField.getValue())) {
if (!node.id().equals(inputField.getValue())) {
Notification.show("Invalid input", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
removeNode(node);
Notification.show("Node '" + node.identifier() + "' was successfully removed!", 3000, Notification.Position.TOP_END)
Notification.show("Node '" + node.id() + "' was successfully removed!", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
dialog.close();
});
@ -129,7 +129,7 @@ public class NodeList extends VerticalLayout {
private void removeNode(Node node) {
if (removeMethod != null) {
removeMethod.accept(node.identifier());
removeMethod.accept(node.id());
}
}
}

View File

@ -17,7 +17,7 @@ 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.LoadBalancingMethod;
import ru.dragonestia.picker.cp.model.type.PickingMode;
import java.util.function.Function;
@ -25,7 +25,7 @@ public class RegisterNode extends Details {
private final Function<Node, Response> onSubmit;
private final TextField identifierField;
private final RadioButtonGroup<LoadBalancingMethod> modeRadio;
private final RadioButtonGroup<PickingMode> modeRadio;
public RegisterNode(Function<Node, Response> onSubmit) {
super(new H2("Register node"));
@ -58,15 +58,15 @@ public class RegisterNode extends Details {
return button;
}
private RadioButtonGroup<LoadBalancingMethod> createModeRadio() {
var radio = new RadioButtonGroup<LoadBalancingMethod>("Mode");
private RadioButtonGroup<PickingMode> createModeRadio() {
var radio = new RadioButtonGroup<PickingMode>("Mode");
radio.addThemeVariants(RadioGroupVariant.LUMO_VERTICAL);
radio.setRenderer(new ComponentRenderer<Component, LoadBalancingMethod>(mode -> new Span(mode.getName())));
radio.setItems(LoadBalancingMethod.SEQUENTIAL_FILLING,
LoadBalancingMethod.ROUND_ROBIN,
LoadBalancingMethod.LEAST_PICKED);
radio.setRenderer(new ComponentRenderer<Component, PickingMode>(mode -> new Span(mode.getName())));
radio.setItems(PickingMode.SEQUENTIAL_FILLING,
PickingMode.ROUND_ROBIN,
PickingMode.LEAST_PICKED);
radio.setValue(LoadBalancingMethod.SEQUENTIAL_FILLING);
radio.setValue(PickingMode.SEQUENTIAL_FILLING);
return radio;
}
@ -76,7 +76,7 @@ public class RegisterNode extends Details {
private @Nullable String validateForm(String identifier) {
if (identifier.isEmpty()) {
return "Node identifier cannot be empty";
return "Node id cannot be empty";
}
return null;
@ -88,7 +88,7 @@ public class RegisterNode extends Details {
String error = null;
if (identifierField.isInvalid() || (error = validateForm(nodeIdentifier)) != null) {
if (identifierField.isInvalid()) {
error = "Invalid node identifier format";
error = "Invalid node id format";
}
Notification.show(error, 3000, Notification.Position.TOP_END)

View File

@ -13,28 +13,28 @@ 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.Bucket;
import ru.dragonestia.picker.cp.model.Room;
import ru.dragonestia.picker.cp.model.Node;
import ru.dragonestia.picker.cp.model.type.SlotLimit;
import java.util.function.Function;
public class RegisterBucket extends Details {
public class RegisterRoom extends Details {
private final Node node;
private final Function<Bucket, Response> onSubmit;
private final Function<Room, Response> onSubmit;
private final TextField identifierField;
private final TextArea payloadField;
private final Checkbox lockedField;
public RegisterBucket(Node node, Function<Bucket, Response> onSubmit) {
super(new H2("Register bucket"));
public RegisterRoom(Node node, Function<Room, Response> onSubmit) {
super(new H2("Register room"));
this.node = node;
this.onSubmit = onSubmit;
var layout = new VerticalLayout();
layout.add(createNodeIdentifierField());
layout.add(identifierField = createBucketIdentifierField());
layout.add(identifierField = createRoomIdentifierField());
layout.add(payloadField = createPayloadField());
layout.add(lockedField = createLockedField());
layout.add(createSubmitButton());
@ -45,15 +45,15 @@ public class RegisterBucket extends Details {
private TextField createNodeIdentifierField() {
var field = new TextField("Node identifier");
field.setMinWidth(20, Unit.REM);
field.setValue(node.identifier());
field.setValue(node.id());
field.setReadOnly(true);
return field;
}
private TextField createBucketIdentifierField() {
private TextField createRoomIdentifierField() {
var field = new TextField("Identifier");
field.setMinWidth(20, Unit.REM);
field.setPlaceholder("example-bucket-id");
field.setPlaceholder("example-room-id");
field.setHelperText("The field can contain only lowercase letters, numbers and a dash character");
field.setRequired(true);
field.setPattern("^[a-z\\d-]+$");
@ -103,7 +103,7 @@ public class RegisterBucket extends Details {
String error = null;
if (identifierField.isInvalid() || (error = validateForm(nodeIdentifier)) != null) {
if (identifierField.isInvalid()) {
error = "Invalid bucket identifier format";
error = "Invalid room id format";
}
Notification.show(error, 3000, Notification.Position.TOP_END)
@ -111,9 +111,9 @@ public class RegisterBucket extends Details {
return;
}
var bucket = Bucket.create(nodeIdentifier, node, SlotLimit.unlimited(), payloadField.getValue());
bucket.setLocked(lockedField.getValue());
var response = onSubmit.apply(bucket);
var room = Room.create(nodeIdentifier, node, SlotLimit.unlimited(), payloadField.getValue());
room.setLocked(lockedField.getValue());
var response = onSubmit.apply(room);
clear();
if (response.error()) {
Notification.show(response.reason(), 3000, Notification.Position.TOP_END)
@ -121,7 +121,7 @@ public class RegisterBucket extends Details {
return;
}
Notification.show("Bucket was successfully registered", 3000, Notification.Position.TOP_END)
Notification.show("Room was successfully registered", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
}

View File

@ -16,32 +16,32 @@ 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.BucketDTO;
import ru.dragonestia.picker.cp.model.dto.RoomDTO;
import java.util.List;
import java.util.function.Consumer;
public class BucketList extends VerticalLayout {
public class RoomList extends VerticalLayout {
private final String nodeIdentifier;
private final Grid<BucketDTO> bucketsGrid;
private final Grid<RoomDTO> roomsGrid;
private final TextField searchField;
private List<BucketDTO> cachedBuckets;
@Setter private Consumer<BucketDTO> removeMethod;
private List<RoomDTO> cachedRooms;
@Setter private Consumer<RoomDTO> removeMethod;
public BucketList(String nodeIdentifier, List<BucketDTO> buckets) {
public RoomList(String nodeIdentifier, List<RoomDTO> buckets) {
this.nodeIdentifier = nodeIdentifier;
cachedBuckets = buckets;
cachedRooms = buckets;
add(new H2("Buckets"));
add(new H2("Rooms"));
add(searchField = createSearchField());
add(bucketsGrid = createGrid());
add(roomsGrid = createGrid());
update(buckets);
}
private TextField createSearchField() {
var field = new TextField("Search bucket");
var field = new TextField("Search room");
field.setPrefixComponent(new Icon(VaadinIcon.SEARCH));
field.setClearButtonVisible(true);
field.setHelperText("Press Enter to search");
@ -52,27 +52,27 @@ public class BucketList extends VerticalLayout {
private void applySearch(String input) {
var temp = input.trim();
bucketsGrid.setItems(cachedBuckets.stream()
.filter(bucket -> bucket.identifier().startsWith(temp))
roomsGrid.setItems(cachedRooms.stream()
.filter(room -> room.id().startsWith(temp))
.toList());
}
private Grid<BucketDTO> createGrid() {
var grid = new Grid<>(BucketDTO.class, false);
grid.addColumn(BucketDTO::identifier).setHeader("Identifier");
grid.addComponentColumn(bucket -> {
private Grid<RoomDTO> createGrid() {
var grid = new Grid<>(RoomDTO.class, false);
grid.addColumn(RoomDTO::id).setHeader("Identifier");
grid.addComponentColumn(room -> {
var result = new Span();
if (bucket.slots() == -1) {
if (room.slots() == -1) {
result.setText("Unlimited");
result.getElement().getThemeList().add("badge contrast");
} else {
result.setText(Integer.toString(bucket.slots()));
result.setText(Integer.toString(room.slots()));
}
return result;
}).setHeader("Slots").setTextAlign(ColumnTextAlign.CENTER);
grid.addComponentColumn(bucket -> {
grid.addComponentColumn(room -> {
var result = new Span();
if (bucket.locked()) {
if (room.locked()) {
result.setText("Yes");
result.getElement().getThemeList().add("badge error");
} else {
@ -84,19 +84,19 @@ public class BucketList extends VerticalLayout {
return grid;
}
private HorizontalLayout createManageButtons(BucketDTO bucket) {
private HorizontalLayout createManageButtons(RoomDTO room) {
var layout = new HorizontalLayout();
{
var button = new Button("Details");
button.addClickListener(event -> clickDetailsButton(bucket));
button.addClickListener(event -> clickDetailsButton(room));
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
layout.add(button);
}
{
var button = new Button("Remove");
button.addClickListener(event -> clickRemoveButton(bucket));
button.addClickListener(event -> clickRemoveButton(room));
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
layout.add(button);
}
@ -104,16 +104,16 @@ public class BucketList extends VerticalLayout {
return layout;
}
private void clickDetailsButton(BucketDTO bucket) {
private void clickDetailsButton(RoomDTO bucket) {
getUI().ifPresent(ui -> {
ui.navigate("/nodes/" + nodeIdentifier +
"/buckets/" + bucket.identifier());
"/rooms/" + bucket.id());
});
}
private void clickRemoveButton(BucketDTO bucket) {
private void clickRemoveButton(RoomDTO bucket) {
var dialog = new Dialog("Confirm bucket deletion");
dialog.add(new Paragraph("Confirm that you want to delete bucket. Enter '" + bucket.identifier() + "' to field below and confirm."));
dialog.add(new Paragraph("Confirm that you want to delete bucket. Enter '" + bucket.id() + "' to field below and confirm."));
var inputField = new TextField();
dialog.add(inputField);
@ -122,14 +122,14 @@ public class BucketList extends VerticalLayout {
var button = new Button("Confirm");
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
button.addClickListener(event -> {
if (!bucket.identifier().equals(inputField.getValue())) {
if (!bucket.id().equals(inputField.getValue())) {
Notification.show("Invalid input", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
removeBucket(bucket);
Notification.show("Bucket '" + bucket.identifier() + "' was successfully removed!", 3000, Notification.Position.TOP_END)
Notification.show("Bucket '" + bucket.id() + "' was successfully removed!", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
dialog.close();
});
@ -146,12 +146,12 @@ public class BucketList extends VerticalLayout {
dialog.open();
}
public void update(List<BucketDTO> buckets) {
cachedBuckets = buckets;
public void update(List<RoomDTO> buckets) {
cachedRooms = buckets;
applySearch(searchField.getValue());
}
private void removeBucket(BucketDTO bucket) {
private void removeBucket(RoomDTO bucket) {
if (removeMethod != null) {
removeMethod.accept(bucket);
}

View File

@ -4,7 +4,7 @@ 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.Bucket;
import ru.dragonestia.picker.cp.model.Room;
import ru.dragonestia.picker.cp.model.User;
import java.util.ArrayList;
@ -12,14 +12,14 @@ import java.util.List;
public class UserList extends VerticalLayout {
private final Bucket bucket;
private final Room room;
private final Grid<User> usersGrid;
private final Span totalUsers = new Span();
private final Span occupancy = new Span();
private List<User> cachedUsers = new ArrayList<>();
public UserList(Bucket bucket, List<User> users) {
this.bucket = bucket;
public UserList(Room room, List<User> users) {
this.room = room;
add(usersGrid = createUsersGrid());
@ -28,8 +28,8 @@ public class UserList extends VerticalLayout {
private Grid<User> createUsersGrid() {
var grid = new Grid<User>();
grid.addColumn(User::identifier).setHeader("User Identifier").setFooter(totalUsers);
grid.addColumn(user -> 0).setTextAlign(ColumnTextAlign.CENTER).setHeader("Linked with buckets") // TODO
grid.addColumn(User::id).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
return grid;
@ -39,6 +39,6 @@ public class UserList extends VerticalLayout {
cachedUsers = users;
usersGrid.setItems(users);
totalUsers.setText("Total users: " + users.size());
occupancy.setText("Occupancy: %s".formatted(bucket.getUsingPercentage(users.size())));
occupancy.setText("Occupancy: %s".formatted(room.getUsingPercentage(users.size())));
}
}

View File

@ -1,15 +1,15 @@
package ru.dragonestia.picker.cp.model;
import lombok.NonNull;
import ru.dragonestia.picker.cp.model.type.LoadBalancingMethod;
import ru.dragonestia.picker.cp.model.type.PickingMode;
import java.io.Serializable;
public record Node(@NonNull String identifier, @NonNull LoadBalancingMethod method) implements Serializable {
public record Node(@NonNull String id, @NonNull PickingMode mode) implements Serializable {
@Override
public int hashCode() {
return identifier.hashCode();
return id.hashCode();
}
@Override
@ -17,7 +17,7 @@ public record Node(@NonNull String identifier, @NonNull LoadBalancingMethod meth
if (object == this) return true;
if (object == null) return false;
if (object instanceof Node other) {
return identifier.equals(other.identifier);
return id.equals(other.id);
}
return false;
}

View File

@ -8,29 +8,30 @@ import ru.dragonestia.picker.cp.model.type.SlotLimit;
import java.net.URI;
@Getter
public class Bucket {
public class Room {
private final String identifier;
private final String nodeIdentifier;
private final String id;
private final String nodeId;
private final SlotLimit slots;
private final String payload;
private boolean locked = false;
@JsonCreator
private Bucket(@JsonProperty("identifier") String identifier,
@JsonProperty("nodeIdentifier") String nodeIdentifier,
@JsonProperty("slots") SlotLimit slots,
@JsonProperty("payload") String payload,
@JsonProperty("locked") boolean locked) {
this.identifier = identifier;
this.nodeIdentifier = nodeIdentifier;
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 Bucket create(String identifier, Node node, SlotLimit limit, String payload) {
return new Bucket(identifier, node.identifier(), limit, payload, false);
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) {
@ -45,21 +46,21 @@ public class Bucket {
@Override
public int hashCode() {
return identifier.hashCode();
return id.hashCode();
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof Bucket other) {
return identifier.equals(other.identifier);
if (object instanceof Room other) {
return id.equals(other.id);
}
return false;
}
public URI createApiURI() {
return URI.create("/nodes/" + nodeIdentifier + "/buckets/" + identifier);
return URI.create("/nodes/" + nodeId + "/rooms/" + id);
}
public String getUsingPercentage(int used) {

View File

@ -2,11 +2,11 @@ package ru.dragonestia.picker.cp.model;
import lombok.NonNull;
public record User(@NonNull String identifier) {
public record User(@NonNull String id) {
@Override
public int hashCode() {
return identifier.hashCode();
return id.hashCode();
}
@Override
@ -14,7 +14,7 @@ public record User(@NonNull String identifier) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof User other) {
return identifier.equals(other.identifier);
return id.equals(other.id);
}
return false;
}

View File

@ -4,13 +4,13 @@ import ru.dragonestia.picker.cp.model.Node;
import java.net.URI;
public record BucketDTO(String identifier, int slots, boolean locked) {
public record RoomDTO(String id, int slots, boolean locked) {
public URI uriAPI(Node node) {
return uriAPI(node.identifier());
return uriAPI(node.id());
}
public URI uriAPI(String nodeId) {
return URI.create("/nodes/" + nodeId + "/buckets/" + identifier);
return URI.create("/nodes/" + nodeId + "/rooms/" + id);
}
}

View File

@ -5,7 +5,7 @@ import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum LoadBalancingMethod {
public enum PickingMode {
SEQUENTIAL_FILLING("Sequential filling"),
ROUND_ROBIN("Round Robin"),
LEAST_PICKED("Least Picked");

View File

@ -13,12 +13,12 @@ import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import ru.dragonestia.picker.cp.component.BucketList;
import ru.dragonestia.picker.cp.component.RoomList;
import ru.dragonestia.picker.cp.component.NavPath;
import ru.dragonestia.picker.cp.component.RegisterBucket;
import ru.dragonestia.picker.cp.component.RegisterRoom;
import ru.dragonestia.picker.cp.model.Node;
import ru.dragonestia.picker.cp.model.dto.BucketDTO;
import ru.dragonestia.picker.cp.repository.BucketRepository;
import ru.dragonestia.picker.cp.model.dto.RoomDTO;
import ru.dragonestia.picker.cp.repository.RoomRepository;
import ru.dragonestia.picker.cp.repository.NodeRepository;
import java.util.List;
@ -29,16 +29,16 @@ import java.util.List;
public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserver {
private final NodeRepository nodeRepository;
private final BucketRepository bucketRepository;
private final RoomRepository roomRepository;
private Node node;
private RegisterBucket registerBucket;
private BucketList bucketList;
private RegisterRoom registerRoom;
private RoomList roomList;
public NodeDetailsPage(@Autowired NodeRepository nodeRepository,
@Autowired BucketRepository bucketRepository) {
@Autowired RoomRepository roomRepository) {
this.nodeRepository = nodeRepository;
this.bucketRepository = bucketRepository;
this.roomRepository = roomRepository;
}
@Override
@ -51,7 +51,7 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv
var nodeId = nodeIdOpt.get();
add(new NavPath(new NavPath.Point("Nodes", "/nodes"), new NavPath.Point(nodeId, "/nodes/" + nodeId)));
var nodeOpt = nodeRepository.findNode(nodeId);
var nodeOpt = nodeRepository.find(nodeId);
if (nodeOpt.isEmpty()) {
add(new H2("Error 404"));
add(new Paragraph("Node not found"));
@ -61,27 +61,27 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv
}
node = nodeOpt.get();
initComponents(node, bucketRepository.all(node));
initComponents(node, roomRepository.all(node));
}
private void initComponents(Node node, List<BucketDTO> buckets) {
private void initComponents(Node node, List<RoomDTO> rooms) {
printNodeDetails(node);
add(new Hr());
add(registerBucket = new RegisterBucket(node, (bucket) -> {
add(registerRoom = new RegisterRoom(node, (bucket) -> {
try {
bucketRepository.register(bucket);
return new RegisterBucket.Response(false, null);
roomRepository.register(bucket);
return new RegisterRoom.Response(false, null);
} catch (Error error) {
return new RegisterBucket.Response(true, error.getMessage());
return new RegisterRoom.Response(true, error.getMessage());
} finally {
bucketList.update(bucketRepository.all(node));
roomList.update(roomRepository.all(node));
}
}));
add(new Hr());
add(bucketList = new BucketList(node.identifier(), buckets));
bucketList.setRemoveMethod(bucket -> {
bucketRepository.remove(node, bucket);
bucketList.update(bucketRepository.all(node));
add(roomList = new RoomList(node.id(), rooms));
roomList.setRemoveMethod(bucket -> {
roomRepository.remove(node, bucket);
roomList.update(roomRepository.all(node));
});
}
@ -89,8 +89,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.identifier() + "</b></span>"));
layout.add(new Html("<span>Mode: <b>" + node.method().getName() + "</b></span>"));
layout.add(new Html("<span>Identifier: <b>" + node.id() + "</b></span>"));
layout.add(new Html("<span>Mode: <b>" + node.mode().getName() + "</b></span>"));
add(layout);
}

View File

@ -31,15 +31,15 @@ public class NodesPage extends VerticalLayout {
add(new Hr());
add(nodeList = createNodeListElement());
nodeList.setRemoveMethod(nodeIdentifier -> {
nodeRepository.removeNode(nodeIdentifier);
nodeList.update(nodeRepository.getNodes());
nodeRepository.remove(nodeIdentifier);
nodeList.update(nodeRepository.all());
});
}
protected RegisterNode createRegisterNodeElement() {
return new RegisterNode(node -> {
try {
nodeRepository.registerNode(node);
nodeRepository.register(node);
return new RegisterNode.Response(false, "");
} catch (Error ex) {
return new RegisterNode.Response(true, ex.getMessage());
@ -47,12 +47,12 @@ public class NodesPage extends VerticalLayout {
log.throwing(ex);
return new RegisterNode.Response(true, ex.getMessage());
} finally {
nodeList.update(nodeRepository.getNodes());
nodeList.update(nodeRepository.all());
}
});
}
protected NodeList createNodeListElement() {
return new NodeList(nodeRepository.getNodes());
return new NodeList(nodeRepository.all());
}
}

View File

@ -1,6 +1,7 @@
package ru.dragonestia.picker.cp.page;
import com.vaadin.flow.component.Html;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.H2;
@ -19,29 +20,29 @@ import org.springframework.beans.factory.annotation.Autowired;
import ru.dragonestia.picker.cp.component.AddUsers;
import ru.dragonestia.picker.cp.component.NavPath;
import ru.dragonestia.picker.cp.component.UserList;
import ru.dragonestia.picker.cp.model.Bucket;
import ru.dragonestia.picker.cp.model.Room;
import ru.dragonestia.picker.cp.model.Node;
import ru.dragonestia.picker.cp.repository.BucketRepository;
import ru.dragonestia.picker.cp.repository.RoomRepository;
import ru.dragonestia.picker.cp.repository.NodeRepository;
import ru.dragonestia.picker.cp.repository.UserRepository;
@Route("/nodes/:nodeId/buckets/:bucketId")
public class BucketDetailsPage extends VerticalLayout implements BeforeEnterObserver {
@Route("/nodes/:nodeId/rooms/:roomId")
public class RoomDetailsPage extends VerticalLayout implements BeforeEnterObserver {
private final NodeRepository nodeRepository;
private final BucketRepository bucketRepository;
private final RoomRepository roomRepository;
private final UserRepository userRepository;
private Node node;
private Bucket bucket;
private Room room;
private AddUsers addUsers;
private UserList userList;
private Button lockBucketButton;
private VerticalLayout bucketInfo;
private Button lockRoomButton;
private VerticalLayout roomInfo;
@Autowired
public BucketDetailsPage(NodeRepository nodeRepository, BucketRepository bucketRepository, UserRepository userRepository) {
public RoomDetailsPage(NodeRepository nodeRepository, RoomRepository roomRepository, UserRepository userRepository) {
this.nodeRepository = nodeRepository;
this.bucketRepository = bucketRepository;
this.roomRepository = roomRepository;
this.userRepository = userRepository;
}
@ -53,19 +54,19 @@ public class BucketDetailsPage extends VerticalLayout implements BeforeEnterObse
return;
}
var bucketIdOpt = event.getRouteParameters().get("bucketId");
if (bucketIdOpt.isEmpty()) {
getUI().ifPresent(ui -> ui.navigate("/buckets/" + nodeIdOpt.get()));
var roomIdOpt = event.getRouteParameters().get("roomId");
if (roomIdOpt.isEmpty()) {
getUI().ifPresent(ui -> ui.navigate("/rooms/" + nodeIdOpt.get()));
return;
}
var nodeId = nodeIdOpt.get();
var bucketId = bucketIdOpt.get();
var roomId = roomIdOpt.get();
add(new NavPath(new NavPath.Point("Nodes", "/nodes"),
new NavPath.Point(nodeId, "/nodes/" + nodeId),
new NavPath.Point(bucketId, "/nodes/" + nodeId + "/buckets/" + bucketId)));
new NavPath.Point(roomId, "/nodes/" + nodeId + "/rooms/" + roomId)));
var nodeOpt = nodeRepository.findNode(nodeId);
var nodeOpt = nodeRepository.find(nodeId);
if (nodeOpt.isEmpty()) {
add(new H2("Error 404"));
add(new Paragraph("Node not found!"));
@ -75,75 +76,75 @@ public class BucketDetailsPage extends VerticalLayout implements BeforeEnterObse
}
node = nodeOpt.get();
var bucketOpt = bucketRepository.find(node, bucketId);
var bucketOpt = roomRepository.find(node, roomId);
if (bucketOpt.isEmpty()) {
add(new H2("Error 404"));
add(new Paragraph("Bucket not found!"));
Notification.show("Bucket '" + nodeId + "' does not exist", 3000, Notification.Position.TOP_END)
add(new Paragraph("Room not found!"));
Notification.show("Room '" + nodeId + "' does not exist", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
bucket = bucketOpt.get();
room = bucketOpt.get();
init();
}
private void init() {
add(new H2("Bucket details"));
printBucketDetails();
add(new H2("Room details"));
printRoomDetails();
add(new Hr());
add(addUsers = new AddUsers(bucket));
add(addUsers = new AddUsers(room));
add(new Hr());
add(new H2("Users"));
add(userList = new UserList(bucket, userRepository.all(bucket)));
add(userList = new UserList(room, userRepository.all(room)));
}
private void updateBucketInfo() {
bucketInfo.removeAll();
bucketInfo.add(new Html("<span>Node identifier: <b>" + bucket.getNodeIdentifier() + "</b></span>"));
bucketInfo.add(new Html("<span>Bucket identifier: <b>" + bucket.getIdentifier() + "</b></span>"));
bucketInfo.add(new Html("<span>Slots: <b>" + (bucket.getSlots().isUnlimited()? "Unlimited" : bucket.getSlots().slots()) + "</b></span>"));
bucketInfo.add(new Html("<span>Locked: <b>" + (bucket.isLocked()? "Yes" : "No") + "</b></span>"));
private void updateRoomInfo() {
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>Locked: <b>" + (room.isLocked()? "Yes" : "No") + "</b></span>"));
}
private void printBucketDetails() {
add(bucketInfo = new VerticalLayout());
bucketInfo.setPadding(false);
private void printRoomDetails() {
add(roomInfo = new VerticalLayout());
roomInfo.setPadding(false);
updateBucketInfo();
add(lockBucketButton = new Button("", event -> changeBucketLockedState()));
setLockBucketButtonState();
updateRoomInfo();
add(lockRoomButton = new Button("", event -> changeBucketLockedState()));
setLockRoomButtonState();
var payload = new TextArea("Payload(" + bucket.getPayload().length() + ")");
payload.setValue(bucket.getPayload());
var payload = new TextArea("Payload(" + room.getPayload().length() + ")");
payload.setValue(room.getPayload());
payload.setReadOnly(true);
payload.setMinWidth(50, Unit.REM);
add(payload);
}
private void setLockBucketButtonState() {
if (bucket.isLocked()) {
lockBucketButton.setText("Unlock");
lockBucketButton.setPrefixComponent(new Icon(VaadinIcon.UNLOCK));
private void setLockRoomButtonState() {
if (room.isLocked()) {
lockRoomButton.setText("Unlock");
lockRoomButton.setPrefixComponent(new Icon(VaadinIcon.UNLOCK));
} else {
lockBucketButton.setText("Lock");
lockBucketButton.setPrefixComponent(new Icon(VaadinIcon.LOCK));
lockRoomButton.setText("Lock");
lockRoomButton.setPrefixComponent(new Icon(VaadinIcon.LOCK));
}
}
private void changeBucketLockedState() {
var newValue = !bucket.isLocked();
var newValue = !room.isLocked();
try {
bucketRepository.lock(bucket, newValue);
roomRepository.lock(room, newValue);
} catch (Error error) {
Notification.show(error.getMessage(), 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
bucket.setLocked(newValue);
setLockBucketButtonState();
updateBucketInfo();
room.setLocked(newValue);
setLockRoomButtonState();
updateRoomInfo();
Notification.show("Success", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);

View File

@ -1,23 +0,0 @@
package ru.dragonestia.picker.cp.repository;
import ru.dragonestia.picker.cp.model.Bucket;
import ru.dragonestia.picker.cp.model.Node;
import ru.dragonestia.picker.cp.model.dto.BucketDTO;
import java.util.List;
import java.util.Optional;
public interface BucketRepository {
List<BucketDTO> all(Node node);
void register(Bucket bucket);
void remove(Bucket bucket);
void remove(Node node, BucketDTO bucket);
Optional<Bucket> find(Node node, String identifier);
void lock(Bucket bucket, boolean value);
}

View File

@ -7,11 +7,11 @@ import java.util.Optional;
public interface NodeRepository {
void registerNode(Node node);
void register(Node node);
List<Node> getNodes();
List<Node> all();
Optional<Node> findNode(String identifier);
Optional<Node> find(String nodeId);
void removeNode(String identifier);
void remove(String nodeId);
}

View File

@ -0,0 +1,23 @@
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);
}

View File

@ -1,6 +1,6 @@
package ru.dragonestia.picker.cp.repository;
import ru.dragonestia.picker.cp.model.Bucket;
import ru.dragonestia.picker.cp.model.Room;
import ru.dragonestia.picker.cp.model.User;
import java.util.Collection;
@ -8,9 +8,9 @@ import java.util.List;
public interface UserRepository {
void linkWithBucket(Bucket bucket, Collection<User> users);
void linkWithRoom(Room room, Collection<User> users);
void unlinkFromBucket(Bucket bucket, Collection<User> users);
void unlinkFromRoom(Room room, Collection<User> users);
List<User> all(Bucket bucket);
List<User> all(Room room);
}

View File

@ -1,100 +0,0 @@
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.Bucket;
import ru.dragonestia.picker.cp.model.Node;
import ru.dragonestia.picker.cp.model.dto.BucketDTO;
import ru.dragonestia.picker.cp.repository.BucketRepository;
import ru.dragonestia.picker.cp.repository.impl.response.BucketInfoResponse;
import ru.dragonestia.picker.cp.repository.impl.response.BucketListResponse;
import ru.dragonestia.picker.cp.repository.impl.response.BucketRegisterResponse;
import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@Log4j2
@RequiredArgsConstructor
@SpringComponent
public class BucketRepositoryImpl implements BucketRepository {
private final RestUtil rest;
@Override
public List<BucketDTO> all(Node node) {
var entity = rest.getEntity(URI.create("/nodes/" + node.identifier() + "/buckets"),
BucketListResponse.class);
if (entity.getStatusCode().value() == 404) {
throw new Error("Node with identifier '" + node.identifier() + "' does not exists'");
}
if (!entity.hasBody()) {
throw new Error("Bucket list did not present");
}
return Objects.requireNonNull(entity.getBody()).buckets();
}
@Override
public void register(Bucket bucket) {
try {
var response = rest.post(URI.create("/nodes/" + bucket.getNodeIdentifier() + "/buckets"),
BucketRegisterResponse.class,
params -> {
params.put("identifier", bucket.getIdentifier());
params.put("slots", Integer.toString(bucket.getSlots().slots()));
params.put("payload", bucket.getPayload());
params.put("locked", Boolean.toString(bucket.isLocked()));
});
if (response.success()) return;
throw new Error(response.message());
} catch (HttpClientErrorException ex) {
var response = ex.getResponseBodyAs(BucketRegisterResponse.class);
if (response != null) {
throw new Error(response.message());
}
log.throwing(ex);
throw new Error("Internal error. Check logs");
}
}
@Override
public void remove(Bucket bucket) {
rest.delete(URI.create("/nodes/" + bucket.getNodeIdentifier() + "/buckets/" + bucket.getIdentifier()), params -> {});
}
@Override
public void remove(Node node, BucketDTO bucket) {
rest.delete(URI.create("/nodes/" + node.identifier() + "/buckets/" + bucket.identifier()), params -> {});
}
@Override
public Optional<Bucket> find(Node node, String identifier) {
try {
var response = rest.get(URI.create("/nodes/" + node.identifier() + "/buckets/" + identifier), BucketInfoResponse.class, map -> {});
return Optional.of(response.bucket());
} catch (Exception ex) {
return Optional.empty();
}
}
@Override
public void lock(Bucket bucket, boolean value) {
try {
rest.post(URI.create(bucket.createApiURI() + "/lock"), Boolean.class, params -> {
params.put("state", Boolean.toString(value));
});
} catch (Exception ex) {
log.throwing(ex);
throw new Error("Error when changing bucket locked state");
}
}
}

View File

@ -21,14 +21,14 @@ public class NodeRepositoryImpl implements NodeRepository {
private final RestUtil rest;
@Override
public void registerNode(Node node) {
public void register(Node node) {
NodeRegisterResponse response;
try {
response = rest.post(URI.create("nodes"),
NodeRegisterResponse.class,
params -> {
params.put("identifier", node.identifier());
params.put("method", node.method().name());
params.put("nodeId", node.id());
params.put("method", node.mode().name());
});
} catch (Exception ex) {
throw new RuntimeException("Internal error", ex);
@ -40,14 +40,14 @@ public class NodeRepositoryImpl implements NodeRepository {
}
@Override
public List<Node> getNodes() {
public List<Node> all() {
return rest.get(URI.create("nodes"), NodeListResponse.class).nodes();
}
@Override
public Optional<Node> findNode(String identifier) {
public Optional<Node> find(String nodeId) {
try {
var response = rest.get(URI.create("nodes/" + identifier), NodeDetailsResponse.class);
var response = rest.get(URI.create("nodes/" + nodeId), NodeDetailsResponse.class);
return Optional.of(response.node());
} catch (Exception ex) {
return Optional.empty();
@ -55,7 +55,7 @@ public class NodeRepositoryImpl implements NodeRepository {
}
@Override
public void removeNode(String identifier) {
rest.delete(URI.create("nodes/" + identifier), params -> {});
public void remove(String nodeId) {
rest.delete(URI.create("nodes/" + nodeId), params -> {});
}
}

View File

@ -0,0 +1,100 @@
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 java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@Log4j2
@RequiredArgsConstructor
@SpringComponent
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();
}
@Override
public void register(Room room) {
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 -> {});
return Optional.of(response.room());
} catch (Exception 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");
}
}
}

View File

@ -3,10 +3,10 @@ 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.Bucket;
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.BucketUserListResponse;
import ru.dragonestia.picker.cp.repository.impl.response.RoomUserListResponse;
import java.net.URI;
import java.util.Collection;
@ -20,20 +20,20 @@ public class UserRepositoryImpl implements UserRepository {
private final RestUtil rest;
@Override
public void linkWithBucket(Bucket bucket, Collection<User> users) {
public void linkWithRoom(Room room, Collection<User> users) {
// TODO
}
@Override
public void unlinkFromBucket(Bucket bucket, Collection<User> users) {
public void unlinkFromRoom(Room room, Collection<User> users) {
// TODO
}
@Override
public List<User> all(Bucket bucket) {
public List<User> all(Room room) {
try {
var response = rest.get(URI.create("/nodes/%s/buckets/%s/users".formatted(bucket.getNodeIdentifier(), bucket.getIdentifier())),
BucketUserListResponse.class,
var response = rest.get(URI.create("/nodes/%s/rooms/%s/users".formatted(room.getNodeId(), room.getId())),
RoomUserListResponse.class,
params -> {});
return response.users();

View File

@ -1,5 +0,0 @@
package ru.dragonestia.picker.cp.repository.impl.response;
import ru.dragonestia.picker.cp.model.Bucket;
public record BucketInfoResponse(Bucket bucket) {}

View File

@ -1,7 +0,0 @@
package ru.dragonestia.picker.cp.repository.impl.response;
import ru.dragonestia.picker.cp.model.dto.BucketDTO;
import java.util.List;
public record BucketListResponse(String node, List<BucketDTO> buckets) {}

View File

@ -1,3 +0,0 @@
package ru.dragonestia.picker.cp.repository.impl.response;
public record BucketRegisterResponse(boolean success, String message) {}

View File

@ -0,0 +1,5 @@
package ru.dragonestia.picker.cp.repository.impl.response;
import ru.dragonestia.picker.cp.model.Room;
public record RoomInfoResponse(Room room) {}

View File

@ -0,0 +1,7 @@
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) {}

View File

@ -0,0 +1,3 @@
package ru.dragonestia.picker.cp.repository.impl.response;
public record RoomRegisterResponse(boolean success, String message) {}

View File

@ -4,4 +4,4 @@ import ru.dragonestia.picker.cp.model.User;
import java.util.List;
public record BucketUserListResponse(int slots, int usedSlots, List<User> users) {}
public record RoomUserListResponse(int slots, int usedSlots, List<User> users) {}