From 82d47a2d8aa998f8601bc0bcf7233d092961fcce Mon Sep 17 00:00:00 2001 From: HttpRafa <60099368+HttpRafa@users.noreply.github.com> Date: Thu, 1 May 2025 16:51:23 +0200 Subject: [PATCH 1/2] feat: Changed gRPCs --- .../window/connect/tab/server/screen.rs | 2 +- .../atomic/cloud/api/resource/Resources.java | 45 +++++++++++++++++++ .../io/atomic/cloud/api/user/CloudUser.java | 17 +++++++ .../java/io/atomic/cloud/api/user/Users.java | 18 ++++++++ .../connection/client/ClientConnection.java | 2 + .../connection/client/ManageConnection.java | 2 +- .../common/resource/ResourceManager.java | 28 ++++++++++++ .../atomic/cloud/common/user/UserManager.java | 14 ++++++ .../argument/TransferTargetArgument.java | 2 +- controller/src/network/client/group.rs | 11 ++++- controller/src/network/manage/group.rs | 25 +---------- protocol/grpc/client/group.proto | 12 ----- protocol/grpc/client/server.proto | 18 -------- protocol/grpc/client/service.proto | 19 +++++--- protocol/grpc/common/group.proto | 15 +++++++ protocol/grpc/common/server.proto | 18 ++++++++ protocol/grpc/{manage => common}/user.proto | 8 ++-- protocol/grpc/manage/group.proto | 6 --- protocol/grpc/manage/server.proto | 9 ---- protocol/grpc/manage/service.proto | 13 ++++-- 20 files changed, 198 insertions(+), 86 deletions(-) create mode 100644 clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java delete mode 100644 protocol/grpc/client/group.proto delete mode 100644 protocol/grpc/client/server.proto create mode 100644 protocol/grpc/common/group.proto create mode 100644 protocol/grpc/common/server.proto rename protocol/grpc/{manage => common}/user.proto (52%) diff --git a/cli/src/application/window/connect/tab/server/screen.rs b/cli/src/application/window/connect/tab/server/screen.rs index 22753b46..44ad86b1 100644 --- a/cli/src/application/window/connect/tab/server/screen.rs +++ b/cli/src/application/window/connect/tab/server/screen.rs @@ -265,7 +265,7 @@ impl ScreenTab { // Calculate the height/lines of the content area self.update_lines( - paragraph.line_count(content_area.width), // Might be removed in the future my ratatui + paragraph.line_count(content_area.width), // Might be removed in the future by ratatui content_area.height, ); diff --git a/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java b/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java index 3878e4a3..28af2491 100644 --- a/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java +++ b/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java @@ -2,6 +2,8 @@ import io.atomic.cloud.api.resource.simple.SimpleCloudGroup; import io.atomic.cloud.api.resource.simple.SimpleCloudServer; +import java.util.Collection; +import java.util.Optional; import java.util.concurrent.CompletableFuture; /** The Resources interface provides methods to access cloud groups and cloud servers. */ @@ -14,10 +16,53 @@ public interface Resources { */ CompletableFuture groups(); + /** + * Retrieves a SimpleGroup object that matches the specified name. + * + * @param name the name of the group to retrieve + * @return a SimpleGroup instance + */ + CompletableFuture> groupFromName(String name); + + /** + * Retrieves a SimpleGroup object that contains the specified user as a member. + * + * @param uuid the uuid of the user to retrieve the group for + * @return a SimpleGroup instance + */ + CompletableFuture> groupFromUser(String uuid); + /** * Retrieves an array of SimpleServer objects. * * @return an array of SimpleServer instances */ CompletableFuture servers(); + + /** + * Retrieves an array of SimpleServer objects that match the specified name. + *

+ * NOTE: It is possible that multiple servers have the same name. + * Because the controller currently on imposes no uniqueness constraints, this method may return multiple servers. + * + * @param name the name of the servers to retrieve + * @return a collection of SimpleServer instances + */ + CompletableFuture> serverFromName(String name); + + /** + * Retrieves a SimpleServer object that matches the specified uuid. + * + * @param uuid the uuid of the server to retrieve + * @return a SimpleServer instance + */ + CompletableFuture> serverFromUuid(String uuid); + + /** + * Retrieves a SimpleServer object that contains the specified user as a member. + * + * @param uuid the uuid of the user to retrieve the server for + * @return a SimpleServer instance + */ + CompletableFuture> serverFromUser(String uuid); } diff --git a/clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java b/clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java new file mode 100644 index 00000000..5d21220c --- /dev/null +++ b/clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java @@ -0,0 +1,17 @@ +package io.atomic.cloud.api.user; + +import io.atomic.cloud.api.resource.simple.SimpleCloudGroup; +import io.atomic.cloud.api.resource.simple.SimpleCloudServer; +import java.util.Optional; +import java.util.UUID; + +public interface CloudUser { + + String name(); + + UUID uuid(); + + Optional group(); + + Optional server(); +} diff --git a/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java b/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java index 9628fa90..8fe280db 100644 --- a/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java +++ b/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java @@ -1,7 +1,9 @@ package io.atomic.cloud.api.user; +import java.util.UUID; import java.util.concurrent.CompletableFuture; +/** The Users interface provides methods to access cloud users. */ public interface Users { /** @@ -10,4 +12,20 @@ public interface Users { * @return The number of users on the network */ CompletableFuture userCount(); + + /** + * Retrieves a User object that matches the specified name. + * + * @param name the uuid of the server to retrieve + * @return a User instance + */ + CompletableFuture userFromName(String name); + + /** + * Retrieves a User object that matches the specified uuid. + * + * @param uuid the uuid of the user to retrieve + * @return a User instance + */ + CompletableFuture userFromUuid(UUID uuid); } diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java index b90db13f..f165322a 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java @@ -9,7 +9,9 @@ import io.atomic.cloud.common.connection.call.CallHandle; import io.atomic.cloud.common.connection.credential.TokenCredential; import io.atomic.cloud.grpc.client.*; +import io.atomic.cloud.grpc.common.Group; import io.atomic.cloud.grpc.common.Notify; +import io.atomic.cloud.grpc.common.Server; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.net.MalformedURLException; diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java index 4ed18a0e..3800b741 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java @@ -27,7 +27,7 @@ public class ManageConnection extends Connection { private final CachedObject controllerVersion = new CachedObject<>(); private final CachedObject pluginsInfo = new CachedObject<>(); private final CachedObject nodesInfo = new CachedObject<>(); - private final CachedObject groupsInfo = new CachedObject<>(); + private final CachedObject groupsInfo = new CachedObject<>(); private final CachedObject serversInfo = new CachedObject<>(); private final CachedObject usersInfo = new CachedObject<>(); diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java b/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java index 318f468e..2fb2c870 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java @@ -6,6 +6,8 @@ import io.atomic.cloud.common.connection.client.ClientConnection; import io.atomic.cloud.common.resource.object.simple.SimpleCloudGroupImpl; import io.atomic.cloud.common.resource.object.simple.SimpleCloudServerImpl; + +import java.util.Collection; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -23,6 +25,16 @@ public CompletableFuture groups() { .toArray(SimpleCloudGroupImpl[]::new)); } + @Override + public CompletableFuture> groupFromName(String name) { + return null; + } + + @Override + public CompletableFuture> groupFromUser(String uuid) { + return null; + } + @Override public CompletableFuture servers() { return this.connection.servers().thenApply(list -> list.getServersList().stream() @@ -33,4 +45,20 @@ public CompletableFuture servers() { server.getNode())) .toArray(SimpleCloudServerImpl[]::new)); } + + @Override + public CompletableFuture> serverFromName(String name) { + return null; + } + + @Override + public CompletableFuture> serverFromUuid(String uuid) { + return null; + } + + @Override + public CompletableFuture> serverFromUser(String uuid) { + return null; + } + } diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java b/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java index 789799ea..2162b4d9 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java @@ -1,8 +1,11 @@ package io.atomic.cloud.common.user; import com.google.protobuf.UInt32Value; +import io.atomic.cloud.api.user.CloudUser; import io.atomic.cloud.api.user.Users; import io.atomic.cloud.common.connection.client.ClientConnection; + +import java.util.UUID; import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; @@ -15,4 +18,15 @@ public class UserManager implements Users { public CompletableFuture userCount() { return this.connection.userCount().thenApply(UInt32Value::getValue); } + + @Override + public CompletableFuture userFromName(String name) { + return null; + } + + @Override + public CompletableFuture userFromUuid(UUID uuid) { + return null; + } + } diff --git a/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java b/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java index 5d3055de..7653dcef 100644 --- a/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java +++ b/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java @@ -72,7 +72,7 @@ public class TransferTargetArgument implements CustomArgumentType.Converted { - response.servers + response. .getServersList() .forEach(server -> builder.suggest( "server:" + server.getName(), diff --git a/controller/src/network/client/group.rs b/controller/src/network/client/group.rs index d49f45cf..77c46fbd 100644 --- a/controller/src/network/client/group.rs +++ b/controller/src/network/client/group.rs @@ -2,8 +2,7 @@ use anyhow::Result; use tonic::async_trait; use crate::{ - application::Controller, - network::proto::client::group::List, + application::{group::Group, Controller}, task::{BoxedAny, GenericTask, Task}, }; @@ -22,3 +21,11 @@ impl GenericTask for GetGroupsTask { }) } } + +impl From<&&Group> for Short { + fn from(group: &&Group) -> Self { + Self { + name: group.name().clone(), + } + } +} diff --git a/controller/src/network/manage/group.rs b/controller/src/network/manage/group.rs index 1dab7680..50ce8241 100644 --- a/controller/src/network/manage/group.rs +++ b/controller/src/network/manage/group.rs @@ -10,7 +10,7 @@ use crate::{ network::proto::{ common::KeyValue, manage::{ - group::{Constraints, Detail, List, Scaling, Short}, + group::{Constraints, Detail, Scaling}, server::{self, Fallback}, }, }, @@ -34,7 +34,6 @@ pub struct UpdateGroupTask( pub Option>, ); pub struct GetGroupTask(pub String); -pub struct GetGroupsTask(); #[async_trait] impl GenericTask for CreateGroupTask { @@ -91,28 +90,6 @@ impl GenericTask for GetGroupTask { } } -#[async_trait] -impl GenericTask for GetGroupsTask { - async fn run(&mut self, controller: &mut Controller) -> Result { - Task::new_ok(List { - groups: controller - .groups - .get_groups() - .iter() - .map(std::convert::Into::into) - .collect(), - }) - } -} - -impl From<&&Group> for Short { - fn from(group: &&Group) -> Self { - Self { - name: group.name().clone(), - } - } -} - impl From<&Group> for Detail { fn from(value: &Group) -> Self { Self { diff --git a/protocol/grpc/client/group.proto b/protocol/grpc/client/group.proto deleted file mode 100644 index fe22243e..00000000 --- a/protocol/grpc/client/group.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -option java_multiple_files = true; -option java_package = "io.atomic.cloud.grpc.client"; - -package client; - -message Group { - message List { - repeated string groups = 1; - } -} \ No newline at end of file diff --git a/protocol/grpc/client/server.proto b/protocol/grpc/client/server.proto deleted file mode 100644 index 8a3981a0..00000000 --- a/protocol/grpc/client/server.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -option java_multiple_files = true; -option java_package = "io.atomic.cloud.grpc.client"; - -package client; - -message Server { - message List { - repeated Short servers = 1; - } - message Short { - string name = 1; - string id = 2; - optional string group = 3; - string node = 4; - } -} \ No newline at end of file diff --git a/protocol/grpc/client/service.proto b/protocol/grpc/client/service.proto index 8353618b..1cdb1853 100644 --- a/protocol/grpc/client/service.proto +++ b/protocol/grpc/client/service.proto @@ -8,13 +8,14 @@ package client; import "google/protobuf/empty.proto"; import "google/protobuf/wrappers.proto"; +import "common/server.proto"; +import "common/group.proto"; +import "common/user.proto"; import "common/notify.proto"; import "client/user.proto"; import "client/transfer.proto"; import "client/channel.proto"; -import "client/server.proto"; -import "client/group.proto"; service ClientService { // Heartbeat @@ -30,6 +31,10 @@ service ClientService { // User operations rpc UserConnected(User.ConnectedReq) returns (google.protobuf.Empty); rpc UserDisconnected(User.DisconnectedReq) returns (google.protobuf.Empty); + /// Info + rpc GetUser(google.protobuf.StringValue) returns (common.CommonUser.Item); + rpc GetUserFromId(google.protobuf.StringValue) returns (common.CommonUser.Item); + rpc GetUsers(google.protobuf.Empty) returns (common.CommonUser.List); rpc GetUserCount(google.protobuf.Empty) returns (google.protobuf.UInt32Value); // Transfer operations @@ -40,9 +45,13 @@ service ClientService { rpc PublishMessage(Channel.Msg) returns (google.protobuf.UInt32Value); rpc SubscribeToChannel(google.protobuf.StringValue) returns (stream Channel.Msg); - // Server/Group info - rpc GetServers(google.protobuf.Empty) returns (Server.List); - rpc GetGroups(google.protobuf.Empty) returns (Group.List); + // Group operations + rpc GetGroup(google.protobuf.StringValue) returns (common.CommonGroup.Short); + rpc GetGroups(google.protobuf.Empty) returns (common.CommonGroup.List); + + // Server operations + rpc GetServer(google.protobuf.StringValue) returns (common.CommonServer.Short); + rpc GetServers(google.protobuf.Empty) returns (common.CommonServer.List); // Version info rpc GetProtoVer(google.protobuf.Empty) returns (google.protobuf.UInt32Value); diff --git a/protocol/grpc/common/group.proto b/protocol/grpc/common/group.proto new file mode 100644 index 00000000..8e36c7bc --- /dev/null +++ b/protocol/grpc/common/group.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.atomic.cloud.grpc.common"; + +package common; + +message CommonGroup { + message List { + repeated Short groups = 1; + } + message Short { + string name = 1; + } +} \ No newline at end of file diff --git a/protocol/grpc/common/server.proto b/protocol/grpc/common/server.proto new file mode 100644 index 00000000..a7f5d7c9 --- /dev/null +++ b/protocol/grpc/common/server.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.atomic.cloud.grpc.common"; + +package common; + +message CommonServer { + message List { + repeated Short servers = 1; + } + message Short { + string name = 1; + string id = 2; + optional string group = 3; + string node = 4; + } +} \ No newline at end of file diff --git a/protocol/grpc/manage/user.proto b/protocol/grpc/common/user.proto similarity index 52% rename from protocol/grpc/manage/user.proto rename to protocol/grpc/common/user.proto index bb211536..f63b0960 100644 --- a/protocol/grpc/manage/user.proto +++ b/protocol/grpc/common/user.proto @@ -1,16 +1,18 @@ syntax = "proto3"; option java_multiple_files = true; -option java_package = "io.atomic.cloud.grpc.manage"; +option java_package = "io.atomic.cloud.grpc.common"; -package manage; +package common; -message User { +message CommonUser { message List { repeated Item users = 1; } message Item { string name = 1; string id = 2; + optional string group = 3; + optional string server = 4; } } \ No newline at end of file diff --git a/protocol/grpc/manage/group.proto b/protocol/grpc/manage/group.proto index ebe71e86..444de643 100644 --- a/protocol/grpc/manage/group.proto +++ b/protocol/grpc/manage/group.proto @@ -8,12 +8,6 @@ package manage; import "manage/server.proto"; message Group { - message List { - repeated Short groups = 1; - } - message Short { - string name = 1; - } message Detail { string name = 1; repeated string nodes = 2; diff --git a/protocol/grpc/manage/server.proto b/protocol/grpc/manage/server.proto index 3befd8d2..244be4e1 100644 --- a/protocol/grpc/manage/server.proto +++ b/protocol/grpc/manage/server.proto @@ -8,15 +8,6 @@ package manage; import "common/common.proto"; message Server { - message List { - repeated Short servers = 1; - } - message Short { - string name = 1; - string id = 2; - optional string group = 3; - string node = 4; - } message Detail { string name = 1; string id = 2; diff --git a/protocol/grpc/manage/service.proto b/protocol/grpc/manage/service.proto index 21eedb6c..082c6b8c 100644 --- a/protocol/grpc/manage/service.proto +++ b/protocol/grpc/manage/service.proto @@ -8,6 +8,9 @@ package manage; import "google/protobuf/empty.proto"; import "google/protobuf/wrappers.proto"; +import "common/server.proto"; +import "common/group.proto"; +import "common/user.proto"; import "common/notify.proto"; import "manage/resource.proto"; @@ -16,7 +19,6 @@ import "manage/node.proto"; import "manage/group.proto"; import "manage/server.proto"; import "manage/screen.proto"; -import "manage/user.proto"; import "manage/transfer.proto"; service ManageService { @@ -39,19 +41,22 @@ service ManageService { rpc CreateGroup(Group.Detail) returns (google.protobuf.Empty); rpc UpdateGroup(Group.UpdateReq) returns (Group.Detail); rpc GetGroup(google.protobuf.StringValue) returns (Group.Detail); - rpc GetGroups(google.protobuf.Empty) returns (Group.List); + rpc GetGroups(google.protobuf.Empty) returns (common.CommonGroup.List); // Server operations rpc ScheduleServer(Server.Proposal) returns (google.protobuf.StringValue); rpc GetServer(google.protobuf.StringValue) returns (Server.Detail); - rpc GetServers(google.protobuf.Empty) returns (Server.List); + rpc GetServers(google.protobuf.Empty) returns (common.CommonServer.List); // Screen operations rpc WriteToScreen(Screen.WriteReq) returns (google.protobuf.Empty); rpc SubscribeToScreen(google.protobuf.StringValue) returns (stream Screen.Lines); // User operations - rpc GetUsers(google.protobuf.Empty) returns (User.List); + rpc GetUser(google.protobuf.StringValue) returns (common.CommonUser.Item); + rpc GetUserFromId(google.protobuf.StringValue) returns (common.CommonUser.Item); + rpc GetUsers(google.protobuf.Empty) returns (common.CommonUser.List); + rpc GetUserCount(google.protobuf.Empty) returns (google.protobuf.UInt32Value); // Transfer operations rpc TransferUsers(Transfer.TransferReq) returns (google.protobuf.UInt32Value); From bce145917af188a6304a73578aa611dabc3da39c Mon Sep 17 00:00:00 2001 From: HttpRafa <60099368+HttpRafa@users.noreply.github.com> Date: Thu, 1 May 2025 18:28:18 +0200 Subject: [PATCH 2/2] feat: Implement rust part --- .../application/network/connection/wrapper.rs | 9 +- .../window/connect/tab/group/create.rs | 6 +- .../window/connect/tab/group/get.rs | 11 +- .../window/connect/tab/server/get.rs | 9 +- .../window/connect/tab/server/screen.rs | 16 ++- .../window/connect/tab/user/transfer.rs | 14 +- .../atomic/cloud/api/resource/Resources.java | 19 ++- .../io/atomic/cloud/api/user/CloudUser.java | 6 +- .../java/io/atomic/cloud/api/user/Users.java | 5 +- .../connection/client/ClientConnection.java | 55 ++++++-- .../connection/client/ManageConnection.java | 51 +++++-- .../common/resource/ResourceManager.java | 78 ++++++++-- .../cloud/common/user/CloudUserImpl.java | 8 ++ .../atomic/cloud/common/user/UserManager.java | 51 ++++++- .../argument/TransferTargetArgument.java | 17 +-- controller/src/application/server/manager.rs | 6 + controller/src/application/user.rs | 1 + controller/src/application/user/manager.rs | 6 +- controller/src/network/client.rs | 133 ++++++++++++++++-- controller/src/network/client/group.rs | 19 ++- controller/src/network/client/server.rs | 33 ++++- controller/src/network/client/user.rs | 98 ++++++++++++- controller/src/network/manage.rs | 105 +++++++++++--- controller/src/network/manage/group.rs | 17 ++- controller/src/network/manage/server.rs | 29 ++-- controller/src/network/manage/user.rs | 101 +++++++++++-- protocol/grpc/client/service.proto | 3 +- protocol/grpc/manage/service.proto | 3 +- 28 files changed, 752 insertions(+), 157 deletions(-) create mode 100644 clients/java/common/src/main/java/io/atomic/cloud/common/user/CloudUserImpl.java diff --git a/cli/src/application/network/connection/wrapper.rs b/cli/src/application/network/connection/wrapper.rs index dcb03c03..fb4a448a 100644 --- a/cli/src/application/network/connection/wrapper.rs +++ b/cli/src/application/network/connection/wrapper.rs @@ -4,13 +4,12 @@ use color_eyre::eyre::Result; use tonic::Streaming; use crate::application::network::proto::{ - common::notify, + common::{common_group, common_server, common_user, notify}, manage::{ group, node, plugin, resource::{DelReq, SetReq}, screen, server, transfer::TransferReq, - user, }, }; @@ -162,7 +161,7 @@ impl EstablishedConnection { }) } - pub fn get_groups(&self) -> NetworkTask>> { + pub fn get_groups(&self) -> NetworkTask>> { let connection = self.connection.clone(); let request = self.create_request(()); @@ -194,7 +193,7 @@ impl EstablishedConnection { }) } - pub fn get_servers(&self) -> NetworkTask>> { + pub fn get_servers(&self) -> NetworkTask>> { let connection = self.connection.clone(); let request = self.create_request(()); @@ -236,7 +235,7 @@ impl EstablishedConnection { }) } - pub fn get_users(&self) -> NetworkTask>> { + pub fn get_users(&self) -> NetworkTask>> { let connection = self.connection.clone(); let request = self.create_request(()); diff --git a/cli/src/application/window/connect/tab/group/create.rs b/cli/src/application/window/connect/tab/group/create.rs index fb6a814f..dba0ca7f 100644 --- a/cli/src/application/window/connect/tab/group/create.rs +++ b/cli/src/application/window/connect/tab/group/create.rs @@ -14,7 +14,7 @@ use tonic::async_trait; use crate::application::{ network::{ connection::{task::EmptyTask, EstablishedConnection}, - proto::manage::group, + proto::{common::common_group, manage::group}, }, util::{ fancy_toml::FancyToml, @@ -50,7 +50,9 @@ pub struct CreateGroupTab { impl CreateGroupTab { /// Creates a new create group tab. /// This function will create a window stack to get the required information to create a group. - pub fn new_stack(connection: Arc) -> FetchWindow> { + pub fn new_stack( + connection: Arc, + ) -> FetchWindow> { FetchWindow::new( connection.get_groups(), connection, diff --git a/cli/src/application/window/connect/tab/group/get.rs b/cli/src/application/window/connect/tab/group/get.rs index c5a52f95..39b77c55 100644 --- a/cli/src/application/window/connect/tab/group/get.rs +++ b/cli/src/application/window/connect/tab/group/get.rs @@ -14,7 +14,10 @@ use ratatui::{ use tonic::async_trait; use crate::application::{ - network::{connection::EstablishedConnection, proto::manage::group}, + network::{ + connection::EstablishedConnection, + proto::{common::common_group, manage::group}, + }, util::fancy_toml::FancyToml, window::{ connect::tab::util::{fetch::FetchWindow, select::SelectWindow}, @@ -34,7 +37,9 @@ pub struct GetGroupTab { impl GetGroupTab { /// Creates a new get group tab. /// This function will create a window stack to get the required information to display the group. - pub fn new_stack(connection: Arc) -> FetchWindow> { + pub fn new_stack( + connection: Arc, + ) -> FetchWindow> { FetchWindow::new( connection.get_groups(), connection, @@ -130,7 +135,7 @@ impl GetGroupTab { } } -impl Display for group::Short { +impl Display for common_group::Short { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { write!(formatter, "{}", self.name) } diff --git a/cli/src/application/window/connect/tab/server/get.rs b/cli/src/application/window/connect/tab/server/get.rs index 0551f26d..de45b13b 100644 --- a/cli/src/application/window/connect/tab/server/get.rs +++ b/cli/src/application/window/connect/tab/server/get.rs @@ -13,7 +13,10 @@ use tonic::async_trait; use crate::application::{ network::{ connection::EstablishedConnection, - proto::manage::server::{self}, + proto::{ + common::common_server, + manage::server::{self}, + }, }, util::fancy_toml::FancyToml, window::{ @@ -34,7 +37,9 @@ pub struct GetServerTab { impl GetServerTab { /// Creates a new get server tab. /// This function will create a window stack to get the required information to display the server. - pub fn new_stack(connection: Arc) -> FetchWindow> { + pub fn new_stack( + connection: Arc, + ) -> FetchWindow> { FetchWindow::new( connection.get_servers(), connection, diff --git a/cli/src/application/window/connect/tab/server/screen.rs b/cli/src/application/window/connect/tab/server/screen.rs index 44ad86b1..36e69ad0 100644 --- a/cli/src/application/window/connect/tab/server/screen.rs +++ b/cli/src/application/window/connect/tab/server/screen.rs @@ -22,9 +22,9 @@ use tui_textarea::TextArea; use crate::application::{ network::{ connection::EstablishedConnection, - proto::manage::{ - screen::{self, WriteReq}, - server, + proto::{ + common::common_server, + manage::screen::{self, WriteReq}, }, }, util::{ @@ -41,7 +41,7 @@ use crate::application::{ pub struct ScreenTab { /* Connection */ connection: Arc, - server: server::Short, + server: common_server::Short, stream: Streaming, /* State */ @@ -63,7 +63,9 @@ pub struct ScreenTab { impl ScreenTab { /// Creates a new screen tab. /// This function will create a window stack to get the required information to display the screen. - pub fn new_stack(connection: Arc) -> FetchWindow> { + pub fn new_stack( + connection: Arc, + ) -> FetchWindow> { FetchWindow::new( connection.get_servers(), connection, @@ -90,7 +92,7 @@ impl ScreenTab { pub fn new( connection: Arc, - server: server::Short, + server: common_server::Short, stream: Streaming, ) -> Self { let mut command = TextArea::default(); @@ -297,7 +299,7 @@ impl ScreenTab { } } -impl Display for server::Short { +impl Display for common_server::Short { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { write!(formatter, "{}", self.name) } diff --git a/cli/src/application/window/connect/tab/user/transfer.rs b/cli/src/application/window/connect/tab/user/transfer.rs index 72fc0414..68d6df12 100644 --- a/cli/src/application/window/connect/tab/user/transfer.rs +++ b/cli/src/application/window/connect/tab/user/transfer.rs @@ -15,9 +15,9 @@ use tonic::async_trait; use crate::application::{ network::{ connection::{task::EmptyTask, EstablishedConnection}, - proto::manage::{ - transfer::{target::Type, Target, TransferReq}, - user, + proto::{ + common::common_user, + manage::transfer::{target::Type, Target, TransferReq}, }, }, util::status::{Status, StatusDisplay}, @@ -42,7 +42,9 @@ pub struct TransferUserTab { impl TransferUserTab { /// Creates a new transfer user tab. /// This function will create a window stack to get the required information to transfer the users. - pub fn new_stack(connection: Arc) -> FetchWindow> { + pub fn new_stack( + connection: Arc, + ) -> FetchWindow> { FetchWindow::new( connection.get_users(), connection, @@ -114,7 +116,7 @@ impl TransferUserTab { target: Option, ) -> Self where - T: IntoIterator, + T: IntoIterator, { Self { request: connection.transfer_users(TransferReq { @@ -205,7 +207,7 @@ impl TransferUserTab { } } -impl Display for user::Item { +impl Display for common_user::Item { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { write!(formatter, "{} ({})", self.name, self.id) } diff --git a/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java b/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java index 28af2491..3a352a90 100644 --- a/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java +++ b/clients/java/api/src/main/java/io/atomic/cloud/api/resource/Resources.java @@ -2,8 +2,8 @@ import io.atomic.cloud.api.resource.simple.SimpleCloudGroup; import io.atomic.cloud.api.resource.simple.SimpleCloudServer; -import java.util.Collection; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.CompletableFuture; /** The Resources interface provides methods to access cloud groups and cloud servers. */ @@ -30,7 +30,7 @@ public interface Resources { * @param uuid the uuid of the user to retrieve the group for * @return a SimpleGroup instance */ - CompletableFuture> groupFromUser(String uuid); + CompletableFuture> groupFromUser(UUID uuid); /** * Retrieves an array of SimpleServer objects. @@ -40,15 +40,12 @@ public interface Resources { CompletableFuture servers(); /** - * Retrieves an array of SimpleServer objects that match the specified name. - *

- * NOTE: It is possible that multiple servers have the same name. - * Because the controller currently on imposes no uniqueness constraints, this method may return multiple servers. + * Retrieves a SimpleServer object that matches the specified name. * - * @param name the name of the servers to retrieve - * @return a collection of SimpleServer instances + * @param name the name of the server to retrieve + * @return a SimpleServer instance */ - CompletableFuture> serverFromName(String name); + CompletableFuture> serverFromName(String name); /** * Retrieves a SimpleServer object that matches the specified uuid. @@ -56,7 +53,7 @@ public interface Resources { * @param uuid the uuid of the server to retrieve * @return a SimpleServer instance */ - CompletableFuture> serverFromUuid(String uuid); + CompletableFuture> serverFromUuid(UUID uuid); /** * Retrieves a SimpleServer object that contains the specified user as a member. @@ -64,5 +61,5 @@ public interface Resources { * @param uuid the uuid of the user to retrieve the server for * @return a SimpleServer instance */ - CompletableFuture> serverFromUser(String uuid); + CompletableFuture> serverFromUser(UUID uuid); } diff --git a/clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java b/clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java index 5d21220c..512276c9 100644 --- a/clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java +++ b/clients/java/api/src/main/java/io/atomic/cloud/api/user/CloudUser.java @@ -1,7 +1,5 @@ package io.atomic.cloud.api.user; -import io.atomic.cloud.api.resource.simple.SimpleCloudGroup; -import io.atomic.cloud.api.resource.simple.SimpleCloudServer; import java.util.Optional; import java.util.UUID; @@ -11,7 +9,7 @@ public interface CloudUser { UUID uuid(); - Optional group(); + Optional group(); - Optional server(); + Optional server(); } diff --git a/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java b/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java index 8fe280db..29d6f1c7 100644 --- a/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java +++ b/clients/java/api/src/main/java/io/atomic/cloud/api/user/Users.java @@ -1,5 +1,6 @@ package io.atomic.cloud.api.user; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -19,7 +20,7 @@ public interface Users { * @param name the uuid of the server to retrieve * @return a User instance */ - CompletableFuture userFromName(String name); + CompletableFuture> userFromName(String name); /** * Retrieves a User object that matches the specified uuid. @@ -27,5 +28,5 @@ public interface Users { * @param uuid the uuid of the user to retrieve * @return a User instance */ - CompletableFuture userFromUuid(UUID uuid); + CompletableFuture> userFromUuid(UUID uuid); } diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java index f165322a..57208d8a 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ClientConnection.java @@ -9,9 +9,8 @@ import io.atomic.cloud.common.connection.call.CallHandle; import io.atomic.cloud.common.connection.credential.TokenCredential; import io.atomic.cloud.grpc.client.*; -import io.atomic.cloud.grpc.common.Group; -import io.atomic.cloud.grpc.common.Notify; -import io.atomic.cloud.grpc.common.Server; +import io.atomic.cloud.grpc.client.User; +import io.atomic.cloud.grpc.common.*; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.net.MalformedURLException; @@ -32,8 +31,9 @@ public class ClientConnection extends Connection { private final CachedObject userCount = new CachedObject<>(); private final CachedObject protocolVersion = new CachedObject<>(); private final CachedObject controllerVersion = new CachedObject<>(); - private final CachedObject serversInfo = new CachedObject<>(); - private final CachedObject groupsInfo = new CachedObject<>(); + private final CachedObject serversInfo = new CachedObject<>(); + private final CachedObject groupsInfo = new CachedObject<>(); + private final CachedObject usersInfo = new CachedObject<>(); public ClientConnection(URL address, String token, String certificate) { super(address, token, certificate); @@ -79,6 +79,43 @@ public CompletableFuture publishMessage(Channel.Msg message) { return super.wrapInFuture(this.futureClient.publishMessage(message)); } + public CompletableFuture user(String server) { + return super.wrapInFuture(this.futureClient.getUser(StringValue.of(server))); + } + + public CompletableFuture userFromName(String server) { + return super.wrapInFuture(this.futureClient.getUserFromName(StringValue.of(server))); + } + + public CompletableFuture group(String group) { + return super.wrapInFuture(this.futureClient.getGroup(StringValue.of(group))); + } + + public CompletableFuture server(String server) { + return super.wrapInFuture(this.futureClient.getServer(StringValue.of(server))); + } + + public CompletableFuture serverFromName(String server) { + return super.wrapInFuture(this.futureClient.getServerFromName(StringValue.of(server))); + } + + public synchronized Optional usersNow() { + var cached = this.usersInfo.getValue(); + if (cached.isEmpty()) { + this.users(); // Request value from controller + } + return cached; + } + + public synchronized CompletableFuture users() { + return this.usersInfo.getValue().map(CompletableFuture::completedFuture).orElseGet(() -> super.wrapInFuture( + this.futureClient.getUsers(Empty.getDefaultInstance())) + .thenApply((value) -> { + this.usersInfo.setValue(value); + return value; + })); + } + public synchronized Optional userCountNow() { var cached = this.userCount.getValue(); if (cached.isEmpty()) { @@ -96,7 +133,7 @@ public synchronized CompletableFuture userCount() { })); } - public synchronized Optional serversNow() { + public synchronized Optional serversNow() { var cached = this.serversInfo.getValue(); if (cached.isEmpty()) { this.servers(); // Request value from controller @@ -104,7 +141,7 @@ public synchronized Optional serversNow() { return cached; } - public synchronized CompletableFuture servers() { + public synchronized CompletableFuture servers() { return this.serversInfo .getValue() .map(CompletableFuture::completedFuture) @@ -115,7 +152,7 @@ public synchronized CompletableFuture servers() { })); } - public synchronized Optional groupsNow() { + public synchronized Optional groupsNow() { var cached = this.groupsInfo.getValue(); if (cached.isEmpty()) { this.groups(); // Request value from controller @@ -123,7 +160,7 @@ public synchronized Optional groupsNow() { return cached; } - public synchronized CompletableFuture groups() { + public synchronized CompletableFuture groups() { return this.groupsInfo .getValue() .map(CompletableFuture::completedFuture) diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java index 3800b741..3cf46c72 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/connection/client/ManageConnection.java @@ -7,6 +7,9 @@ import io.atomic.cloud.common.connection.Connection; import io.atomic.cloud.common.connection.call.CallHandle; import io.atomic.cloud.common.connection.credential.TokenCredential; +import io.atomic.cloud.grpc.common.CommonGroup; +import io.atomic.cloud.grpc.common.CommonServer; +import io.atomic.cloud.grpc.common.CommonUser; import io.atomic.cloud.grpc.common.Notify; import io.atomic.cloud.grpc.manage.*; import io.grpc.stub.StreamObserver; @@ -23,13 +26,14 @@ public class ManageConnection extends Connection { private ManageServiceGrpc.ManageServiceFutureStub futureClient; // Cache values + private final CachedObject userCount = new CachedObject<>(); private final CachedObject protocolVersion = new CachedObject<>(); private final CachedObject controllerVersion = new CachedObject<>(); private final CachedObject pluginsInfo = new CachedObject<>(); private final CachedObject nodesInfo = new CachedObject<>(); - private final CachedObject groupsInfo = new CachedObject<>(); - private final CachedObject serversInfo = new CachedObject<>(); - private final CachedObject usersInfo = new CachedObject<>(); + private final CachedObject groupsInfo = new CachedObject<>(); + private final CachedObject serversInfo = new CachedObject<>(); + private final CachedObject usersInfo = new CachedObject<>(); public ManageConnection(URL address, String token, String certificate) { super(address, token, certificate); @@ -87,6 +91,35 @@ public CompletableFuture server(String server) { return super.wrapInFuture(this.futureClient.getServer(StringValue.of(server))); } + public CompletableFuture serverFromName(String server) { + return super.wrapInFuture(this.futureClient.getServerFromName(StringValue.of(server))); + } + + public CompletableFuture user(String server) { + return super.wrapInFuture(this.futureClient.getUser(StringValue.of(server))); + } + + public CompletableFuture userFromName(String server) { + return super.wrapInFuture(this.futureClient.getUserFromName(StringValue.of(server))); + } + + public synchronized Optional userCountNow() { + var cached = this.userCount.getValue(); + if (cached.isEmpty()) { + this.userCount(); // Request value from controller + } + return cached; + } + + public synchronized CompletableFuture userCount() { + return this.userCount.getValue().map(CompletableFuture::completedFuture).orElseGet(() -> super.wrapInFuture( + this.futureClient.getUserCount(Empty.getDefaultInstance())) + .thenApply((value) -> { + this.userCount.setValue(value); + return value; + })); + } + public synchronized Optional pluginsNow() { var cached = this.pluginsInfo.getValue(); if (cached.isEmpty()) { @@ -123,7 +156,7 @@ public synchronized CompletableFuture nodes() { })); } - public synchronized Optional groupsNow() { + public synchronized Optional groupsNow() { var cached = this.groupsInfo.getValue(); if (cached.isEmpty()) { this.groups(); // Request value from controller @@ -131,7 +164,7 @@ public synchronized Optional groupsNow() { return cached; } - public synchronized CompletableFuture groups() { + public synchronized CompletableFuture groups() { return this.groupsInfo .getValue() .map(CompletableFuture::completedFuture) @@ -142,7 +175,7 @@ public synchronized CompletableFuture groups() { })); } - public synchronized Optional serversNow() { + public synchronized Optional serversNow() { var cached = this.serversInfo.getValue(); if (cached.isEmpty()) { this.servers(); // Request value from controller @@ -150,7 +183,7 @@ public synchronized Optional serversNow() { return cached; } - public synchronized CompletableFuture servers() { + public synchronized CompletableFuture servers() { return this.serversInfo .getValue() .map(CompletableFuture::completedFuture) @@ -161,7 +194,7 @@ public synchronized CompletableFuture servers() { })); } - public synchronized Optional usersNow() { + public synchronized Optional usersNow() { var cached = this.usersInfo.getValue(); if (cached.isEmpty()) { this.users(); // Request value from controller @@ -169,7 +202,7 @@ public synchronized Optional usersNow() { return cached; } - public synchronized CompletableFuture users() { + public synchronized CompletableFuture users() { return this.usersInfo.getValue().map(CompletableFuture::completedFuture).orElseGet(() -> super.wrapInFuture( this.futureClient.getUsers(Empty.getDefaultInstance())) .thenApply((value) -> { diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java b/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java index 2fb2c870..6cc62a37 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/resource/ResourceManager.java @@ -1,17 +1,17 @@ package io.atomic.cloud.common.resource; +import io.atomic.cloud.api.Cloud; import io.atomic.cloud.api.resource.Resources; import io.atomic.cloud.api.resource.simple.SimpleCloudGroup; import io.atomic.cloud.api.resource.simple.SimpleCloudServer; import io.atomic.cloud.common.connection.client.ClientConnection; import io.atomic.cloud.common.resource.object.simple.SimpleCloudGroupImpl; import io.atomic.cloud.common.resource.object.simple.SimpleCloudServerImpl; - -import java.util.Collection; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; +import org.jetbrains.annotations.NotNull; @AllArgsConstructor public class ResourceManager implements Resources { @@ -21,18 +21,33 @@ public class ResourceManager implements Resources { @Override public CompletableFuture groups() { return this.connection.groups().thenApply(list -> list.getGroupsList().stream() - .map(SimpleCloudGroupImpl::new) + .map(group -> new SimpleCloudGroupImpl(group.getName())) .toArray(SimpleCloudGroupImpl[]::new)); } @Override public CompletableFuture> groupFromName(String name) { - return null; + return this.connection + .group(name) + .thenApply(group -> Optional.of((SimpleCloudGroup) new SimpleCloudGroupImpl(group.getName()))) + .exceptionally(throwable -> { + if (throwable.getMessage().equals("NOT_FOUND")) { + return Optional.empty(); + } else { + throw new RuntimeException(throwable); + } + }); } @Override - public CompletableFuture> groupFromUser(String uuid) { - return null; + public CompletableFuture> groupFromUser(UUID uuid) { + return Cloud.users().userFromUuid(uuid).thenCompose(user -> { + if (user.isPresent() && user.get().group().isPresent()) { + return this.groupFromName(user.get().group().get()); + } else { + return CompletableFuture.completedFuture(Optional.empty()); + } + }); } @Override @@ -47,18 +62,55 @@ public CompletableFuture servers() { } @Override - public CompletableFuture> serverFromName(String name) { - return null; + public CompletableFuture> serverFromName(String name) { + return this.connection + .serverFromName(name) + .thenApply(server -> { + Optional group = Optional.empty(); + if (server.hasGroup()) { + group = Optional.of(server.getGroup()); + } + return Optional.of((SimpleCloudServer) new SimpleCloudServerImpl( + server.getName(), UUID.fromString(server.getId()), group, server.getNode())); + }) + .exceptionally(throwable -> { + if (throwable.getMessage().equals("NOT_FOUND")) { + return Optional.empty(); + } else { + throw new RuntimeException(throwable); + } + }); } @Override - public CompletableFuture> serverFromUuid(String uuid) { - return null; + public CompletableFuture> serverFromUuid(@NotNull UUID uuid) { + return this.connection + .server(uuid.toString()) + .thenApply(server -> { + Optional group = Optional.empty(); + if (server.hasGroup()) { + group = Optional.of(server.getGroup()); + } + return Optional.of((SimpleCloudServer) + new SimpleCloudServerImpl(server.getName(), uuid, group, server.getNode())); + }) + .exceptionally(throwable -> { + if (throwable.getMessage().equals("NOT_FOUND")) { + return Optional.empty(); + } else { + throw new RuntimeException(throwable); + } + }); } @Override - public CompletableFuture> serverFromUser(String uuid) { - return null; + public CompletableFuture> serverFromUser(UUID uuid) { + return Cloud.users().userFromUuid(uuid).thenCompose(user -> { + if (user.isPresent() && user.get().server().isPresent()) { + return this.serverFromUuid(user.get().server().get()); + } else { + return CompletableFuture.completedFuture(Optional.empty()); + } + }); } - } diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/user/CloudUserImpl.java b/clients/java/common/src/main/java/io/atomic/cloud/common/user/CloudUserImpl.java new file mode 100644 index 00000000..3130c6c5 --- /dev/null +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/user/CloudUserImpl.java @@ -0,0 +1,8 @@ +package io.atomic.cloud.common.user; + +import io.atomic.cloud.api.user.CloudUser; +import java.util.Optional; +import java.util.UUID; + +public record CloudUserImpl(String name, UUID uuid, Optional group, Optional server) + implements CloudUser {} diff --git a/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java b/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java index 2162b4d9..8f20a258 100644 --- a/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java +++ b/clients/java/common/src/main/java/io/atomic/cloud/common/user/UserManager.java @@ -4,10 +4,11 @@ import io.atomic.cloud.api.user.CloudUser; import io.atomic.cloud.api.user.Users; import io.atomic.cloud.common.connection.client.ClientConnection; - +import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import lombok.AllArgsConstructor; +import org.jetbrains.annotations.NotNull; @AllArgsConstructor public class UserManager implements Users { @@ -20,13 +21,51 @@ public CompletableFuture userCount() { } @Override - public CompletableFuture userFromName(String name) { - return null; + public CompletableFuture> userFromName(String name) { + return this.connection + .userFromName(name) + .thenApply(user -> { + Optional group = Optional.empty(); + Optional server = Optional.empty(); + if (user.hasGroup()) { + group = Optional.of(user.getGroup()); + } + if (user.hasServer()) { + server = Optional.of(UUID.fromString(user.getServer())); + } + return Optional.of((CloudUser) + new CloudUserImpl(user.getName(), UUID.fromString(user.getId()), group, server)); + }) + .exceptionally(throwable -> { + if (throwable.getMessage().equals("NOT_FOUND")) { + return Optional.empty(); + } else { + throw new RuntimeException(throwable); + } + }); } @Override - public CompletableFuture userFromUuid(UUID uuid) { - return null; + public CompletableFuture> userFromUuid(@NotNull UUID uuid) { + return this.connection + .user(uuid.toString()) + .thenApply(user -> { + Optional group = Optional.empty(); + Optional server = Optional.empty(); + if (user.hasGroup()) { + group = Optional.of(user.getGroup()); + } + if (user.hasServer()) { + server = Optional.of(UUID.fromString(user.getServer())); + } + return Optional.of((CloudUser) new CloudUserImpl(user.getName(), uuid, group, server)); + }) + .exceptionally(throwable -> { + if (throwable.getMessage().equals("NOT_FOUND")) { + return Optional.empty(); + } else { + throw new RuntimeException(throwable); + } + }); } - } diff --git a/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java b/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java index 7653dcef..19c00129 100644 --- a/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java +++ b/clients/java/paper/send/src/main/java/io/atomic/cloud/paper/send/command/argument/TransferTargetArgument.java @@ -7,9 +7,9 @@ import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import io.atomic.cloud.grpc.client.Group; -import io.atomic.cloud.grpc.client.Server; import io.atomic.cloud.grpc.client.Transfer; +import io.atomic.cloud.grpc.common.CommonGroup; +import io.atomic.cloud.grpc.common.CommonServer; import io.atomic.cloud.paper.CloudPlugin; import io.papermc.paper.command.brigadier.MessageComponentSerializer; import io.papermc.paper.command.brigadier.argument.CustomArgumentType; @@ -53,12 +53,12 @@ public class TransferTargetArgument implements CustomArgumentType.Converted item.equalsIgnoreCase(identifier)) + .filter(item -> item.getName().equalsIgnoreCase(identifier)) .findFirst(); if (group.isEmpty()) throw createException("\"" + identifier + "\" does not exist"); return Transfer.Target.newBuilder() .setType(Transfer.Target.Type.GROUP) - .setTarget(group.get()) + .setTarget(group.get().getName()) .build(); } throw createException("Unknown transfer target type: " + type); @@ -72,7 +72,7 @@ public class TransferTargetArgument implements CustomArgumentType.Converted { - response. + response.servers .getServersList() .forEach(server -> builder.suggest( "server:" + server.getName(), @@ -82,9 +82,10 @@ public class TransferTargetArgument implements CustomArgumentType.Converted builder.suggest( - "group:" + group, + "group:" + group.getName(), MessageComponentSerializer.message() - .serialize(Component.text(group).color(NamedTextColor.BLUE)))); + .serialize(Component.text(group.getName()) + .color(NamedTextColor.BLUE)))); builder.suggest( "fallback", MessageComponentSerializer.message() @@ -105,5 +106,5 @@ public class TransferTargetArgument implements CustomArgumentType.Converted message), () -> message); } - private record SuggestionsData(Server.List servers, Group.List groups) {} + private record SuggestionsData(CommonServer.List servers, CommonGroup.List groups) {} } diff --git a/controller/src/application/server/manager.rs b/controller/src/application/server/manager.rs index 745fadff..b10df8c8 100644 --- a/controller/src/application/server/manager.rs +++ b/controller/src/application/server/manager.rs @@ -70,6 +70,12 @@ impl ServerManager { self.servers.values().collect() } + pub fn get_server_from_name(&self, name: &str) -> Option<&Server> { + self.servers + .values() + .find(|server| server.id().name() == name) + } + pub fn get_server(&self, uuid: &Uuid) -> Option<&Server> { self.servers.get(uuid) } diff --git a/controller/src/application/user.rs b/controller/src/application/user.rs index 5949eb2e..a6fb9d66 100644 --- a/controller/src/application/user.rs +++ b/controller/src/application/user.rs @@ -10,6 +10,7 @@ pub mod transfer; pub struct User { #[getset(get = "pub")] id: NameAndUuid, + #[getset(get = "pub")] server: CurrentServer, } diff --git a/controller/src/application/user/manager.rs b/controller/src/application/user/manager.rs index 042adc18..64489c88 100644 --- a/controller/src/application/user/manager.rs +++ b/controller/src/application/user/manager.rs @@ -120,7 +120,11 @@ impl UserManager { self.users.values().collect() } - pub fn _get_user(&self, uuid: &Uuid) -> Option<&User> { + pub fn get_user_from_name(&self, name: &str) -> Option<&User> { + self.users.values().find(|user| user.id().name() == name) + } + + pub fn get_user(&self, uuid: &Uuid) -> Option<&User> { self.users.get(uuid) } pub fn get_user_mut(&mut self, uuid: &Uuid) -> Option<&mut User> { diff --git a/controller/src/network/client.rs b/controller/src/network/client.rs index 46665ee7..2a69f807 100644 --- a/controller/src/network/client.rs +++ b/controller/src/network/client.rs @@ -2,13 +2,16 @@ use std::{str::FromStr, sync::Arc}; use anyhow::Result; use beat::BeatTask; -use group::GetGroupsTask; +use group::{GetGroupTask, GetGroupsTask}; use health::{RequestStopTask, SetRunningTask}; use ready::SetReadyTask; -use server::GetServersTask; +use server::{GetServerFromNameTask, GetServerTask, GetServersTask}; use tokio_stream::wrappers::ReceiverStream; use tonic::{async_trait, Request, Response, Status}; -use user::{UserConnectedTask, UserCountTask, UserDisconnectedTask}; +use user::{ + GetUserFromNameTask, GetUserTask, GetUsersTask, UserConnectedTask, UserCountTask, + UserDisconnectedTask, +}; use uuid::Uuid; use crate::{ @@ -24,13 +27,15 @@ use super::{ manage::transfer::TransferUsersTask, proto::{ client::{ - self, channel::Msg, client_service_server::ClientService, transfer::{target::Type, TransferReq, TransferRes}, user::{ConnectedReq, DisconnectedReq}, }, - common::notify::{PowerEvent, ReadyEvent}, + common::{ + common_group, common_server, common_user, + notify::{PowerEvent, ReadyEvent}, + }, }, }; @@ -130,6 +135,54 @@ impl ClientService for ClientServiceImpl { .await?, )) } + async fn get_user( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::( + AuthType::Server, + &self.0, + request, + |request, _| { + let request = request.into_inner(); + + let Ok(uuid) = Uuid::from_str(&request) else { + return Err(Status::invalid_argument("Invalid UUID provided")); + }; + + Ok(Box::new(GetUserTask(uuid))) + }, + ) + .await?, + )) + } + async fn get_user_from_name( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::( + AuthType::Server, + &self.0, + request, + |request, _| { + let request = request.into_inner(); + + Ok(Box::new(GetUserFromNameTask(request))) + }, + ) + .await?, + )) + } + async fn get_users(&self, request: Request<()>) -> Result, Status> { + Ok(Response::new( + Task::execute::(AuthType::Server, &self.0, request, |_, _| { + Ok(Box::new(GetUsersTask)) + }) + .await?, + )) + } async fn get_user_count(&self, request: Request<()>) -> Result, Status> { Ok(Response::new( Task::execute::(AuthType::Server, &self.0, request, |_, _| { @@ -238,32 +291,90 @@ impl ClientService for ClientServiceImpl { } // Server + async fn get_server( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::( + AuthType::Server, + &self.0, + request, + |request, _| { + let request = request.into_inner(); + + let Ok(uuid) = Uuid::parse_str(&request) else { + return Err(Status::invalid_argument("Invalid UUID provided")); + }; + + Ok(Box::new(GetServerTask(uuid))) + }, + ) + .await?, + )) + } + async fn get_server_from_name( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::( + AuthType::Server, + &self.0, + request, + |request, _| { + let request = request.into_inner(); + + Ok(Box::new(GetServerFromNameTask(request))) + }, + ) + .await?, + )) + } async fn get_servers( &self, request: Request<()>, - ) -> Result, Status> { + ) -> Result, Status> { Ok(Response::new( - Task::execute::( + Task::execute::( AuthType::Server, &self.0, request, - |_, _| Ok(Box::new(GetServersTask())), + |_, _| Ok(Box::new(GetServersTask)), ) .await?, )) } // Group + async fn get_group( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::( + AuthType::Server, + &self.0, + request, + |request, _| { + let request = request.into_inner(); + + Ok(Box::new(GetGroupTask(request))) + }, + ) + .await?, + )) + } async fn get_groups( &self, request: Request<()>, - ) -> Result, Status> { + ) -> Result, Status> { Ok(Response::new( - Task::execute::( + Task::execute::( AuthType::Server, &self.0, request, - |_, _| Ok(Box::new(GetGroupsTask())), + |_, _| Ok(Box::new(GetGroupsTask)), ) .await?, )) diff --git a/controller/src/network/client/group.rs b/controller/src/network/client/group.rs index 77c46fbd..51a24632 100644 --- a/controller/src/network/client/group.rs +++ b/controller/src/network/client/group.rs @@ -1,12 +1,25 @@ use anyhow::Result; -use tonic::async_trait; +use tonic::{async_trait, Status}; use crate::{ application::{group::Group, Controller}, + network::proto::common::common_group::{List, Short}, task::{BoxedAny, GenericTask, Task}, }; -pub struct GetGroupsTask(); +pub struct GetGroupTask(pub String); +pub struct GetGroupsTask; + +#[async_trait] +impl GenericTask for GetGroupTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(group) = controller.groups.get_group(&self.0) else { + return Task::new_err(Status::not_found("Group not found")); + }; + + Task::new_ok(Short::from(&group)) + } +} #[async_trait] impl GenericTask for GetGroupsTask { @@ -16,7 +29,7 @@ impl GenericTask for GetGroupsTask { .groups .get_groups() .iter() - .map(|group| group.name().clone()) + .map(Into::into) .collect(), }) } diff --git a/controller/src/network/client/server.rs b/controller/src/network/client/server.rs index 6ec06fb1..425ec993 100644 --- a/controller/src/network/client/server.rs +++ b/controller/src/network/client/server.rs @@ -1,13 +1,38 @@ use anyhow::Result; -use tonic::async_trait; +use tonic::{async_trait, Status}; +use uuid::Uuid; use crate::{ application::{server::Server, Controller}, - network::proto::client::server::{List, Short}, + network::proto::common::common_server::{List, Short}, task::{BoxedAny, GenericTask, Task}, }; -pub struct GetServersTask(); +pub struct GetServerTask(pub Uuid); +pub struct GetServerFromNameTask(pub String); +pub struct GetServersTask; + +#[async_trait] +impl GenericTask for GetServerTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(server) = controller.servers.get_server(&self.0) else { + return Task::new_err(Status::not_found("Server not found")); + }; + + Task::new_ok(Short::from(&server)) + } +} + +#[async_trait] +impl GenericTask for GetServerFromNameTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(server) = controller.servers.get_server_from_name(&self.0) else { + return Task::new_err(Status::not_found("Server not found")); + }; + + Task::new_ok(Short::from(&server)) + } +} #[async_trait] impl GenericTask for GetServersTask { @@ -17,7 +42,7 @@ impl GenericTask for GetServersTask { .servers .get_servers() .iter() - .map(std::convert::Into::into) + .map(Into::into) .collect(), }) } diff --git a/controller/src/network/client/user.rs b/controller/src/network/client/user.rs index 329a5c14..84794066 100644 --- a/controller/src/network/client/user.rs +++ b/controller/src/network/client/user.rs @@ -1,18 +1,23 @@ use anyhow::Result; -use tonic::async_trait; +use tonic::{async_trait, Status}; use uuid::Uuid; use crate::{ application::{ auth::{ActionResult, Authorization}, server::NameAndUuid, + user::CurrentServer, Controller, }, + network::proto::common::common_user::{Item, List}, task::{BoxedAny, GenericTask, Task}, }; pub struct UserConnectedTask(pub Authorization, pub NameAndUuid); pub struct UserDisconnectedTask(pub Authorization, pub Uuid); +pub struct GetUserTask(pub Uuid); +pub struct GetUserFromNameTask(pub String); +pub struct GetUsersTask; pub struct UserCountTask; #[async_trait] @@ -47,6 +52,97 @@ impl GenericTask for UserDisconnectedTask { } } +#[async_trait] +impl GenericTask for GetUserTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(user) = controller.users.get_user(&self.0) else { + return Task::new_err(Status::not_found("User not found")); + }; + + let (server, group) = if let CurrentServer::Connected(server) = user.server() { + let server = server.uuid(); + ( + Some(server.to_string()), + controller + .servers + .get_server(server) + .and_then(|server| server.group().clone()), + ) + } else { + (None, None) + }; + Task::new_ok(Item { + name: user.id().name().clone(), + id: user.id().uuid().to_string(), + group, + server, + }) + } +} + +#[async_trait] +impl GenericTask for GetUserFromNameTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(user) = controller.users.get_user_from_name(&self.0) else { + return Task::new_err(Status::not_found("User not found")); + }; + + let (server, group) = if let CurrentServer::Connected(server) = user.server() { + let server = server.uuid(); + ( + Some(server.to_string()), + controller + .servers + .get_server(server) + .and_then(|server| server.group().clone()), + ) + } else { + (None, None) + }; + Task::new_ok(Item { + name: user.id().name().clone(), + id: user.id().uuid().to_string(), + group, + server, + }) + } +} + +// TODO: This call is very expensive +// TODO: Remove or find a different solution +#[async_trait] +impl GenericTask for GetUsersTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + Task::new_ok(List { + users: controller + .users + .get_users() + .iter() + .map(|user| { + let (server, group) = if let CurrentServer::Connected(server) = user.server() { + let server = server.uuid(); + ( + Some(server.to_string()), + controller + .servers + .get_server(server) + .and_then(|server| server.group().clone()), + ) + } else { + (None, None) + }; + Item { + name: user.id().name().clone(), + id: user.id().uuid().to_string(), + group, + server, + } + }) + .collect(), + }) + } +} + #[async_trait] impl GenericTask for UserCountTask { async fn run(&mut self, controller: &mut Controller) -> Result { diff --git a/controller/src/network/manage.rs b/controller/src/network/manage.rs index e36c33e9..6fcdafac 100644 --- a/controller/src/network/manage.rs +++ b/controller/src/network/manage.rs @@ -6,11 +6,11 @@ use node::{CreateNodeTask, GetNodeTask, GetNodesTask, UpdateNodeTask}; use plugin::GetPluginsTask; use power::RequestStopTask; use resource::{DeleteResourceTask, SetResourceTask}; -use server::{GetServerTask, GetServersTask, ScheduleServerTask}; +use server::{GetServerFromNameTask, GetServerTask, GetServersTask, ScheduleServerTask}; use tokio_stream::wrappers::ReceiverStream; use tonic::{async_trait, Request, Response, Status}; use transfer::TransferUsersTask; -use user::GetUsersTask; +use user::{GetUserFromNameTask, GetUserTask, GetUsersTask, UserCountTask}; use uuid::Uuid; use crate::{ @@ -28,7 +28,10 @@ use crate::{ }; use super::proto::{ - common::notify::{PowerEvent, ReadyEvent}, + common::{ + common_group, common_server, common_user, + notify::{PowerEvent, ReadyEvent}, + }, manage::{ self, manage_service_server::ManageService, @@ -435,11 +438,7 @@ impl ManageService for ManageServiceImpl { AuthType::User, &self.0, request, - |request, _| { - let request = request.into_inner(); - - Ok(Box::new(GetGroupTask(request))) - }, + |request, _| Ok(Box::new(GetGroupTask(request.into_inner()))), ) .await?, )) @@ -447,10 +446,10 @@ impl ManageService for ManageServiceImpl { async fn get_groups( &self, request: Request<()>, - ) -> Result, Status> { + ) -> Result, Status> { Ok(Response::new( - Task::execute::(AuthType::User, &self.0, request, |_, _| { - Ok(Box::new(GetGroupsTask())) + Task::execute::(AuthType::User, &self.0, request, |_, _| { + Ok(Box::new(GetGroupsTask)) }) .await?, )) @@ -558,20 +557,35 @@ impl ManageService for ManageServiceImpl { .await?, )) } - async fn get_servers( + async fn get_server_from_name( &self, - request: Request<()>, - ) -> Result, Status> { + request: Request, + ) -> Result, Status> { Ok(Response::new( - Task::execute::( + Task::execute::( AuthType::User, &self.0, request, - |_, _| Ok(Box::new(GetServersTask())), + |request, _| { + let request = request.into_inner(); + + Ok(Box::new(GetServerFromNameTask(request))) + }, ) .await?, )) } + async fn get_servers( + &self, + request: Request<()>, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::(AuthType::User, &self.0, request, |_, _| { + Ok(Box::new(GetServersTask)) + }) + .await?, + )) + } // Screen async fn write_to_screen(&self, request: Request) -> Result, Status> { @@ -590,7 +604,9 @@ impl ManageService for ManageServiceImpl { &self, request: Request, ) -> Result, Status> { - let Ok(uuid) = Uuid::from_str(&request.into_inner()) else { + let request = request.into_inner(); + + let Ok(uuid) = Uuid::from_str(&request) else { return Err(Status::invalid_argument("Invalid UUID provided")); }; @@ -598,13 +614,58 @@ impl ManageService for ManageServiceImpl { } // User - async fn get_users( + async fn get_user( &self, - request: Request<()>, - ) -> Result, Status> { + request: Request, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::( + AuthType::User, + &self.0, + request, + |request, _| { + let request = request.into_inner(); + + let Ok(uuid) = Uuid::from_str(&request) else { + return Err(Status::invalid_argument("Invalid UUID provided")); + }; + + Ok(Box::new(GetUserTask(uuid))) + }, + ) + .await?, + )) + } + async fn get_user_from_name( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new( + Task::execute::( + AuthType::User, + &self.0, + request, + |request, _| { + let request = request.into_inner(); + + Ok(Box::new(GetUserFromNameTask(request))) + }, + ) + .await?, + )) + } + async fn get_users(&self, request: Request<()>) -> Result, Status> { + Ok(Response::new( + Task::execute::(AuthType::User, &self.0, request, |_, _| { + Ok(Box::new(GetUsersTask)) + }) + .await?, + )) + } + async fn get_user_count(&self, request: Request<()>) -> Result, Status> { Ok(Response::new( - Task::execute::(AuthType::User, &self.0, request, |_, _| { - Ok(Box::new(GetUsersTask())) + Task::execute::(AuthType::User, &self.0, request, |_, _| { + Ok(Box::new(UserCountTask)) }) .await?, )) diff --git a/controller/src/network/manage/group.rs b/controller/src/network/manage/group.rs index 50ce8241..7d3054ac 100644 --- a/controller/src/network/manage/group.rs +++ b/controller/src/network/manage/group.rs @@ -8,7 +8,7 @@ use crate::{ Controller, }, network::proto::{ - common::KeyValue, + common::{common_group::List, KeyValue}, manage::{ group::{Constraints, Detail, Scaling}, server::{self, Fallback}, @@ -34,6 +34,7 @@ pub struct UpdateGroupTask( pub Option>, ); pub struct GetGroupTask(pub String); +pub struct GetGroupsTask; #[async_trait] impl GenericTask for CreateGroupTask { @@ -90,6 +91,20 @@ impl GenericTask for GetGroupTask { } } +#[async_trait] +impl GenericTask for GetGroupsTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + Task::new_ok(List { + groups: controller + .groups + .get_groups() + .iter() + .map(Into::into) + .collect(), + }) + } +} + impl From<&Group> for Detail { fn from(value: &Group) -> Self { Self { diff --git a/controller/src/network/manage/server.rs b/controller/src/network/manage/server.rs index 066eeeb2..5eac5d17 100644 --- a/controller/src/network/manage/server.rs +++ b/controller/src/network/manage/server.rs @@ -10,8 +10,8 @@ use crate::{ Controller, }, network::proto::{ - common::Address, - manage::server::{self, Detail, List, Short}, + common::{common_server::List, Address}, + manage::server::{self, Detail}, }, task::{BoxedAny, GenericTask, Task}, }; @@ -24,7 +24,8 @@ pub struct ScheduleServerTask( pub Specification, ); pub struct GetServerTask(pub Uuid); -pub struct GetServersTask(); +pub struct GetServerFromNameTask(pub String); +pub struct GetServersTask; #[async_trait] impl GenericTask for ScheduleServerTask { @@ -60,6 +61,17 @@ impl GenericTask for GetServerTask { } } +#[async_trait] +impl GenericTask for GetServerFromNameTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(server) = controller.servers.get_server_from_name(&self.0) else { + return Task::new_err(Status::not_found("Server not found")); + }; + + Task::new_ok(Detail::from(server)) + } +} + #[async_trait] impl GenericTask for GetServersTask { async fn run(&mut self, controller: &mut Controller) -> Result { @@ -74,17 +86,6 @@ impl GenericTask for GetServersTask { } } -impl From<&&Server> for Short { - fn from(server: &&Server) -> Self { - Self { - id: server.id().uuid().to_string(), - name: server.id().name().clone(), - group: server.group().clone(), - node: server.node().clone(), - } - } -} - impl From<&Server> for Detail { fn from(server: &Server) -> Self { Self { diff --git a/controller/src/network/manage/user.rs b/controller/src/network/manage/user.rs index ffe485a0..f9643dae 100644 --- a/controller/src/network/manage/user.rs +++ b/controller/src/network/manage/user.rs @@ -1,14 +1,76 @@ use anyhow::Result; -use tonic::async_trait; +use tonic::{async_trait, Status}; +use uuid::Uuid; use crate::{ - application::{user::User, Controller}, - network::proto::manage::user::{Item, List}, + application::{user::CurrentServer, Controller}, + network::proto::common::common_user::{Item, List}, task::{BoxedAny, GenericTask, Task}, }; -pub struct GetUsersTask(); +pub struct GetUserTask(pub Uuid); +pub struct GetUserFromNameTask(pub String); +pub struct GetUsersTask; +pub struct UserCountTask; +#[async_trait] +impl GenericTask for GetUserTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(user) = controller.users.get_user(&self.0) else { + return Task::new_err(Status::not_found("User not found")); + }; + + let (server, group) = if let CurrentServer::Connected(server) = user.server() { + let server = server.uuid(); + ( + Some(server.to_string()), + controller + .servers + .get_server(server) + .and_then(|server| server.group().clone()), + ) + } else { + (None, None) + }; + Task::new_ok(Item { + name: user.id().name().clone(), + id: user.id().uuid().to_string(), + group, + server, + }) + } +} + +#[async_trait] +impl GenericTask for GetUserFromNameTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + let Some(user) = controller.users.get_user_from_name(&self.0) else { + return Task::new_err(Status::not_found("User not found")); + }; + + let (server, group) = if let CurrentServer::Connected(server) = user.server() { + let server = server.uuid(); + ( + Some(server.to_string()), + controller + .servers + .get_server(server) + .and_then(|server| server.group().clone()), + ) + } else { + (None, None) + }; + Task::new_ok(Item { + name: user.id().name().clone(), + id: user.id().uuid().to_string(), + group, + server, + }) + } +} + +// TODO: This call is very expensive +// TODO: Remove or find a different solution #[async_trait] impl GenericTask for GetUsersTask { async fn run(&mut self, controller: &mut Controller) -> Result { @@ -17,17 +79,34 @@ impl GenericTask for GetUsersTask { .users .get_users() .iter() - .map(Into::into) + .map(|user| { + let (server, group) = if let CurrentServer::Connected(server) = user.server() { + let server = server.uuid(); + ( + Some(server.to_string()), + controller + .servers + .get_server(server) + .and_then(|server| server.group().clone()), + ) + } else { + (None, None) + }; + Item { + name: user.id().name().clone(), + id: user.id().uuid().to_string(), + group, + server, + } + }) .collect(), }) } } -impl From<&&User> for Item { - fn from(user: &&User) -> Self { - Self { - id: user.id().uuid().to_string(), - name: user.id().name().clone(), - } +#[async_trait] +impl GenericTask for UserCountTask { + async fn run(&mut self, controller: &mut Controller) -> Result { + Task::new_ok(controller.users.get_user_count()) } } diff --git a/protocol/grpc/client/service.proto b/protocol/grpc/client/service.proto index 1cdb1853..25241389 100644 --- a/protocol/grpc/client/service.proto +++ b/protocol/grpc/client/service.proto @@ -33,7 +33,7 @@ service ClientService { rpc UserDisconnected(User.DisconnectedReq) returns (google.protobuf.Empty); /// Info rpc GetUser(google.protobuf.StringValue) returns (common.CommonUser.Item); - rpc GetUserFromId(google.protobuf.StringValue) returns (common.CommonUser.Item); + rpc GetUserFromName(google.protobuf.StringValue) returns (common.CommonUser.Item); rpc GetUsers(google.protobuf.Empty) returns (common.CommonUser.List); rpc GetUserCount(google.protobuf.Empty) returns (google.protobuf.UInt32Value); @@ -51,6 +51,7 @@ service ClientService { // Server operations rpc GetServer(google.protobuf.StringValue) returns (common.CommonServer.Short); + rpc GetServerFromName(google.protobuf.StringValue) returns (common.CommonServer.Short); rpc GetServers(google.protobuf.Empty) returns (common.CommonServer.List); // Version info diff --git a/protocol/grpc/manage/service.proto b/protocol/grpc/manage/service.proto index 082c6b8c..64b44655 100644 --- a/protocol/grpc/manage/service.proto +++ b/protocol/grpc/manage/service.proto @@ -46,6 +46,7 @@ service ManageService { // Server operations rpc ScheduleServer(Server.Proposal) returns (google.protobuf.StringValue); rpc GetServer(google.protobuf.StringValue) returns (Server.Detail); + rpc GetServerFromName(google.protobuf.StringValue) returns (Server.Detail); rpc GetServers(google.protobuf.Empty) returns (common.CommonServer.List); // Screen operations @@ -54,7 +55,7 @@ service ManageService { // User operations rpc GetUser(google.protobuf.StringValue) returns (common.CommonUser.Item); - rpc GetUserFromId(google.protobuf.StringValue) returns (common.CommonUser.Item); + rpc GetUserFromName(google.protobuf.StringValue) returns (common.CommonUser.Item); rpc GetUsers(google.protobuf.Empty) returns (common.CommonUser.List); rpc GetUserCount(google.protobuf.Empty) returns (google.protobuf.UInt32Value);