parent
cf70b34e58
commit
0b0a078b29
|
|
@ -1,4 +1,13 @@
|
||||||
|
|
||||||
|
Before running the test, AppFlowy Cloud need to run with nginx server by this command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```shell
|
||||||
|
|
||||||
## Run test
|
## Run test
|
||||||
|
|
||||||
> Before executing the test, you need to install the [Chrome Driver](https://chromedriver.chromium.org/downloads). If
|
> Before executing the test, you need to install the [Chrome Driver](https://chromedriver.chromium.org/downloads). If
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use actix::Addr;
|
||||||
use actix_web::web::{Data, Path, Payload};
|
use actix_web::web::{Data, Path, Payload};
|
||||||
use actix_web::{get, web, HttpRequest, HttpResponse, Result, Scope};
|
use actix_web::{get, web, HttpRequest, HttpResponse, Result, Scope};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::biz::collab::storage::CollabAccessControlStorage;
|
use crate::biz::collab::storage::CollabAccessControlStorage;
|
||||||
|
|
@ -16,9 +17,10 @@ use realtime::client::rt_client::RealtimeClient;
|
||||||
use realtime::server::RealtimeServer;
|
use realtime::server::RealtimeServer;
|
||||||
|
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
||||||
use shared_entity::response::AppResponseError;
|
use shared_entity::response::AppResponseError;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tracing::{debug, error, instrument};
|
use tracing::{debug, error, instrument, trace};
|
||||||
|
|
||||||
pub fn ws_scope() -> Scope {
|
pub fn ws_scope() -> Scope {
|
||||||
web::scope("/ws")
|
web::scope("/ws")
|
||||||
|
|
@ -65,12 +67,21 @@ pub async fn establish_ws_connection_v1(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
state: Data<AppState>,
|
state: Data<AppState>,
|
||||||
server: Data<RealtimeServerAddr>,
|
server: Data<RealtimeServerAddr>,
|
||||||
|
web::Query(query_params): web::Query<HashMap<String, String>>,
|
||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
|
// Try to parse the connect info from the request body
|
||||||
|
// If it fails, try to parse it from the query params
|
||||||
let ConnectInfo {
|
let ConnectInfo {
|
||||||
access_token,
|
access_token,
|
||||||
client_version,
|
client_version,
|
||||||
device_id,
|
device_id,
|
||||||
} = ConnectInfo::try_from(&request)?;
|
} = match ConnectInfo::parse_from(&request) {
|
||||||
|
Ok(info) => info,
|
||||||
|
Err(_) => {
|
||||||
|
trace!("Failed to parse connect info from request body. Trying to parse from query params.");
|
||||||
|
ConnectInfo::parse_from(&query_params)?
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
start_connect(
|
start_connect(
|
||||||
&request,
|
&request,
|
||||||
|
|
@ -122,7 +133,7 @@ async fn start_connect(
|
||||||
{
|
{
|
||||||
Ok(response) => Ok(response),
|
Ok(response) => Ok(response),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("🔴ws connection error: {:?}", e);
|
error!("🔴ws connection error: {:?}", e);
|
||||||
Err(e)
|
Err(e)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -142,44 +153,56 @@ struct ConnectInfo {
|
||||||
device_id: String,
|
device_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&HttpRequest> for ConnectInfo {
|
const CLIENT_VERSION: &str = "client-version";
|
||||||
type Error = AppError;
|
const DEVICE_ID: &str = "device-id";
|
||||||
fn try_from(req: &HttpRequest) -> Result<Self, Self::Error> {
|
|
||||||
let headers = req.headers();
|
|
||||||
|
|
||||||
let access_token = match headers.get(AUTHORIZATION) {
|
// Trait for parameter extraction
|
||||||
Some(token) => token
|
trait ExtractParameter {
|
||||||
.to_str()
|
fn extract_param(&self, key: &str) -> Result<String, AppError>;
|
||||||
.map_err(|_| AppError::InvalidRequest("invalid access token".to_string()))?,
|
}
|
||||||
None => return Err(AppError::OAuthError("no access token".to_string())),
|
|
||||||
};
|
|
||||||
|
|
||||||
let client_version = headers
|
// Implement the trait for HashMap<String, String>
|
||||||
.get("client-version")
|
impl ExtractParameter for HashMap<String, String> {
|
||||||
.map(|v| {
|
fn extract_param(&self, key: &str) -> Result<String, AppError> {
|
||||||
v.to_str()
|
self
|
||||||
.map_err(|_| AppError::InvalidRequest("invalid client version".to_string()))
|
.get(key)
|
||||||
.and_then(|v| {
|
.ok_or_else(|| {
|
||||||
Version::parse(v)
|
AppError::InvalidRequest(format!("Parameter with given key:{} not found", key))
|
||||||
.map_err(|_| AppError::InvalidRequest("fail to parse client version".to_string()))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| {
|
.map(|s| s.to_string())
|
||||||
error!("fail to get client version from header, use default version 0.5.0");
|
}
|
||||||
Ok(Version::new(0, 5, 0))
|
}
|
||||||
})?;
|
|
||||||
|
|
||||||
let device_id = match headers.get("device-id") {
|
// Implement the trait for HttpRequest
|
||||||
Some(device_id) => device_id
|
impl ExtractParameter for HttpRequest {
|
||||||
.to_str()
|
fn extract_param(&self, key: &str) -> Result<String, AppError> {
|
||||||
.map_err(|_| AppError::InvalidRequest("invalid device id".to_string()))?,
|
self
|
||||||
None => return Err(AppError::InvalidRequest("empty device id".to_string())),
|
.headers()
|
||||||
};
|
.get(key)
|
||||||
|
.ok_or_else(|| AppError::InvalidRequest(format!("Header with given key:{} not found", key)))
|
||||||
|
.and_then(|value| {
|
||||||
|
value
|
||||||
|
.to_str()
|
||||||
|
.map_err(|_| {
|
||||||
|
AppError::InvalidRequest(format!("Invalid header value for given key:{}", key))
|
||||||
|
})
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectInfo {
|
||||||
|
fn parse_from<T: ExtractParameter>(source: &T) -> Result<Self, AppError> {
|
||||||
|
let access_token = source.extract_param(AUTHORIZATION.as_str())?;
|
||||||
|
let client_version_str = source.extract_param(CLIENT_VERSION)?;
|
||||||
|
let client_version = Version::parse(&client_version_str)
|
||||||
|
.map_err(|_| AppError::InvalidRequest(format!("Invalid version:{}", client_version_str)))?;
|
||||||
|
let device_id = source.extract_param(DEVICE_ID)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
access_token: access_token.to_string(),
|
access_token,
|
||||||
client_version,
|
client_version,
|
||||||
device_id: device_id.to_string(),
|
device_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue