From ea5ff314ed63c66d17ccc1b13db0413fbb7959d2 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Mon, 27 Nov 2023 01:24:22 +0700 Subject: [PATCH] Implemented getting buckets --- .../web/component/BucketList.java | 41 ++++++++++--------- .../web/page/NodeDetailsPage.java | 22 +++++----- .../web/repository/BucketRepository.java | 12 ++++++ .../repository/impl/BucketRepositoryImpl.java | 37 +++++++++++++++++ .../web/repository/impl/RestUtil.java | 6 +++ .../impl/response/BucketListResponse.java | 7 ++++ .../loadbalancer/config/TestConfig.java | 24 +++++++++-- .../controller/BucketController.java | 30 ++++++++++++++ .../controller/UserController.java | 29 +++++++++++++ .../response/BucketListResponse.java | 8 ++++ 10 files changed, 182 insertions(+), 34 deletions(-) create mode 100644 LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/BucketRepository.java create mode 100644 LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/BucketRepositoryImpl.java create mode 100644 LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/response/BucketListResponse.java create mode 100644 src/main/java/ru/dragonestia/loadbalancer/controller/BucketController.java create mode 100644 src/main/java/ru/dragonestia/loadbalancer/controller/UserController.java create mode 100644 src/main/java/ru/dragonestia/loadbalancer/controller/response/BucketListResponse.java diff --git a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/component/BucketList.java b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/component/BucketList.java index ea78d20..cbbbe2f 100644 --- a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/component/BucketList.java +++ b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/component/BucketList.java @@ -16,16 +16,19 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.textfield.TextField; import ru.dragonestia.loadbalancer.web.model.Bucket; +import ru.dragonestia.loadbalancer.web.repository.BucketRepository; import java.util.List; public class BucketList extends VerticalLayout { - private final Grid bucketsGrid; + private final String nodeIdentifier; + private final Grid bucketsGrid; private final TextField searchField; - private List cachedBuckets; + private List cachedBuckets; - public BucketList(List buckets) { + public BucketList(String nodeIdentifier, List buckets) { + this.nodeIdentifier = nodeIdentifier; cachedBuckets = buckets; add(new H2("Buckets")); @@ -48,20 +51,20 @@ public class BucketList extends VerticalLayout { var temp = input.trim(); bucketsGrid.setItems(cachedBuckets.stream() - .filter(bucket -> bucket.getIdentifier().startsWith(temp)) + .filter(bucket -> bucket.identifier().startsWith(temp)) .toList()); } - private Grid createGrid() { - var grid = new Grid<>(Bucket.class, false); - grid.addColumn(Bucket::getIdentifier).setHeader("Identifier"); + private Grid createGrid() { + var grid = new Grid<>(BucketRepository.BucketInfo.class, false); + grid.addColumn(BucketRepository.BucketInfo::identifier).setHeader("Identifier"); grid.addComponentColumn(bucket -> { var result = new Span(); - if (bucket.getSlots().isUnlimited()) { + if (bucket.slots() == -1) { result.setText("Unlimited"); result.getElement().getThemeList().add("badge contrast"); } else { - result.setText(Integer.toString(bucket.getSlots().getSlots())); + result.setText(Integer.toString(bucket.slots())); } return result; }).setHeader("Slots").setTextAlign(ColumnTextAlign.CENTER); @@ -69,7 +72,7 @@ public class BucketList extends VerticalLayout { return grid; } - private HorizontalLayout createManageButtons(Bucket bucket) { + private HorizontalLayout createManageButtons(BucketRepository.BucketInfo bucket) { var layout = new HorizontalLayout(); { @@ -89,16 +92,16 @@ public class BucketList extends VerticalLayout { return layout; } - private void clickDetailsButton(Bucket bucket) { + private void clickDetailsButton(BucketRepository.BucketInfo bucket) { getUI().ifPresent(ui -> { - ui.navigate("/nodes/" + bucket.getNodeIdentifier() + - "/buckets/" + bucket.getIdentifier()); + ui.navigate("/nodes/" + nodeIdentifier + + "/buckets/" + bucket.identifier()); }); } - private void clickRemoveButton(Bucket bucket) { + private void clickRemoveButton(BucketRepository.BucketInfo bucket) { var dialog = new Dialog("Confirm bucket deletion"); - dialog.add(new Paragraph("Confirm that you want to delete bucket. Enter '" + bucket.getIdentifier() + "' to field below and confirm.")); + dialog.add(new Paragraph("Confirm that you want to delete bucket. Enter '" + bucket.identifier() + "' to field below and confirm.")); var inputField = new TextField(); dialog.add(inputField); @@ -107,14 +110,14 @@ public class BucketList extends VerticalLayout { var button = new Button("Confirm"); button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR); button.addClickListener(event -> { - if (!bucket.getIdentifier().equals(inputField.getValue())) { + if (!bucket.identifier().equals(inputField.getValue())) { Notification.show("Invalid input", 3000, Notification.Position.TOP_END) .addThemeVariants(NotificationVariant.LUMO_ERROR); return; } removeBucket(bucket); - Notification.show("Bucket '" + bucket.getIdentifier() + "' was successfully removed!", 3000, Notification.Position.TOP_END) + Notification.show("Bucket '" + bucket.identifier() + "' was successfully removed!", 3000, Notification.Position.TOP_END) .addThemeVariants(NotificationVariant.LUMO_SUCCESS); dialog.close(); }); @@ -131,12 +134,12 @@ public class BucketList extends VerticalLayout { dialog.open(); } - public void update(List buckets) { + public void update(List buckets) { cachedBuckets = buckets; applySearch(searchField.getValue()); } - private void removeBucket(Bucket bucket) { + private void removeBucket(BucketRepository.BucketInfo bucket) { // TODO: send remove request and getting list } } diff --git a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/page/NodeDetailsPage.java b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/page/NodeDetailsPage.java index 2478da3..d43887e 100644 --- a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/page/NodeDetailsPage.java +++ b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/page/NodeDetailsPage.java @@ -20,6 +20,7 @@ import ru.dragonestia.loadbalancer.web.model.Bucket; import ru.dragonestia.loadbalancer.web.model.Node; import ru.dragonestia.loadbalancer.web.model.type.LoadBalancingMethod; import ru.dragonestia.loadbalancer.web.model.type.SlotLimit; +import ru.dragonestia.loadbalancer.web.repository.BucketRepository; import ru.dragonestia.loadbalancer.web.repository.NodeRepository; import java.util.List; @@ -30,12 +31,16 @@ import java.util.List; public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserver { private final NodeRepository nodeRepository; + private final BucketRepository bucketRepository; private Node node; private RegisterBucket registerBucket; private BucketList bucketList; - public NodeDetailsPage(@Autowired NodeRepository nodeRepository) { + public NodeDetailsPage(@Autowired NodeRepository nodeRepository, + @Autowired BucketRepository bucketRepository) { + this.nodeRepository = nodeRepository; + this.bucketRepository = bucketRepository; } @Override @@ -56,19 +61,12 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv .addThemeVariants(NotificationVariant.LUMO_ERROR); return; } + node = nodeOpt.get(); - node = nodeOpt.get(); // TODO: getting node - - // TODO: getting buckets - initComponents(node, List.of( - Bucket.create("test-1", node, SlotLimit.unlimited(), "Hello world!"), - Bucket.create("test-2", node, SlotLimit.of(12), "Hello world!"), - Bucket.create("test-3", node, SlotLimit.unlimited(), "Hello world!"), - Bucket.create("test-4", node, SlotLimit.of(32), "Hello world!"), - Bucket.create("test-5", node, SlotLimit.of(54), "Hello world!"))); + initComponents(node, bucketRepository.all(node)); } - private void initComponents(Node node, List buckets) { + private void initComponents(Node node, List buckets) { printNodeDetails(node); add(new Hr()); add(registerBucket = new RegisterBucket(node, (bucket) -> { @@ -76,7 +74,7 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv return new RegisterBucket.Response(false, ""); })); add(new Hr()); - add(bucketList = new BucketList(buckets)); + add(bucketList = new BucketList(node.identifier(), buckets)); } private void printNodeDetails(Node node) { diff --git a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/BucketRepository.java b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/BucketRepository.java new file mode 100644 index 0000000..93535de --- /dev/null +++ b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/BucketRepository.java @@ -0,0 +1,12 @@ +package ru.dragonestia.loadbalancer.web.repository; + +import ru.dragonestia.loadbalancer.web.model.Node; + +import java.util.List; + +public interface BucketRepository { + + List all(Node node); + + record BucketInfo(String identifier, int slots) {} +} diff --git a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/BucketRepositoryImpl.java b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/BucketRepositoryImpl.java new file mode 100644 index 0000000..4807271 --- /dev/null +++ b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/BucketRepositoryImpl.java @@ -0,0 +1,37 @@ +package ru.dragonestia.loadbalancer.web.repository.impl; + +import com.vaadin.flow.spring.annotation.SpringComponent; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import ru.dragonestia.loadbalancer.web.model.Bucket; +import ru.dragonestia.loadbalancer.web.model.Node; +import ru.dragonestia.loadbalancer.web.repository.BucketRepository; +import ru.dragonestia.loadbalancer.web.repository.impl.response.BucketListResponse; + +import java.net.URI; +import java.util.List; +import java.util.Objects; + +@Log4j2 +@RequiredArgsConstructor +@SpringComponent +public class BucketRepositoryImpl implements BucketRepository { + + private final RestUtil rest; + + @Override + public List 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(); + } +} diff --git a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/RestUtil.java b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/RestUtil.java index 18ad604..eca9789 100644 --- a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/RestUtil.java +++ b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/RestUtil.java @@ -1,6 +1,7 @@ package ru.dragonestia.loadbalancer.web.repository.impl; import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @@ -24,6 +25,11 @@ public class RestUtil { return Objects.requireNonNull(template.getForObject(serverUrl.resolve(uri), responseType)); } + public ResponseEntity getEntity(URI uri, Class responseType) { + var template = restTemplate.get(); + return template.getForEntity(serverUrl.resolve(uri), responseType); + } + public T get(URI uri, Class responseType, Consumer> paramsConsumer) { var params = new HashMap(); paramsConsumer.accept(params); diff --git a/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/response/BucketListResponse.java b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/response/BucketListResponse.java new file mode 100644 index 0000000..c3959df --- /dev/null +++ b/LoadBalancerWeb/src/main/java/ru/dragonestia/loadbalancer/web/repository/impl/response/BucketListResponse.java @@ -0,0 +1,7 @@ +package ru.dragonestia.loadbalancer.web.repository.impl.response; + +import ru.dragonestia.loadbalancer.web.repository.BucketRepository; + +import java.util.List; + +public record BucketListResponse(String node, List buckets) {} diff --git a/src/main/java/ru/dragonestia/loadbalancer/config/TestConfig.java b/src/main/java/ru/dragonestia/loadbalancer/config/TestConfig.java index 79a0027..45bc16c 100644 --- a/src/main/java/ru/dragonestia/loadbalancer/config/TestConfig.java +++ b/src/main/java/ru/dragonestia/loadbalancer/config/TestConfig.java @@ -8,16 +8,22 @@ import org.springframework.lang.NonNull; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import ru.dragonestia.loadbalancer.interceptor.DebugInterceptor; +import ru.dragonestia.loadbalancer.model.Bucket; import ru.dragonestia.loadbalancer.model.Node; import ru.dragonestia.loadbalancer.model.type.LoadBalancingMethod; +import ru.dragonestia.loadbalancer.model.type.SlotLimit; +import ru.dragonestia.loadbalancer.repository.BucketRepository; import ru.dragonestia.loadbalancer.repository.NodeRepository; +import java.util.UUID; + @Profile("test") @Configuration @RequiredArgsConstructor public class TestConfig implements WebMvcConfigurer { private final NodeRepository nodeRepository; + private final BucketRepository bucketRepository; @Override public void addInterceptors(@NonNull InterceptorRegistry registry) { @@ -26,8 +32,20 @@ public class TestConfig implements WebMvcConfigurer { @Bean void createNodes() { - nodeRepository.createNode(new Node("game-servers", LoadBalancingMethod.ROUND_ROBIN)); - nodeRepository.createNode(new Node("game-lobbies", LoadBalancingMethod.LEAST_PICKED)); - nodeRepository.createNode(new Node("hub", LoadBalancingMethod.SEQUENTIAL_FILLING)); + createNodeWithContent(new Node("game-servers", LoadBalancingMethod.ROUND_ROBIN)); + createNodeWithContent(new Node("game-lobbies", LoadBalancingMethod.LEAST_PICKED)); + createNodeWithContent(new Node("hub", LoadBalancingMethod.SEQUENTIAL_FILLING)); + } + + private void createNodeWithContent(Node node) { + nodeRepository.createNode(node); + + for (int i = 1; i <= 5; i++) { + bucketRepository.createBucket(Bucket.create("test-" + i, node, SlotLimit.of(5 * i), "Some payload")); + } + + for (int i = 0; i < 5; i++) { + bucketRepository.createBucket(Bucket.create(UUID.randomUUID().toString(), node, SlotLimit.unlimited(), "Some payload")); + } } } diff --git a/src/main/java/ru/dragonestia/loadbalancer/controller/BucketController.java b/src/main/java/ru/dragonestia/loadbalancer/controller/BucketController.java new file mode 100644 index 0000000..b79a55a --- /dev/null +++ b/src/main/java/ru/dragonestia/loadbalancer/controller/BucketController.java @@ -0,0 +1,30 @@ +package ru.dragonestia.loadbalancer.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.dragonestia.loadbalancer.controller.response.BucketListResponse; +import ru.dragonestia.loadbalancer.service.BucketService; +import ru.dragonestia.loadbalancer.service.NodeService; + +@RestController +@RequestMapping("/nodes/{nodeIdentifier}/buckets") +@RequiredArgsConstructor +public class BucketController { + + private final NodeService nodeService; + private final BucketService bucketService; + + @GetMapping + ResponseEntity 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.BucketInfo(bucket.getIdentifier(), bucket.getSlots().getSlots())) + .toList() + ))).orElseGet(() -> ResponseEntity.notFound().build()); + } +} diff --git a/src/main/java/ru/dragonestia/loadbalancer/controller/UserController.java b/src/main/java/ru/dragonestia/loadbalancer/controller/UserController.java new file mode 100644 index 0000000..ed738b6 --- /dev/null +++ b/src/main/java/ru/dragonestia/loadbalancer/controller/UserController.java @@ -0,0 +1,29 @@ +package ru.dragonestia.loadbalancer.controller; + +import org.springframework.web.bind.annotation.*; +import ru.dragonestia.loadbalancer.model.Bucket; + +import java.util.Collection; + +@RestController +@RequestMapping("/users") +public class UserController { + + @GetMapping + Collection linkedBucketsForUser(@RequestParam(name = "user") String userIdentifier) { + return null; + } + + @PostMapping + String linkUserWithBucket(@RequestParam(name = "user") String userIdentifier, + @RequestParam(name = "bucket") String bucketIdentifier, + @RequestParam(name = "force") boolean force) { + return null; + } + + @DeleteMapping + String unlinkUsersForBucket(@RequestParam(name = "users") String userIdentifiers, + @RequestParam(name = "bucket") String bucketIdentifier) { + return null; + } +} diff --git a/src/main/java/ru/dragonestia/loadbalancer/controller/response/BucketListResponse.java b/src/main/java/ru/dragonestia/loadbalancer/controller/response/BucketListResponse.java new file mode 100644 index 0000000..4f0481d --- /dev/null +++ b/src/main/java/ru/dragonestia/loadbalancer/controller/response/BucketListResponse.java @@ -0,0 +1,8 @@ +package ru.dragonestia.loadbalancer.controller.response; + +import java.util.List; + +public record BucketListResponse(String node, List buckets) { + + public record BucketInfo(String identifier, int slots) {} +}