From 742dd750411291657ddee4cabbd6d2f6e4ab4aad Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 25 May 2022 17:42:25 -0600 Subject: [PATCH] Implement /rpc_server_snapshot endpoint This returns a JSON snapshot of the state of the server --- crates/collab/src/api.rs | 14 +++++++++++--- crates/collab/src/main.rs | 6 ++++++ crates/collab/src/rpc.rs | 26 ++++++++++++++++++++++++++ crates/collab/src/rpc/store.rs | 11 +++++++++-- crates/rpc/src/peer.rs | 18 ++++++++++++++++-- styles/src/styleTree/workspace.ts | 1 - 6 files changed, 68 insertions(+), 8 deletions(-) diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 67c56e78ee..2b22ca1c16 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,7 +1,7 @@ use crate::{ auth, db::{User, UserId}, - rpc::ResultExt, + rpc::{self, ResultExt}, AppState, Error, Result, }; use anyhow::anyhow; @@ -15,11 +15,12 @@ use axum::{ Extension, Json, Router, }; use serde::{Deserialize, Serialize}; +use serde_json::Value; use std::sync::Arc; use tower::ServiceBuilder; use tracing::instrument; -pub fn routes(rpc_server: &Arc, state: Arc) -> Router { +pub fn routes(rpc_server: &Arc, state: Arc) -> Router { Router::new() .route("/users", get(get_users).post(create_user)) .route( @@ -29,6 +30,7 @@ pub fn routes(rpc_server: &Arc, state: Arc) -> Rou .route("/users/:id/access_tokens", post(create_access_token)) .route("/invite_codes/:code", get(get_user_for_invite_code)) .route("/panic", post(trace_panic)) + .route("/rpc_server_snapshot", get(get_rpc_server_snapshot)) .layer( ServiceBuilder::new() .layer(Extension(state)) @@ -84,7 +86,7 @@ struct CreateUserParams { async fn create_user( Json(params): Json, Extension(app): Extension>, - Extension(rpc_server): Extension>, + Extension(rpc_server): Extension>, ) -> Result> { println!("{:?}", params); @@ -177,6 +179,12 @@ async fn trace_panic(panic: Json) -> Result<()> { Ok(()) } +async fn get_rpc_server_snapshot<'a>( + Extension(rpc_server): Extension>, +) -> Result> { + Ok(Json(serde_json::to_value(rpc_server.snapshot().await)?)) +} + #[derive(Deserialize)] struct CreateAccessTokenQueryParams { public_key: String, diff --git a/crates/collab/src/main.rs b/crates/collab/src/main.rs index 617f1f273f..74401699ca 100644 --- a/crates/collab/src/main.rs +++ b/crates/collab/src/main.rs @@ -104,6 +104,12 @@ impl From for Error { } } +impl From for Error { + fn from(error: serde_json::Error) -> Self { + Self::Internal(error.into()) + } +} + impl IntoResponse for Error { fn into_response(self) -> axum::response::Response { match self { diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index ae5f43e34b..7ea925fce7 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -33,6 +33,7 @@ use rpc::{ proto::{self, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, RequestMessage}, Connection, ConnectionId, Peer, Receipt, TypedEnvelope, }; +use serde::{Serialize, Serializer}; use std::{ any::TypeId, future::Future, @@ -85,6 +86,7 @@ pub struct Server { notifications: Option>, } + pub trait Executor: Send + Clone { type Sleep: Send + Future; fn spawn_detached>(&self, future: F); @@ -107,6 +109,23 @@ struct StoreWriteGuard<'a> { _not_send: PhantomData>, } +#[derive(Serialize)] +pub struct ServerSnapshot<'a> { + peer: &'a Peer, + #[serde(serialize_with = "serialize_deref")] + store: RwLockReadGuard<'a, Store>, +} + +pub fn serialize_deref(value: &T, serializer: S) -> Result +where + S: Serializer, + T: Deref, + U: Serialize +{ + Serialize::serialize(value.deref(), serializer) +} + + impl Server { pub fn new( app_state: Arc, @@ -1469,6 +1488,13 @@ impl Server { _not_send: PhantomData, } } + + pub async fn snapshot<'a>(self: &'a Arc) -> ServerSnapshot<'a> { + ServerSnapshot { + store: self.store.read().await, + peer: &self.peer + } + } } impl<'a> Deref for StoreReadGuard<'a> { diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 33fa1eb113..fe9d21879e 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -2,18 +2,21 @@ use crate::db::{self, ChannelId, UserId}; use anyhow::{anyhow, Result}; use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}; use rpc::{proto, ConnectionId, Receipt}; +use serde::Serialize; use std::{collections::hash_map, mem, path::PathBuf}; use tracing::instrument; -#[derive(Default)] +#[derive(Default, Serialize)] pub struct Store { connections: HashMap, connections_by_user_id: HashMap>, projects: HashMap, + #[serde(skip)] channels: HashMap, next_project_id: u64, } +#[derive(Serialize)] struct ConnectionState { user_id: UserId, projects: HashSet, @@ -21,21 +24,25 @@ struct ConnectionState { channels: HashSet, } +#[derive(Serialize)] pub struct Project { pub host_connection_id: ConnectionId, pub host_user_id: UserId, pub guests: HashMap, + #[serde(skip)] pub join_requests: HashMap>>, pub active_replica_ids: HashSet, pub worktrees: HashMap, pub language_servers: Vec, } -#[derive(Default)] +#[derive(Default, Serialize)] pub struct Worktree { pub root_name: String, pub visible: bool, + #[serde(skip)] pub entries: HashMap, + #[serde(skip)] pub diagnostic_summaries: BTreeMap, pub scan_id: u64, } diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 2fcc5dc09b..59abf3c8e3 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -10,6 +10,7 @@ use futures::{ FutureExt, SinkExt, StreamExt, }; use parking_lot::{Mutex, RwLock}; +use serde::{ser::SerializeStruct, Serialize}; use smol_timeout::TimeoutExt; use std::sync::atomic::Ordering::SeqCst; use std::{ @@ -24,7 +25,7 @@ use std::{ }; use tracing::instrument; -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)] pub struct ConnectionId(pub u32); impl fmt::Display for ConnectionId { @@ -89,10 +90,12 @@ pub struct Peer { next_connection_id: AtomicU32, } -#[derive(Clone)] +#[derive(Clone, Serialize)] pub struct ConnectionState { + #[serde(skip)] outgoing_tx: mpsc::UnboundedSender, next_message_id: Arc, + #[serde(skip)] response_channels: Arc)>>>>>, } @@ -471,6 +474,17 @@ impl Peer { } } +impl Serialize for Peer { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Peer", 2)?; + state.serialize_field("connections", &*self.connections.read())?; + state.end() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index b506b3f780..261656e62a 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -8,7 +8,6 @@ export function workspaceBackground(theme: Theme) { } export default function workspace(theme: Theme) { - const tab = { height: 32, background: workspaceBackground(theme),