Use live_kit_client::TestServer
in integration tests
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
288c039929
commit
b6e5aa3bb0
9 changed files with 269 additions and 94 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1066,6 +1066,7 @@ dependencies = [
|
||||||
"language",
|
"language",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lipsum",
|
"lipsum",
|
||||||
|
"live_kit_client",
|
||||||
"live_kit_server",
|
"live_kit_server",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp",
|
||||||
|
@ -3163,6 +3164,7 @@ name = "live_kit_client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
"block",
|
"block",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes 1.2.1",
|
"bytes 1.2.1",
|
||||||
|
@ -3193,6 +3195,7 @@ name = "live_kit_server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
"futures 0.3.24",
|
"futures 0.3.24",
|
||||||
"hmac 0.12.1",
|
"hmac 0.12.1",
|
||||||
"jwt",
|
"jwt",
|
||||||
|
|
|
@ -62,15 +62,17 @@ editor = { path = "../editor", features = ["test-support"] }
|
||||||
language = { path = "../language", features = ["test-support"] }
|
language = { path = "../language", features = ["test-support"] }
|
||||||
fs = { path = "../fs", features = ["test-support"] }
|
fs = { path = "../fs", features = ["test-support"] }
|
||||||
git = { path = "../git", features = ["test-support"] }
|
git = { path = "../git", features = ["test-support"] }
|
||||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
|
||||||
lsp = { path = "../lsp", features = ["test-support"] }
|
lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
project = { path = "../project", features = ["test-support"] }
|
project = { path = "../project", features = ["test-support"] }
|
||||||
rpc = { path = "../rpc", features = ["test-support"] }
|
rpc = { path = "../rpc", features = ["test-support"] }
|
||||||
settings = { path = "../settings", features = ["test-support"] }
|
settings = { path = "../settings", features = ["test-support"] }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace", features = ["test-support"] }
|
workspace = { path = "../workspace", features = ["test-support"] }
|
||||||
|
|
||||||
ctor = "0.1"
|
ctor = "0.1"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
|
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
serde_json = { version = "1.0", features = ["preserve_order"] }
|
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||||
|
|
|
@ -47,7 +47,7 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering::SeqCst},
|
atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
|
@ -6138,6 +6138,7 @@ struct TestServer {
|
||||||
connection_killers: Arc<Mutex<HashMap<PeerId, Arc<AtomicBool>>>>,
|
connection_killers: Arc<Mutex<HashMap<PeerId, Arc<AtomicBool>>>>,
|
||||||
forbid_connections: Arc<AtomicBool>,
|
forbid_connections: Arc<AtomicBool>,
|
||||||
_test_db: TestDb,
|
_test_db: TestDb,
|
||||||
|
test_live_kit_server: Arc<live_kit_client::TestServer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestServer {
|
impl TestServer {
|
||||||
|
@ -6145,8 +6146,18 @@ impl TestServer {
|
||||||
foreground: Rc<executor::Foreground>,
|
foreground: Rc<executor::Foreground>,
|
||||||
background: Arc<executor::Background>,
|
background: Arc<executor::Background>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
static NEXT_LIVE_KIT_SERVER_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
let test_db = TestDb::fake(background.clone());
|
let test_db = TestDb::fake(background.clone());
|
||||||
let app_state = Self::build_app_state(&test_db).await;
|
let live_kit_server_id = NEXT_LIVE_KIT_SERVER_ID.fetch_add(1, SeqCst);
|
||||||
|
let live_kit_server = live_kit_client::TestServer::create(
|
||||||
|
format!("http://livekit.{}.test", live_kit_server_id),
|
||||||
|
format!("devkey-{}", live_kit_server_id),
|
||||||
|
format!("secret-{}", live_kit_server_id),
|
||||||
|
background.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let app_state = Self::build_app_state(&test_db, &live_kit_server).await;
|
||||||
let peer = Peer::new();
|
let peer = Peer::new();
|
||||||
let notifications = mpsc::unbounded();
|
let notifications = mpsc::unbounded();
|
||||||
let server = Server::new(app_state.clone(), Some(notifications.0));
|
let server = Server::new(app_state.clone(), Some(notifications.0));
|
||||||
|
@ -6159,6 +6170,7 @@ impl TestServer {
|
||||||
connection_killers: Default::default(),
|
connection_killers: Default::default(),
|
||||||
forbid_connections: Default::default(),
|
forbid_connections: Default::default(),
|
||||||
_test_db: test_db,
|
_test_db: test_db,
|
||||||
|
test_live_kit_server: live_kit_server,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6354,10 +6366,13 @@ impl TestServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn build_app_state(test_db: &TestDb) -> Arc<AppState> {
|
async fn build_app_state(
|
||||||
|
test_db: &TestDb,
|
||||||
|
fake_server: &live_kit_client::TestServer,
|
||||||
|
) -> Arc<AppState> {
|
||||||
Arc::new(AppState {
|
Arc::new(AppState {
|
||||||
db: test_db.db().clone(),
|
db: test_db.db().clone(),
|
||||||
live_kit_client: None,
|
live_kit_client: Some(Arc::new(fake_server.create_api_client())),
|
||||||
api_token: Default::default(),
|
api_token: Default::default(),
|
||||||
invite_link_prefix: Default::default(),
|
invite_link_prefix: Default::default(),
|
||||||
})
|
})
|
||||||
|
@ -6390,6 +6405,7 @@ impl Deref for TestServer {
|
||||||
impl Drop for TestServer {
|
impl Drop for TestServer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.peer.reset();
|
self.peer.reset();
|
||||||
|
self.test_live_kit_server.teardown().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub struct AppState {
|
||||||
db: Arc<dyn Db>,
|
db: Arc<dyn Db>,
|
||||||
api_token: String,
|
api_token: String,
|
||||||
invite_link_prefix: String,
|
invite_link_prefix: String,
|
||||||
live_kit_client: Option<live_kit_server::api::Client>,
|
live_kit_client: Option<Arc<dyn live_kit_server::api::Client>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
|
@ -53,11 +53,11 @@ impl AppState {
|
||||||
.zip(config.live_kit_key.as_ref())
|
.zip(config.live_kit_key.as_ref())
|
||||||
.zip(config.live_kit_secret.as_ref())
|
.zip(config.live_kit_secret.as_ref())
|
||||||
{
|
{
|
||||||
Some(live_kit_server::api::Client::new(
|
Some(Arc::new(live_kit_server::api::LiveKitClient::new(
|
||||||
server.clone(),
|
server.clone(),
|
||||||
key.clone(),
|
key.clone(),
|
||||||
secret.clone(),
|
secret.clone(),
|
||||||
))
|
)) as Arc<dyn live_kit_server::api::Client>)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,13 @@ doctest = false
|
||||||
name = "test_app"
|
name = "test_app"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
test-support = ["collections/test-support", "gpui/test-support", "lazy_static", "live_kit_server"]
|
test-support = [
|
||||||
|
"async-trait",
|
||||||
|
"collections/test-support",
|
||||||
|
"gpui/test-support",
|
||||||
|
"lazy_static",
|
||||||
|
"live_kit_server"
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
collections = { path = "../collections", optional = true }
|
collections = { path = "../collections", optional = true }
|
||||||
|
@ -21,6 +27,7 @@ live_kit_server = { path = "../live_kit_server", optional = true }
|
||||||
media = { path = "../media" }
|
media = { path = "../media" }
|
||||||
|
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
|
async-trait = { version = "0.1", optional = true }
|
||||||
core-foundation = "0.9.3"
|
core-foundation = "0.9.3"
|
||||||
core-graphics = "0.22.3"
|
core-graphics = "0.22.3"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
@ -30,11 +37,11 @@ parking_lot = "0.11.1"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
collections = { path = "../collections", features = ["test-support"] }
|
collections = { path = "../collections", features = ["test-support"] }
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
lazy_static = "1.4"
|
|
||||||
live_kit_server = { path = "../live_kit_server" }
|
live_kit_server = { path = "../live_kit_server" }
|
||||||
media = { path = "../media" }
|
media = { path = "../media" }
|
||||||
|
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
|
async-trait = "0.1"
|
||||||
block = "0.1"
|
block = "0.1"
|
||||||
bytes = "1.2"
|
bytes = "1.2"
|
||||||
byteorder = "1.4"
|
byteorder = "1.4"
|
||||||
|
@ -45,6 +52,7 @@ foreign-types = "0.3"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
hmac = "0.12"
|
hmac = "0.12"
|
||||||
jwt = "0.16"
|
jwt = "0.16"
|
||||||
|
lazy_static = "1.4"
|
||||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||||
objc = "0.2"
|
objc = "0.2"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
|
|
|
@ -7,7 +7,10 @@ use gpui::{
|
||||||
Menu, MenuItem, ViewContext,
|
Menu, MenuItem, ViewContext,
|
||||||
};
|
};
|
||||||
use live_kit_client::{LocalVideoTrack, RemoteVideoTrackUpdate, Room};
|
use live_kit_client::{LocalVideoTrack, RemoteVideoTrackUpdate, Room};
|
||||||
use live_kit_server::token::{self, VideoGrant};
|
use live_kit_server::{
|
||||||
|
api::Client,
|
||||||
|
token::{self, VideoGrant},
|
||||||
|
};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use media::core_video::CVImageBuffer;
|
use media::core_video::CVImageBuffer;
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
|
|
|
@ -1,35 +1,40 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use futures::{channel::mpsc, future};
|
use futures::{channel::mpsc, future};
|
||||||
use gpui::executor::Background;
|
use gpui::executor::Background;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use live_kit_server::token;
|
||||||
use media::core_video::CVImageBuffer;
|
use media::core_video::CVImageBuffer;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{future::Future, sync::Arc};
|
use std::{future::Future, sync::Arc};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SERVERS: Mutex<HashMap<String, Arc<FakeServer>>> = Default::default();
|
static ref SERVERS: Mutex<HashMap<String, Arc<TestServer>>> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FakeServer {
|
pub struct TestServer {
|
||||||
url: String,
|
pub url: String,
|
||||||
secret_key: String,
|
pub api_key: String,
|
||||||
rooms: Mutex<HashMap<String, FakeServerRoom>>,
|
pub secret_key: String,
|
||||||
|
rooms: Mutex<HashMap<String, TestServerRoom>>,
|
||||||
background: Arc<Background>,
|
background: Arc<Background>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeServer {
|
impl TestServer {
|
||||||
pub fn create(
|
pub fn create(
|
||||||
url: String,
|
url: String,
|
||||||
|
api_key: String,
|
||||||
secret_key: String,
|
secret_key: String,
|
||||||
background: Arc<Background>,
|
background: Arc<Background>,
|
||||||
) -> Result<Arc<FakeServer>> {
|
) -> Result<Arc<TestServer>> {
|
||||||
let mut servers = SERVERS.lock();
|
let mut servers = SERVERS.lock();
|
||||||
if servers.contains_key(&url) {
|
if servers.contains_key(&url) {
|
||||||
Err(anyhow!("a server with url {:?} already exists", url))
|
Err(anyhow!("a server with url {:?} already exists", url))
|
||||||
} else {
|
} else {
|
||||||
let server = Arc::new(FakeServer {
|
let server = Arc::new(TestServer {
|
||||||
url: url.clone(),
|
url: url.clone(),
|
||||||
|
api_key,
|
||||||
secret_key,
|
secret_key,
|
||||||
rooms: Default::default(),
|
rooms: Default::default(),
|
||||||
background,
|
background,
|
||||||
|
@ -39,7 +44,7 @@ impl FakeServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(url: &str) -> Result<Arc<FakeServer>> {
|
fn get(url: &str) -> Result<Arc<TestServer>> {
|
||||||
Ok(SERVERS
|
Ok(SERVERS
|
||||||
.lock()
|
.lock()
|
||||||
.get(url)
|
.get(url)
|
||||||
|
@ -55,33 +60,151 @@ impl FakeServer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_api_client(&self) -> TestApiClient {
|
||||||
|
TestApiClient {
|
||||||
|
url: self.url.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_room(&self, room: String) -> Result<()> {
|
||||||
|
self.background.simulate_random_delay().await;
|
||||||
|
let mut server_rooms = self.rooms.lock();
|
||||||
|
if server_rooms.contains_key(&room) {
|
||||||
|
Err(anyhow!("room {:?} already exists", room))
|
||||||
|
} else {
|
||||||
|
server_rooms.insert(room, Default::default());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_room(&self, room: String) -> Result<()> {
|
||||||
|
// TODO: clear state associated with all `Room`s.
|
||||||
|
self.background.simulate_random_delay().await;
|
||||||
|
let mut server_rooms = self.rooms.lock();
|
||||||
|
server_rooms
|
||||||
|
.remove(&room)
|
||||||
|
.ok_or_else(|| anyhow!("room {:?} does not exist", room))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn join_room(&self, token: String, client_room: Arc<Room>) -> Result<()> {
|
async fn join_room(&self, token: String, client_room: Arc<Room>) -> Result<()> {
|
||||||
self.background.simulate_random_delay().await;
|
self.background.simulate_random_delay().await;
|
||||||
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
|
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
|
||||||
let identity = claims.sub.unwrap().to_string();
|
let identity = claims.sub.unwrap().to_string();
|
||||||
let room = claims.video.room.unwrap();
|
let room_name = claims.video.room.unwrap();
|
||||||
let mut server_rooms = self.rooms.lock();
|
let mut server_rooms = self.rooms.lock();
|
||||||
let room = server_rooms
|
let room = server_rooms
|
||||||
.get_mut(&*room)
|
.get_mut(&*room_name)
|
||||||
.ok_or_else(|| anyhow!("room {} does not exist", room))?;
|
.ok_or_else(|| anyhow!("room {:?} does not exist", room_name))?;
|
||||||
room.clients.insert(identity, client_room);
|
if room.clients.contains_key(&identity) {
|
||||||
|
Err(anyhow!(
|
||||||
|
"{:?} attempted to join room {:?} twice",
|
||||||
|
identity,
|
||||||
|
room_name
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
room.clients.insert(identity, client_room);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn leave_room(&self, token: String) -> Result<()> {
|
||||||
|
self.background.simulate_random_delay().await;
|
||||||
|
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
|
||||||
|
let identity = claims.sub.unwrap().to_string();
|
||||||
|
let room_name = claims.video.room.unwrap();
|
||||||
|
let mut server_rooms = self.rooms.lock();
|
||||||
|
let room = server_rooms
|
||||||
|
.get_mut(&*room_name)
|
||||||
|
.ok_or_else(|| anyhow!("room {} does not exist", room_name))?;
|
||||||
|
room.clients.remove(&identity).ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"{:?} attempted to leave room {:?} before joining it",
|
||||||
|
identity,
|
||||||
|
room_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_participant(&self, room_name: String, identity: String) -> Result<()> {
|
||||||
|
// TODO: clear state associated with the `Room`.
|
||||||
|
|
||||||
|
self.background.simulate_random_delay().await;
|
||||||
|
let mut server_rooms = self.rooms.lock();
|
||||||
|
let room = server_rooms
|
||||||
|
.get_mut(&room_name)
|
||||||
|
.ok_or_else(|| anyhow!("room {} does not exist", room_name))?;
|
||||||
|
room.clients.remove(&identity).ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"participant {:?} did not join room {:?}",
|
||||||
|
identity,
|
||||||
|
room_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FakeServerRoom {
|
#[derive(Default)]
|
||||||
|
struct TestServerRoom {
|
||||||
clients: HashMap<Sid, Arc<Room>>,
|
clients: HashMap<Sid, Arc<Room>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeServerRoom {}
|
impl TestServerRoom {}
|
||||||
|
|
||||||
|
pub struct TestApiClient {
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl live_kit_server::api::Client for TestApiClient {
|
||||||
|
fn url(&self) -> &str {
|
||||||
|
&self.url
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_room(&self, name: String) -> Result<()> {
|
||||||
|
let server = TestServer::get(&self.url)?;
|
||||||
|
server.create_room(name).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_room(&self, name: String) -> Result<()> {
|
||||||
|
let server = TestServer::get(&self.url)?;
|
||||||
|
server.delete_room(name).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_participant(&self, room: String, identity: String) -> Result<()> {
|
||||||
|
let server = TestServer::get(&self.url)?;
|
||||||
|
server.remove_participant(room, identity).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn room_token(&self, room: &str, identity: &str) -> Result<String> {
|
||||||
|
let server = TestServer::get(&self.url)?;
|
||||||
|
token::create(
|
||||||
|
&server.api_key,
|
||||||
|
&server.secret_key,
|
||||||
|
Some(identity),
|
||||||
|
token::VideoGrant::to_join(room),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type Sid = String;
|
pub type Sid = String;
|
||||||
|
|
||||||
pub struct Room;
|
#[derive(Default)]
|
||||||
|
struct RoomState {
|
||||||
|
token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Room(Mutex<RoomState>);
|
||||||
|
|
||||||
impl Room {
|
impl Room {
|
||||||
pub fn new() -> Arc<Self> {
|
pub fn new() -> Arc<Self> {
|
||||||
Arc::new(Self)
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect(self: &Arc<Self>, url: &str, token: &str) -> impl Future<Output = Result<()>> {
|
pub fn connect(self: &Arc<Self>, url: &str, token: &str) -> impl Future<Output = Result<()>> {
|
||||||
|
@ -89,8 +212,9 @@ impl Room {
|
||||||
let url = url.to_string();
|
let url = url.to_string();
|
||||||
let token = token.to_string();
|
let token = token.to_string();
|
||||||
async move {
|
async move {
|
||||||
let server = FakeServer::get(&url)?;
|
let server = TestServer::get(&url)?;
|
||||||
server.join_room(token, this).await?;
|
server.join_room(token.clone(), this.clone()).await?;
|
||||||
|
this.0.lock().token = Some(token);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +239,14 @@ impl Room {
|
||||||
|
|
||||||
impl Drop for Room {
|
impl Drop for Room {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
todo!()
|
if let Some(token) = self.0.lock().token.take() {
|
||||||
|
if let Ok(server) = TestServer::get(&token) {
|
||||||
|
let background = server.background.clone();
|
||||||
|
background
|
||||||
|
.spawn(async move { server.leave_room(token).await.unwrap() })
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
|
async-trait = "0.1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
hmac = "0.12"
|
hmac = "0.12"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
use crate::{proto, token};
|
use crate::{proto, token};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use reqwest::header::CONTENT_TYPE;
|
use reqwest::header::CONTENT_TYPE;
|
||||||
use std::{future::Future, sync::Arc};
|
use std::{future::Future, sync::Arc};
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Client: Send + Sync {
|
||||||
|
fn url(&self) -> &str;
|
||||||
|
async fn create_room(&self, name: String) -> Result<()>;
|
||||||
|
async fn delete_room(&self, name: String) -> Result<()>;
|
||||||
|
async fn remove_participant(&self, room: String, identity: String) -> Result<()>;
|
||||||
|
fn room_token(&self, room: &str, identity: &str) -> Result<String>;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Client {
|
pub struct LiveKitClient {
|
||||||
http: reqwest::Client,
|
http: reqwest::Client,
|
||||||
url: Arc<str>,
|
url: Arc<str>,
|
||||||
key: Arc<str>,
|
key: Arc<str>,
|
||||||
secret: Arc<str>,
|
secret: Arc<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl LiveKitClient {
|
||||||
pub fn new(mut url: String, key: String, secret: String) -> Self {
|
pub fn new(mut url: String, key: String, secret: String) -> Self {
|
||||||
if url.ends_with('/') {
|
if url.ends_with('/') {
|
||||||
url.pop();
|
url.pop();
|
||||||
|
@ -26,67 +36,6 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn url(&self) -> &str {
|
|
||||||
&self.url
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_room(&self, name: String) -> impl Future<Output = Result<proto::Room>> {
|
|
||||||
self.request(
|
|
||||||
"twirp/livekit.RoomService/CreateRoom",
|
|
||||||
token::VideoGrant {
|
|
||||||
room_create: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
proto::CreateRoomRequest {
|
|
||||||
name,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete_room(&self, name: String) -> impl Future<Output = Result<()>> {
|
|
||||||
let response = self.request(
|
|
||||||
"twirp/livekit.RoomService/DeleteRoom",
|
|
||||||
token::VideoGrant {
|
|
||||||
room_create: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
proto::DeleteRoomRequest { room: name },
|
|
||||||
);
|
|
||||||
async move {
|
|
||||||
let _: proto::DeleteRoomResponse = response.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_participant(
|
|
||||||
&self,
|
|
||||||
room: String,
|
|
||||||
identity: String,
|
|
||||||
) -> impl Future<Output = Result<()>> {
|
|
||||||
let response = self.request(
|
|
||||||
"twirp/livekit.RoomService/RemoveParticipant",
|
|
||||||
token::VideoGrant::to_admin(&room),
|
|
||||||
proto::RoomParticipantIdentity {
|
|
||||||
room: room.clone(),
|
|
||||||
identity,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
async move {
|
|
||||||
let _: proto::RemoveParticipantResponse = response.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn room_token(&self, room: &str, identity: &str) -> Result<String> {
|
|
||||||
token::create(
|
|
||||||
&self.key,
|
|
||||||
&self.secret,
|
|
||||||
Some(identity),
|
|
||||||
token::VideoGrant::to_join(room),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn request<Req, Res>(
|
fn request<Req, Res>(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
|
@ -126,3 +75,65 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Client for LiveKitClient {
|
||||||
|
fn url(&self) -> &str {
|
||||||
|
&self.url
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_room(&self, name: String) -> Result<()> {
|
||||||
|
let x: proto::Room = self
|
||||||
|
.request(
|
||||||
|
"twirp/livekit.RoomService/CreateRoom",
|
||||||
|
token::VideoGrant {
|
||||||
|
room_create: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
proto::CreateRoomRequest {
|
||||||
|
name,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
dbg!(x);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_room(&self, name: String) -> Result<()> {
|
||||||
|
let _: proto::DeleteRoomResponse = self
|
||||||
|
.request(
|
||||||
|
"twirp/livekit.RoomService/DeleteRoom",
|
||||||
|
token::VideoGrant {
|
||||||
|
room_create: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
proto::DeleteRoomRequest { room: name },
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_participant(&self, room: String, identity: String) -> Result<()> {
|
||||||
|
let _: proto::RemoveParticipantResponse = self
|
||||||
|
.request(
|
||||||
|
"twirp/livekit.RoomService/RemoveParticipant",
|
||||||
|
token::VideoGrant::to_admin(&room),
|
||||||
|
proto::RoomParticipantIdentity {
|
||||||
|
room: room.clone(),
|
||||||
|
identity,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn room_token(&self, room: &str, identity: &str) -> Result<String> {
|
||||||
|
token::create(
|
||||||
|
&self.key,
|
||||||
|
&self.secret,
|
||||||
|
Some(identity),
|
||||||
|
token::VideoGrant::to_join(room),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue