Implemented registration and getting buckets

This commit is contained in:
Andrey Terentev 2023-11-30 21:22:29 +07:00
parent d6d733e8f5
commit 96299218ec
11 changed files with 84 additions and 47 deletions

View File

@ -15,19 +15,21 @@ import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import lombok.Setter;
import ru.dragonestia.loadbalancer.web.model.Bucket;
import ru.dragonestia.loadbalancer.web.repository.BucketRepository;
import java.util.List;
import java.util.function.Consumer;
public class BucketList extends VerticalLayout {
private final String nodeIdentifier;
private final Grid<BucketRepository.BucketInfo> bucketsGrid;
private final Grid<Bucket> bucketsGrid;
private final TextField searchField;
private List<BucketRepository.BucketInfo> cachedBuckets;
private List<Bucket> cachedBuckets;
@Setter private Consumer<Bucket> removeMethod;
public BucketList(String nodeIdentifier, List<BucketRepository.BucketInfo> buckets) {
public BucketList(String nodeIdentifier, List<Bucket> buckets) {
this.nodeIdentifier = nodeIdentifier;
cachedBuckets = buckets;
@ -51,20 +53,20 @@ public class BucketList extends VerticalLayout {
var temp = input.trim();
bucketsGrid.setItems(cachedBuckets.stream()
.filter(bucket -> bucket.identifier().startsWith(temp))
.filter(bucket -> bucket.getIdentifier().startsWith(temp))
.toList());
}
private Grid<BucketRepository.BucketInfo> createGrid() {
var grid = new Grid<>(BucketRepository.BucketInfo.class, false);
grid.addColumn(BucketRepository.BucketInfo::identifier).setHeader("Identifier");
private Grid<Bucket> createGrid() {
var grid = new Grid<>(Bucket.class, false);
grid.addColumn(Bucket::getIdentifier).setHeader("Identifier");
grid.addComponentColumn(bucket -> {
var result = new Span();
if (bucket.slots() == -1) {
if (bucket.getSlots().isUnlimited()) {
result.setText("Unlimited");
result.getElement().getThemeList().add("badge contrast");
} else {
result.setText(Integer.toString(bucket.slots()));
result.setText(Integer.toString(bucket.getSlots().slots()));
}
return result;
}).setHeader("Slots").setTextAlign(ColumnTextAlign.CENTER);
@ -72,7 +74,7 @@ public class BucketList extends VerticalLayout {
return grid;
}
private HorizontalLayout createManageButtons(BucketRepository.BucketInfo bucket) {
private HorizontalLayout createManageButtons(Bucket bucket) {
var layout = new HorizontalLayout();
{
@ -92,16 +94,16 @@ public class BucketList extends VerticalLayout {
return layout;
}
private void clickDetailsButton(BucketRepository.BucketInfo bucket) {
private void clickDetailsButton(Bucket bucket) {
getUI().ifPresent(ui -> {
ui.navigate("/nodes/" + nodeIdentifier +
"/buckets/" + bucket.identifier());
"/buckets/" + bucket.getIdentifier());
});
}
private void clickRemoveButton(BucketRepository.BucketInfo bucket) {
private void clickRemoveButton(Bucket 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.getIdentifier() + "' to field below and confirm."));
var inputField = new TextField();
dialog.add(inputField);
@ -110,14 +112,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.getIdentifier().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.getIdentifier() + "' was successfully removed!", 3000, Notification.Position.TOP_END)
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
dialog.close();
});
@ -134,12 +136,14 @@ public class BucketList extends VerticalLayout {
dialog.open();
}
public void update(List<BucketRepository.BucketInfo> buckets) {
public void update(List<Bucket> buckets) {
cachedBuckets = buckets;
applySearch(searchField.getValue());
}
private void removeBucket(BucketRepository.BucketInfo bucket) {
// TODO: send remove request and getting list
private void removeBucket(Bucket bucket) {
if (removeMethod != null) {
removeMethod.accept(bucket);
}
}
}

View File

@ -1,12 +1,13 @@
package ru.dragonestia.loadbalancer.web.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import ru.dragonestia.loadbalancer.web.model.type.SlotLimit;
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Bucket {
private final String identifier;
@ -15,8 +16,21 @@ public class Bucket {
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;
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);
return new Bucket(identifier, node.identifier(), limit, payload, false);
}
public void setLocked(boolean value) {
@ -26,7 +40,7 @@ public class Bucket {
public boolean isAvailable(int usedSlots, int requiredSlots) {
if (locked) return false;
if (slots.isUnlimited()) return true;
return slots.getSlots() >= usedSlots + requiredSlots;
return slots.slots() >= usedSlots + requiredSlots;
}
@Override

View File

@ -1,18 +1,11 @@
package ru.dragonestia.loadbalancer.web.model.type;
import lombok.Getter;
import java.beans.Transient;
@Getter
public class SlotLimit {
public record SlotLimit(int slots) {
private final static int UNLIMITED_VALUE = -1;
private final int slots;
private SlotLimit(int slots) {
this.slots = slots;
}
public static SlotLimit unlimited() {
return new SlotLimit(UNLIMITED_VALUE);
}
@ -21,6 +14,7 @@ public class SlotLimit {
return new SlotLimit(slots);
}
@Transient
public boolean isUnlimited() {
return slots == UNLIMITED_VALUE;
}

View File

@ -50,6 +50,6 @@ public class BucketsPage extends VerticalLayout implements BeforeEnterObserver {
private void printBucketDetails() {
add(new Html("<span>Node identifier: <b>" + bucket.getNodeIdentifier() + "</b></span>"));
add(new Html("<span>Bucket identifier: <b>" + bucket.getIdentifier() + "</b></span>"));
add(new Html("<span>Slots: <b>" + (bucket.getSlots().isUnlimited()? "Unlimited" : bucket.getSlots().getSlots()) + "</b></span>"));
add(new Html("<span>Slots: <b>" + (bucket.getSlots().isUnlimited()? "Unlimited" : bucket.getSlots().slots()) + "</b></span>"));
}
}

View File

@ -66,7 +66,7 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv
initComponents(node, bucketRepository.all(node));
}
private void initComponents(Node node, List<BucketRepository.BucketInfo> buckets) {
private void initComponents(Node node, List<Bucket> buckets) {
printNodeDetails(node);
add(new Hr());
add(registerBucket = new RegisterBucket(node, (bucket) -> {
@ -81,6 +81,10 @@ public class NodeDetailsPage extends VerticalLayout implements BeforeEnterObserv
}));
add(new Hr());
add(bucketList = new BucketList(node.identifier(), buckets));
bucketList.setRemoveMethod(bucket -> {
bucketRepository.remove(bucket);
bucketList.update(bucketRepository.all(node));
});
}
private void printNodeDetails(Node node) {

View File

@ -7,9 +7,9 @@ import java.util.List;
public interface BucketRepository {
List<BucketInfo> all(Node node);
List<Bucket> all(Node node);
void register(Bucket bucket);
record BucketInfo(String identifier, int slots) {}
void remove(Bucket bucket);
}

View File

@ -22,7 +22,7 @@ public class BucketRepositoryImpl implements BucketRepository {
private final RestUtil rest;
@Override
public List<BucketInfo> all(Node node) {
public List<Bucket> all(Node node) {
var entity = rest.getEntity(URI.create("/nodes/" + node.identifier() + "/buckets"),
BucketListResponse.class);
@ -44,12 +44,13 @@ public class BucketRepositoryImpl implements BucketRepository {
BucketRegisterResponse.class,
params -> {
params.put("identifier", bucket.getIdentifier());
params.put("slots", Integer.toString(bucket.getSlots().getSlots()));
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);
@ -61,4 +62,9 @@ public class BucketRepositoryImpl implements BucketRepository {
throw new Error("Internal error. Check logs");
}
}
@Override
public void remove(Bucket bucket) {
rest.delete(URI.create("/nodes/" + bucket.getNodeIdentifier() + "/buckets/" + bucket.getIdentifier()), params -> {});
}
}

View File

@ -1,7 +1,7 @@
package ru.dragonestia.loadbalancer.web.repository.impl.response;
import ru.dragonestia.loadbalancer.web.repository.BucketRepository;
import ru.dragonestia.loadbalancer.web.model.Bucket;
import java.util.List;
public record BucketListResponse(String node, List<BucketRepository.BucketInfo> buckets) {}
public record BucketListResponse(String node, List<Bucket> buckets) {}

View File

@ -11,6 +11,7 @@ import ru.dragonestia.loadbalancer.model.Bucket;
import ru.dragonestia.loadbalancer.model.type.SlotLimit;
import ru.dragonestia.loadbalancer.service.BucketService;
import ru.dragonestia.loadbalancer.service.NodeService;
import ru.dragonestia.loadbalancer.util.NamingValidator;
@Log4j2
@RestController
@ -25,9 +26,7 @@ public class BucketController {
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.BucketInfo(bucket.getIdentifier(), bucket.getSlots().getSlots()))
.toList()
bucketService.allBuckets(node).stream().toList()
))).orElseGet(() -> ResponseEntity.notFound().build());
}
@ -56,4 +55,18 @@ public class BucketController {
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();
}
}

View File

@ -1,8 +1,7 @@
package ru.dragonestia.loadbalancer.controller.response;
import ru.dragonestia.loadbalancer.model.Bucket;
import java.util.List;
public record BucketListResponse(String node, List<BucketInfo> buckets) {
public record BucketInfo(String identifier, int slots) {}
}
public record BucketListResponse(String node, List<Bucket> buckets) {}

View File

@ -2,6 +2,8 @@ package ru.dragonestia.loadbalancer.model.type;
import lombok.Getter;
import java.beans.Transient;
@Getter
public class SlotLimit {
@ -21,6 +23,7 @@ public class SlotLimit {
return new SlotLimit(slots);
}
@Transient
public boolean isUnlimited() {
return slots == UNLIMITED_VALUE;
}