Implemented search users
This commit is contained in:
parent
3e679f2b9a
commit
be203ccf5c
@ -24,4 +24,6 @@ public interface UserRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<RUser> all(RRoom room, Set<UserDetails> details) throws NodeNotFoundException, RoomNotFoundException;
|
List<RUser> all(RRoom room, Set<UserDetails> details) throws NodeNotFoundException, RoomNotFoundException;
|
||||||
|
|
||||||
|
List<RUser> search(String input, Set<UserDetails> details);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
package ru.dragonestia.picker.api.repository.response;
|
||||||
|
|
||||||
|
import ru.dragonestia.picker.api.repository.response.type.RUser;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record SearchUserResponse(List<RUser> users) {}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package ru.dragonestia.picker.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.dragonestia.picker.api.repository.details.UserDetails;
|
||||||
|
import ru.dragonestia.picker.api.repository.response.SearchUserResponse;
|
||||||
|
import ru.dragonestia.picker.service.UserService;
|
||||||
|
import ru.dragonestia.picker.util.NamingValidator;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/users")
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
private final NamingValidator namingValidator;
|
||||||
|
|
||||||
|
@GetMapping("/search")
|
||||||
|
SearchUserResponse search(@RequestParam(name = "input") String input,
|
||||||
|
@RequestParam(name = "requiredDetails", required = false, defaultValue = "") String detailsSeq) {
|
||||||
|
|
||||||
|
if (!namingValidator.validateUserId(input) || input.isEmpty()) {
|
||||||
|
return new SearchUserResponse(List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
var details = new HashSet<UserDetails>();
|
||||||
|
for (var detailStr: detailsSeq.split(",")) {
|
||||||
|
try {
|
||||||
|
details.add(UserDetails.valueOf(detailStr.toUpperCase()));
|
||||||
|
} catch (IllegalArgumentException ignore) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SearchUserResponse(userService.searchUsers(input, details));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package ru.dragonestia.picker.repository;
|
package ru.dragonestia.picker.repository;
|
||||||
|
|
||||||
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
||||||
|
import ru.dragonestia.picker.api.repository.response.type.RUser;
|
||||||
import ru.dragonestia.picker.model.Room;
|
import ru.dragonestia.picker.model.Room;
|
||||||
import ru.dragonestia.picker.model.User;
|
import ru.dragonestia.picker.model.User;
|
||||||
|
|
||||||
@ -19,4 +20,6 @@ public interface UserRepository {
|
|||||||
void onRemoveRoom(Room room);
|
void onRemoveRoom(Room room);
|
||||||
|
|
||||||
List<User> usersOf(Room room);
|
List<User> usersOf(Room room);
|
||||||
|
|
||||||
|
List<User> search(String input);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -120,6 +120,16 @@ public class UserRepositoryImpl implements UserRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<User> search(String input) {
|
||||||
|
synchronized (usersMap) {
|
||||||
|
return usersMap.keySet().stream()
|
||||||
|
.filter(user -> user.id().startsWith(input))
|
||||||
|
.sorted(Comparator.comparing(User::id))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private record NodeRoomPath(String node, String bucket) {
|
private record NodeRoomPath(String node, String bucket) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -21,4 +21,6 @@ public interface UserService {
|
|||||||
List<User> getRoomUsers(Room room);
|
List<User> getRoomUsers(Room room);
|
||||||
|
|
||||||
List<RUser> getRoomUsersWithDetailsResponse(Room room, Set<UserDetails> details);
|
List<RUser> getRoomUsersWithDetailsResponse(Room room, Set<UserDetails> details);
|
||||||
|
|
||||||
|
List<RUser> searchUsers(String input, Set<UserDetails> details);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,4 +56,20 @@ public class UserServiceImpl implements UserService {
|
|||||||
}
|
}
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RUser> searchUsers(String input, Set<UserDetails> details) {
|
||||||
|
return userRepository.search(input).stream()
|
||||||
|
.map(user -> {
|
||||||
|
var responseUser = user.toResponseObject();
|
||||||
|
|
||||||
|
for (var detail: details) {
|
||||||
|
if (detail == UserDetails.COUNT_ROOMS) {
|
||||||
|
responseUser.putDetail(UserDetails.COUNT_ROOMS, Integer.toString(getUserRooms(user).size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseUser;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class NamingValidator {
|
|||||||
throw new InvalidRoomIdentifierException(nodeId, input);
|
throw new InvalidRoomIdentifierException(nodeId, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateUserId(String input) {
|
public boolean validateUserId(String input) {
|
||||||
return ValidateIdentifier.forUser(input);
|
return ValidateIdentifier.forUser(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ public class MainLayout extends AppLayout {
|
|||||||
private SideNav createSideNav() {
|
private SideNav createSideNav() {
|
||||||
var nav = new SideNav();
|
var nav = new SideNav();
|
||||||
nav.addItem(new SideNavItem("Nodes list", NodesPage.class, VaadinIcon.FOLDER_O.create()));
|
nav.addItem(new SideNavItem("Nodes list", NodesPage.class, VaadinIcon.FOLDER_O.create()));
|
||||||
nav.addItem(new SideNavItem("Search users", HomePage.class, VaadinIcon.SEARCH.create()));
|
nav.addItem(new SideNavItem("Search users", UserSearchPage.class, VaadinIcon.SEARCH.create()));
|
||||||
nav.addItem(new SideNavItem("Documentation", "https://github.com/ScarletRedMan/RoomPicker", VaadinIcon.BOOK.create()));
|
nav.addItem(new SideNavItem("Documentation", "https://github.com/ScarletRedMan/RoomPicker", VaadinIcon.BOOK.create()));
|
||||||
nav.addItem(new SideNavItem("Sign-out", HomePage.class, VaadinIcon.SIGN_OUT.create()));
|
nav.addItem(new SideNavItem("Sign-out", HomePage.class, VaadinIcon.SIGN_OUT.create()));
|
||||||
return nav;
|
return nav;
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
package ru.dragonestia.picker.cp.page;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.Unit;
|
||||||
|
import com.vaadin.flow.component.button.Button;
|
||||||
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
|
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.icon.Icon;
|
||||||
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||||
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
|
import com.vaadin.flow.router.PageTitle;
|
||||||
|
import com.vaadin.flow.router.Route;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import ru.dragonestia.picker.api.repository.UserRepository;
|
||||||
|
import ru.dragonestia.picker.api.repository.details.UserDetails;
|
||||||
|
import ru.dragonestia.picker.api.repository.response.type.RUser;
|
||||||
|
import ru.dragonestia.picker.cp.component.Notifications;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@PageTitle("Search users")
|
||||||
|
@Route(value = "/users", layout = MainLayout.class)
|
||||||
|
public class UserSearchPage extends VerticalLayout {
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final TextField fieldUsername;
|
||||||
|
private final Grid<RUser> userGrid;
|
||||||
|
private List<RUser> cachedUsers = new LinkedList<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UserSearchPage(UserRepository userRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
|
||||||
|
add(fieldUsername = createUsernameInputField());
|
||||||
|
add(userGrid = createUserGrid());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextField createUsernameInputField() {
|
||||||
|
var field = new TextField();
|
||||||
|
field.setLabel("Username");
|
||||||
|
field.setPlaceholder("some-user-identifier");
|
||||||
|
field.setRequired(true);
|
||||||
|
field.setMinWidth(30, Unit.PERCENTAGE);
|
||||||
|
|
||||||
|
var button = new Button(new Icon(VaadinIcon.SEARCH));
|
||||||
|
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
button.getStyle().set("color", "#FFFFFF");
|
||||||
|
button.addClickListener(event -> search(fieldUsername.getValue().trim()));
|
||||||
|
|
||||||
|
field.setSuffixComponent(button);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Grid<RUser> createUserGrid() {
|
||||||
|
var grid = new Grid<RUser>();
|
||||||
|
|
||||||
|
grid.addColumn(RUser::getId).setHeader("Identifier")
|
||||||
|
.setFooter("Found %s users".formatted(cachedUsers.size()));
|
||||||
|
|
||||||
|
grid.addColumn(user -> user.getDetail(UserDetails.COUNT_ROOMS)).setTextAlign(ColumnTextAlign.CENTER).setHeader("Linked with rooms");
|
||||||
|
|
||||||
|
grid.addComponentColumn(user -> new Span("buttons")).setHeader("Manage"); // TODO
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void search(String input) {
|
||||||
|
userGrid.setItems(cachedUsers = userRepository.search(input, UserRepository.ALL_DETAILS));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import org.springframework.http.HttpMethod;
|
|||||||
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
|
||||||
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
import ru.dragonestia.picker.api.exception.RoomAreFullException;
|
||||||
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
|
||||||
|
import ru.dragonestia.picker.api.repository.response.SearchUserResponse;
|
||||||
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.api.repository.UserRepository;
|
import ru.dragonestia.picker.api.repository.UserRepository;
|
||||||
@ -54,4 +55,17 @@ public class UserRepositoryImpl implements UserRepository {
|
|||||||
params.put("requiredDetails", detailsStr);
|
params.put("requiredDetails", detailsStr);
|
||||||
}).users();
|
}).users();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RUser> search(String input, Set<UserDetails> details) {
|
||||||
|
return rest.query("/users/search",
|
||||||
|
HttpMethod.GET,
|
||||||
|
SearchUserResponse.class,
|
||||||
|
params -> {
|
||||||
|
var detailsStr = String.join(",", details.stream().map(Enum::toString).toList());
|
||||||
|
|
||||||
|
params.put("requiredDetails", detailsStr);
|
||||||
|
params.put("input", input);
|
||||||
|
}).users();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user