Allow rpc_url to be assigned on Client with test-support feature (#13430)

Also, allow proto messages to be deserialized. This is to support
translating these messages JS types in a new server implementation based
on CloudFlare durable objects.

Release Notes:

- N/A
This commit is contained in:
Nathan Sobo 2024-07-10 13:36:22 -06:00 committed by GitHub
parent 15b8790a2c
commit 77b31d1845
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 60 additions and 33 deletions

View File

@ -217,6 +217,9 @@ pub struct Client {
>, >,
>, >,
>, >,
#[cfg(any(test, feature = "test-support"))]
rpc_url: RwLock<Option<Url>>,
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -527,6 +530,8 @@ impl Client {
authenticate: Default::default(), authenticate: Default::default(),
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
establish_connection: Default::default(), establish_connection: Default::default(),
#[cfg(any(test, feature = "test-support"))]
rpc_url: RwLock::default(),
}) })
} }
@ -584,6 +589,12 @@ impl Client {
self self
} }
#[cfg(any(test, feature = "test-support"))]
pub fn override_rpc_url(&self, url: Url) -> &Self {
*self.rpc_url.write() = Some(url);
self
}
pub fn global(cx: &AppContext) -> Arc<Self> { pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<GlobalClient>().0.clone() cx.global::<GlobalClient>().0.clone()
} }
@ -1086,38 +1097,50 @@ impl Client {
self.establish_websocket_connection(credentials, cx) self.establish_websocket_connection(credentials, cx)
} }
async fn get_rpc_url( fn rpc_url(
&self,
http: Arc<HttpClientWithUrl>, http: Arc<HttpClientWithUrl>,
release_channel: Option<ReleaseChannel>, release_channel: Option<ReleaseChannel>,
) -> Result<Url> { ) -> impl Future<Output = Result<Url>> {
if let Some(url) = &*ZED_RPC_URL { #[cfg(any(test, feature = "test-support"))]
return Url::parse(url).context("invalid rpc url"); let url_override = self.rpc_url.read().clone();
}
let mut url = http.build_url("/rpc"); async move {
if let Some(preview_param) = #[cfg(any(test, feature = "test-support"))]
release_channel.and_then(|channel| channel.release_query_param()) if let Some(url) = url_override {
{ return Ok(url);
url += "?"; }
url += preview_param;
}
let response = http.get(&url, Default::default(), false).await?;
let collab_url = if response.status().is_redirection() {
response
.headers()
.get("Location")
.ok_or_else(|| anyhow!("missing location header in /rpc response"))?
.to_str()
.map_err(EstablishConnectionError::other)?
.to_string()
} else {
Err(anyhow!(
"unexpected /rpc response status {}",
response.status()
))?
};
Url::parse(&collab_url).context("invalid rpc url") if let Some(url) = &*ZED_RPC_URL {
return Url::parse(url).context("invalid rpc url");
}
let mut url = http.build_url("/rpc");
if let Some(preview_param) =
release_channel.and_then(|channel| channel.release_query_param())
{
url += "?";
url += preview_param;
}
let response = http.get(&url, Default::default(), false).await?;
let collab_url = if response.status().is_redirection() {
response
.headers()
.get("Location")
.ok_or_else(|| anyhow!("missing location header in /rpc response"))?
.to_str()
.map_err(EstablishConnectionError::other)?
.to_string()
} else {
Err(anyhow!(
"unexpected /rpc response status {}",
response.status()
))?
};
Url::parse(&collab_url).context("invalid rpc url")
}
} }
fn establish_websocket_connection( fn establish_websocket_connection(
@ -1144,8 +1167,9 @@ impl Client {
); );
let http = self.http.clone(); let http = self.http.clone();
let rpc_url = self.rpc_url(http, release_channel);
cx.background_executor().spawn(async move { cx.background_executor().spawn(async move {
let mut rpc_url = Self::get_rpc_url(http, release_channel).await?; let mut rpc_url = rpc_url.await?;
let rpc_host = rpc_url let rpc_host = rpc_url
.host_str() .host_str()
.zip(rpc_url.port_or_known_default()) .zip(rpc_url.port_or_known_default())
@ -1186,6 +1210,7 @@ impl Client {
cx: &AsyncAppContext, cx: &AsyncAppContext,
) -> Task<Result<Credentials>> { ) -> Task<Result<Credentials>> {
let http = self.http.clone(); let http = self.http.clone();
let this = self.clone();
cx.spawn(|cx| async move { cx.spawn(|cx| async move {
let background = cx.background_executor().clone(); let background = cx.background_executor().clone();
@ -1215,7 +1240,8 @@ impl Client {
{ {
eprintln!("authenticate as admin {login}, {token}"); eprintln!("authenticate as admin {login}, {token}");
return Self::authenticate_as_admin(http, login.clone(), token.clone()) return this
.authenticate_as_admin(http, login.clone(), token.clone())
.await; .await;
} }
@ -1303,6 +1329,7 @@ impl Client {
} }
async fn authenticate_as_admin( async fn authenticate_as_admin(
self: &Arc<Self>,
http: Arc<HttpClientWithUrl>, http: Arc<HttpClientWithUrl>,
login: String, login: String,
mut api_token: String, mut api_token: String,
@ -1319,7 +1346,7 @@ impl Client {
// Use the collab server's admin API to retrieve the id // Use the collab server's admin API to retrieve the id
// of the impersonated user. // of the impersonated user.
let mut url = Self::get_rpc_url(http.clone(), None).await?; let mut url = self.rpc_url(http.clone(), None).await?;
url.set_path("/user"); url.set_path("/user");
url.set_query(Some(&format!("github_login={login}"))); url.set_query(Some(&format!("github_login={login}")));
let request = Request::get(url.as_str()) let request = Request::get(url.as_str())

View File

@ -1,7 +1,7 @@
fn main() { fn main() {
let mut build = prost_build::Config::new(); let mut build = prost_build::Config::new();
build build
.type_attribute(".", "#[derive(serde::Serialize)]") .type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
.compile_protos(&["proto/zed.proto"], &["proto"]) .compile_protos(&["proto/zed.proto"], &["proto"])
.unwrap(); .unwrap();
} }

View File

@ -8,7 +8,7 @@ pub use error::*;
pub use typed_envelope::*; pub use typed_envelope::*;
use collections::HashMap; use collections::HashMap;
pub use prost::Message; pub use prost::{DecodeError, Message};
use serde::Serialize; use serde::Serialize;
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::time::Instant; use std::time::Instant;