Refactored Node response object

This commit is contained in:
Andrey Terentev 2024-03-12 02:22:28 +07:00 committed by Andrey Terentev
parent 31ff4cf95b
commit 2d0cdca3e7
22 changed files with 209 additions and 119 deletions

View File

@ -0,0 +1,22 @@
package ru.dragonestia.picker.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
ObjectMapper objectMapper() {
var mapper = new ObjectMapper();
mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
return mapper;
}
}

View File

@ -10,7 +10,7 @@ import org.springframework.context.annotation.Profile;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.interceptor.DebugInterceptor; import ru.dragonestia.picker.interceptor.DebugInterceptor;
import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.Node;
@ -42,9 +42,9 @@ public class TestConfig implements WebMvcConfigurer {
@Bean @Bean
void createNodes() { void createNodes() {
createNodeWithContent(new Node("game-servers", PickingMode.ROUND_ROBIN, false)); createNodeWithContent(new Node("game-servers", PickingMethod.ROUND_ROBIN, false));
createNodeWithContent(new Node("game-lobbies", PickingMode.LEAST_PICKED, false)); createNodeWithContent(new Node("game-lobbies", PickingMethod.LEAST_PICKED, false));
createNodeWithContent(new Node("hub", PickingMode.SEQUENTIAL_FILLING, false)); createNodeWithContent(new Node("hub", PickingMethod.SEQUENTIAL_FILLING, false));
} }
@SneakyThrows @SneakyThrows

View File

@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import ru.dragonestia.picker.api.exception.NodeNotFoundException; import ru.dragonestia.picker.api.exception.NodeNotFoundException;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.api.repository.response.NodeDetailsResponse; import ru.dragonestia.picker.api.repository.response.NodeDetailsResponse;
import ru.dragonestia.picker.api.repository.response.NodeListResponse; import ru.dragonestia.picker.api.repository.response.NodeListResponse;
import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.Node;
@ -41,7 +41,7 @@ public class NodeController {
@PostMapping @PostMapping
ResponseEntity<?> registerNode( ResponseEntity<?> registerNode(
@Parameter(description = "Node identifier") @RequestParam(name = "nodeId") String nodeId, @Parameter(description = "Node identifier") @RequestParam(name = "nodeId") String nodeId,
@Parameter(description = "Picking mode method") @RequestParam(name = "method") PickingMode method, @Parameter(description = "Picking method method") @RequestParam(name = "method") PickingMethod method,
@Parameter(description = "Save node") @RequestParam(name = "persist", required = false, defaultValue = "false") boolean persist @Parameter(description = "Save node") @RequestParam(name = "persist", required = false, defaultValue = "false") boolean persist
) { ) {
nodeService.create(new Node(nodeId, method, persist)); nodeService.create(new Node(nodeId, method, persist));

View File

@ -1,10 +1,10 @@
package ru.dragonestia.picker.model; package ru.dragonestia.picker.model;
import lombok.NonNull; import lombok.NonNull;
import ru.dragonestia.picker.api.repository.response.type.RNode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.ResponseNode;
public record Node(@NonNull String id, @NonNull PickingMode mode, boolean persist) { public record Node(@NonNull String id, @NonNull PickingMethod method, boolean persist) {
@Override @Override
public int hashCode() { public int hashCode() {
@ -21,7 +21,7 @@ public record Node(@NonNull String id, @NonNull PickingMode mode, boolean persis
return false; return false;
} }
public RNode toResponseObject() { public ResponseNode toResponseObject() {
return new RNode(id, mode); return new ResponseNode(id, method);
} }
} }

View File

@ -31,7 +31,7 @@ public class NodeRepositoryImpl implements NodeRepository {
} }
nodeMap.put(node.id(), node); nodeMap.put(node.id(), node);
var picker = pickerRepository.create(node.id(), node.mode()); var picker = pickerRepository.create(node.id(), node.method());
nodeId2PickerModeCache.put(node.id(), picker); nodeId2PickerModeCache.put(node.id(), picker);
} }

View File

@ -2,7 +2,7 @@ package ru.dragonestia.picker.repository.impl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.UserRepository; import ru.dragonestia.picker.repository.UserRepository;
@ -20,7 +20,7 @@ public class PickerRepository {
private final UserRepository userRepository; private final UserRepository userRepository;
private final Map<String, RoomPicker> pickers = new ConcurrentHashMap<>(); private final Map<String, RoomPicker> pickers = new ConcurrentHashMap<>();
public RoomPicker create(String nodeId, PickingMode mode) { public RoomPicker create(String nodeId, PickingMethod mode) {
var picker = of(mode); var picker = of(mode);
pickers.put(nodeId, picker); pickers.put(nodeId, picker);
return picker; return picker;
@ -38,7 +38,7 @@ public class PickerRepository {
return pickers.get(nodeId).pick(users); return pickers.get(nodeId).pick(users);
} }
private RoomPicker of(PickingMode mode) { private RoomPicker of(PickingMethod mode) {
switch (mode) { switch (mode) {
case SEQUENTIAL_FILLING -> { case SEQUENTIAL_FILLING -> {
return new SequentialFillingPicker(userRepository); return new SequentialFillingPicker(userRepository);

View File

@ -1,6 +1,6 @@
package ru.dragonestia.picker.repository.impl.picker; package ru.dragonestia.picker.repository.impl.picker;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.UserRepository; import ru.dragonestia.picker.repository.UserRepository;
@ -55,7 +55,7 @@ public class LeastPickedPicker implements RoomPicker {
} }
@Override @Override
public PickingMode getPickingMode() { public PickingMethod getPickingMode() {
return PickingMode.LEAST_PICKED; return PickingMethod.LEAST_PICKED;
} }
} }

View File

@ -1,10 +1,10 @@
package ru.dragonestia.picker.repository.impl.picker; package ru.dragonestia.picker.repository.impl.picker;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.model.User;
public interface RoomPicker extends Picker<Room, User> { public interface RoomPicker extends Picker<Room, User> {
PickingMode getPickingMode(); PickingMethod getPickingMode();
} }

View File

@ -1,6 +1,6 @@
package ru.dragonestia.picker.repository.impl.picker; package ru.dragonestia.picker.repository.impl.picker;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.UserRepository; import ru.dragonestia.picker.repository.UserRepository;
@ -51,7 +51,7 @@ public class RoundRobinPicker implements RoomPicker {
} }
@Override @Override
public PickingMode getPickingMode() { public PickingMethod getPickingMode() {
return PickingMode.ROUND_ROBIN; return PickingMethod.ROUND_ROBIN;
} }
} }

View File

@ -1,6 +1,6 @@
package ru.dragonestia.picker.repository.impl.picker; package ru.dragonestia.picker.repository.impl.picker;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode; import ru.dragonestia.picker.api.model.node.PickingMethod;
import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Room;
import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.model.User;
import ru.dragonestia.picker.repository.UserRepository; import ru.dragonestia.picker.repository.UserRepository;
@ -48,7 +48,7 @@ public class SequentialFillingPicker implements RoomPicker {
} }
@Override @Override
public PickingMode getPickingMode() { public PickingMethod getPickingMode() {
return PickingMode.SEQUENTIAL_FILLING; return PickingMethod.SEQUENTIAL_FILLING;
} }
} }

View File

@ -3,7 +3,7 @@ package ru.dragonestia.picker.service;
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException; import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException; import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
import ru.dragonestia.picker.api.model.node.NodeDetails; import ru.dragonestia.picker.api.model.node.NodeDetails;
import ru.dragonestia.picker.api.repository.response.type.RNode; import ru.dragonestia.picker.api.model.node.ResponseNode;
import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.Node;
import java.util.List; import java.util.List;
@ -18,7 +18,7 @@ public interface NodeService {
List<Node> all(); List<Node> all();
List<RNode> getAllNodesWithDetailsResponse(Set<NodeDetails> details); List<ResponseNode> getAllNodesWithDetailsResponse(Set<NodeDetails> details);
Optional<Node> find(String nodeId); Optional<Node> find(String nodeId);
} }

View File

@ -5,7 +5,7 @@ import org.springframework.stereotype.Service;
import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException; import ru.dragonestia.picker.api.exception.InvalidNodeIdentifierException;
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException; import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
import ru.dragonestia.picker.api.model.node.NodeDetails; import ru.dragonestia.picker.api.model.node.NodeDetails;
import ru.dragonestia.picker.api.repository.response.type.RNode; import ru.dragonestia.picker.api.model.node.ResponseNode;
import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.Node;
import ru.dragonestia.picker.repository.NodeRepository; import ru.dragonestia.picker.repository.NodeRepository;
import ru.dragonestia.picker.service.NodeService; import ru.dragonestia.picker.service.NodeService;
@ -48,8 +48,8 @@ public class NodeServiceImpl implements NodeService {
} }
@Override @Override
public List<RNode> getAllNodesWithDetailsResponse(Set<NodeDetails> details) { public List<ResponseNode> getAllNodesWithDetailsResponse(Set<NodeDetails> details) {
var response = new LinkedList<RNode>(); var response = new LinkedList<ResponseNode>();
for (var node: all()) { for (var node: all()) {
response.add(detailsExtractor.extract(node, details)); response.add(detailsExtractor.extract(node, details));
} }

View File

@ -3,9 +3,9 @@ package ru.dragonestia.picker.util;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ru.dragonestia.picker.api.model.node.NodeDetails; import ru.dragonestia.picker.api.model.node.NodeDetails;
import ru.dragonestia.picker.api.model.node.ResponseNode;
import ru.dragonestia.picker.api.model.room.RoomDetails; import ru.dragonestia.picker.api.model.room.RoomDetails;
import ru.dragonestia.picker.api.model.user.UserDetails; import ru.dragonestia.picker.api.model.user.UserDetails;
import ru.dragonestia.picker.api.repository.response.type.RNode;
import ru.dragonestia.picker.api.repository.response.type.RRoom; import ru.dragonestia.picker.api.repository.response.type.RRoom;
import ru.dragonestia.picker.api.repository.response.type.RUser; import ru.dragonestia.picker.api.repository.response.type.RUser;
import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.Node;
@ -25,7 +25,7 @@ public class DetailsExtractor {
private final RoomRepository roomRepository; private final RoomRepository roomRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
public RNode extract(Node node, Set<NodeDetails> details) { public ResponseNode extract(Node node, Set<NodeDetails> details) {
var response = node.toResponseObject(); var response = node.toResponseObject();
for (var detail: details) { for (var detail: details) {

View File

@ -0,0 +1,15 @@
package ru.dragonestia.picker.api.model.node;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface INode {
@NotNull String getIdentifier();
@NotNull PickingMethod getPickingMethod();
@Nullable Boolean isPersist();
@Nullable String getDetail(@NotNull NodeDetails detail);
}

View File

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

View File

@ -0,0 +1,7 @@
package ru.dragonestia.picker.api.model.node;
public enum PickingMethod {
SEQUENTIAL_FILLING,
ROUND_ROBIN,
LEAST_PICKED
}

View File

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

View File

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

View File

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

View File

@ -1,64 +0,0 @@
package ru.dragonestia.picker.api.repository.response.type;
import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.node.NodeDetails;
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode;
import java.util.HashMap;
import java.util.Map;
@Schema(title = "Node")
public class RNode {
@Schema(description = "Node identifier", example = "test-node")
private String id;
@Schema(description = "Picking mode method for users between rooms", example = "LEAST_PICKED")
private PickingMode mode;
@Schema(description = "Additional data requested (Key-Value)")
private Map<NodeDetails, String> details;
private RNode() {}
public RNode(String id, PickingMode mode) {
this.id = id;
this.mode = mode;
this.details = new HashMap<>();
}
public String getId() {
return id;
}
public PickingMode getMode() {
return mode;
}
public void putDetail(NodeDetails detail, String value) {
details.put(detail, value);
}
public String getDetails(NodeDetails detail) {
return details.get(detail);
}
public Map<NodeDetails, String> getDetails() {
return details;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if (object == null) return false;
if (object instanceof RNode other) {
return id.equals(other.id);
}
return false;
}
}

View File

@ -1,6 +1,7 @@
package ru.dragonestia.picker.api.repository.response.type; package ru.dragonestia.picker.api.repository.response.type;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import ru.dragonestia.picker.api.model.node.ResponseNode;
import ru.dragonestia.picker.api.model.room.RoomDetails; import ru.dragonestia.picker.api.model.room.RoomDetails;
import java.beans.Transient; import java.beans.Transient;
@ -40,8 +41,8 @@ public class RRoom {
this.details = new HashMap<>(); this.details = new HashMap<>();
} }
public RRoom(String id, RNode node, int limit, String payload) { public RRoom(String id, ResponseNode node, int limit, String payload) {
this(id, node.getId(), limit, payload); this(id, node.getIdentifier(), limit, payload);
} }
public String getId() { public String getId() {

View File

@ -1,17 +0,0 @@
package ru.dragonestia.picker.api.repository.response.type.type;
public enum PickingMode {
SEQUENTIAL_FILLING("Sequential filling"),
ROUND_ROBIN("Round Robin"),
LEAST_PICKED("Least Picked");
private final String name;
PickingMode(String name) {
this.name = name;
}
public String getName() {
return name;
}
}