Merge pull request #1068 from zed-industries/collab-snapshots

Implement /rpc_server_snapshot endpoint in collab server API
This commit is contained in:
Antonio Scandurra 2022-05-26 09:42:39 +02:00 committed by GitHub
commit f4d13ef596
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 109 additions and 28 deletions

30
Cargo.lock generated
View File

@ -416,6 +416,25 @@ dependencies = [
"mime",
]
[[package]]
name = "axum-extra"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75330529f6b27544cedc6089108602a056d016df6aa4f2cb24408d840392ef2d"
dependencies = [
"axum",
"bytes",
"http",
"mime",
"pin-project-lite 0.2.9",
"serde",
"serde_json",
"tower",
"tower-http",
"tower-layer",
"tower-service",
]
[[package]]
name = "backtrace"
version = "0.3.64"
@ -593,9 +612,9 @@ checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
[[package]]
name = "bytes"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cache-padded"
@ -841,6 +860,7 @@ dependencies = [
"async-trait",
"async-tungstenite",
"axum",
"axum-extra",
"base64 0.13.0",
"clap 3.1.12",
"client",
@ -4237,12 +4257,12 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.64"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
"indexmap",
"itoa 0.4.7",
"itoa 1.0.1",
"ryu",
"serde",
]

View File

@ -18,6 +18,6 @@ isahc = "1.7"
lazy_static = "1.4"
log = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
smol = "1.2.5"
tempdir = "0.3.7"

View File

@ -21,6 +21,7 @@ anyhow = "1.0.40"
async-trait = "0.1.50"
async-tungstenite = "0.16"
axum = { version = "0.5", features = ["json", "headers", "ws"] }
axum-extra = { version = "0.3", features = ["erased-json"] }
base64 = "0.13"
clap = { version = "3.1", features = ["derive"], optional = true }
envy = "0.4.2"
@ -70,7 +71,7 @@ ctor = "0.1"
env_logger = "0.8"
util = { path = "../util" }
lazy_static = "1.4"
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
[features]
seed-support = ["clap", "lipsum", "reqwest"]

View File

@ -1,7 +1,7 @@
use crate::{
auth,
db::{User, UserId},
rpc::ResultExt,
rpc::{self, ResultExt},
AppState, Error, Result,
};
use anyhow::anyhow;
@ -14,12 +14,13 @@ use axum::{
routing::{get, post, put},
Extension, Json, Router,
};
use axum_extra::response::ErasedJson;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tower::ServiceBuilder;
use tracing::instrument;
pub fn routes(rpc_server: &Arc<crate::rpc::Server>, state: Arc<AppState>) -> Router<Body> {
pub fn routes(rpc_server: &Arc<rpc::Server>, state: Arc<AppState>) -> Router<Body> {
Router::new()
.route("/users", get(get_users).post(create_user))
.route(
@ -29,6 +30,7 @@ pub fn routes(rpc_server: &Arc<crate::rpc::Server>, state: Arc<AppState>) -> 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<CreateUserParams>,
Extension(app): Extension<Arc<AppState>>,
Extension(rpc_server): Extension<Arc<crate::rpc::Server>>,
Extension(rpc_server): Extension<Arc<rpc::Server>>,
) -> Result<Json<User>> {
println!("{:?}", params);
@ -177,6 +179,12 @@ async fn trace_panic(panic: Json<Panic>) -> Result<()> {
Ok(())
}
async fn get_rpc_server_snapshot(
Extension(rpc_server): Extension<Arc<rpc::Server>>,
) -> Result<ErasedJson> {
Ok(ErasedJson::pretty(rpc_server.snapshot().await))
}
#[derive(Deserialize)]
struct CreateAccessTokenQueryParams {
public_key: String,

View File

@ -104,6 +104,12 @@ impl From<hyper::Error> for Error {
}
}
impl From<serde_json::Error> 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 {

View File

@ -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<mpsc::UnboundedSender<()>>,
}
pub trait Executor: Send + Clone {
type Sleep: Send + Future;
fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F);
@ -107,6 +109,23 @@ struct StoreWriteGuard<'a> {
_not_send: PhantomData<Rc<()>>,
}
#[derive(Serialize)]
pub struct ServerSnapshot<'a> {
peer: &'a Peer,
#[serde(serialize_with = "serialize_deref")]
store: RwLockReadGuard<'a, Store>,
}
pub fn serialize_deref<S, T, U>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Deref<Target = U>,
U: Serialize
{
Serialize::serialize(value.deref(), serializer)
}
impl Server {
pub fn new(
app_state: Arc<AppState>,
@ -1469,6 +1488,13 @@ impl Server {
_not_send: PhantomData,
}
}
pub async fn snapshot<'a>(self: &'a Arc<Self>) -> ServerSnapshot<'a> {
ServerSnapshot {
store: self.store.read().await,
peer: &self.peer
}
}
}
impl<'a> Deref for StoreReadGuard<'a> {

View File

@ -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<ConnectionId, ConnectionState>,
connections_by_user_id: HashMap<UserId, HashSet<ConnectionId>>,
projects: HashMap<u64, Project>,
#[serde(skip)]
channels: HashMap<ChannelId, Channel>,
next_project_id: u64,
}
#[derive(Serialize)]
struct ConnectionState {
user_id: UserId,
projects: HashSet<u64>,
@ -21,21 +24,25 @@ struct ConnectionState {
channels: HashSet<ChannelId>,
}
#[derive(Serialize)]
pub struct Project {
pub host_connection_id: ConnectionId,
pub host_user_id: UserId,
pub guests: HashMap<ConnectionId, (ReplicaId, UserId)>,
#[serde(skip)]
pub join_requests: HashMap<UserId, Vec<Receipt<proto::JoinProject>>>,
pub active_replica_ids: HashSet<ReplicaId>,
pub worktrees: HashMap<u64, Worktree>,
pub language_servers: Vec<proto::LanguageServer>,
}
#[derive(Default)]
#[derive(Default, Serialize)]
pub struct Worktree {
pub root_name: String,
pub visible: bool,
#[serde(skip)]
pub entries: HashMap<u64, proto::Entry>,
#[serde(skip)]
pub diagnostic_summaries: BTreeMap<PathBuf, proto::DiagnosticSummary>,
pub scan_id: u64,
}

View File

@ -22,7 +22,7 @@ workspace = { path = "../workspace" }
gpui = { path = "../gpui", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.8"

View File

@ -21,7 +21,7 @@ postage = { version = "0.4.1", features = ["futures-traits"] }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.8"

View File

@ -37,7 +37,7 @@ rand = "0.8.3"
resvg = "0.14"
seahash = "4.1"
serde = { version = "1.0.125", features = ["derive"] }
serde_json = "1.0.64"
serde_json = "1.0"
smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
time = { version = "0.3" }

View File

@ -17,7 +17,7 @@ workspace = { path = "../workspace" }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.8"

View File

@ -41,7 +41,7 @@ postage = { version = "0.4.1", features = ["futures-traits"] }
rand = "0.8.3"
regex = "1.5"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
sha2 = "0.10"
similar = "1.3"
smol = "1.2.5"

View File

@ -23,4 +23,4 @@ unicase = "2.6"
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }

View File

@ -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<proto::Message>,
next_message_id: Arc<AtomicU32>,
#[serde(skip)]
response_channels:
Arc<Mutex<Option<HashMap<u32, oneshot::Sender<(proto::Envelope, oneshot::Sender<()>)>>>>>,
}
@ -471,6 +474,17 @@ impl Peer {
}
}
impl Serialize for Peer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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::*;

View File

@ -26,6 +26,6 @@ smallvec = { version = "1.6", features = ["union"] }
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] }
unindent = "0.1"

View File

@ -20,6 +20,6 @@ anyhow = "1.0.38"
json_comments = "0.2"
schemars = "0.8"
serde = { version = "1", features = ["derive", "rc"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_path_to_error = "0.1.4"
toml = "0.5"

View File

@ -13,6 +13,6 @@ anyhow = "1.0.38"
indexmap = "1.6.2"
parking_lot = "0.11.1"
serde = { version = "1", features = ["derive", "rc"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_path_to_error = "0.1.4"
toml = "0.5"

View File

@ -15,6 +15,6 @@ futures = "0.3"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
rand = { version = "0.8", optional = true }
tempdir = { version = "0.3.7", optional = true }
serde_json = { version = "1.0.64", features = [
serde_json = { version = "1.0", features = [
"preserve_order",
], optional = true }

View File

@ -26,7 +26,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] }
parking_lot = "0.11.1"
postage = { version = "0.4.1", features = ["futures-traits"] }
serde = { version = "1", features = ["derive", "rc"] }
serde_json = { version = "1", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
smallvec = { version = "1.6", features = ["union"] }
[dev-dependencies]

View File

@ -76,7 +76,7 @@ regex = "1.5"
rsa = "0.4"
rust-embed = { version = "6.3", features = ["include-exclude"] }
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_path_to_error = "0.1.4"
simplelog = "0.9"
smallvec = { version = "1.6", features = ["union"] }
@ -107,7 +107,7 @@ settings = { path = "../settings", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
workspace = { path = "../workspace", features = ["test-support"] }
env_logger = "0.8"
serde_json = { version = "1.0.64", features = ["preserve_order"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
unindent = "0.1.7"
[package.metadata.bundle]

View File

@ -8,7 +8,6 @@ export function workspaceBackground(theme: Theme) {
}
export default function workspace(theme: Theme) {
const tab = {
height: 32,
background: workspaceBackground(theme),