lib.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. use std::collections::HashMap;
  2. use std::convert::TryFrom;
  3. use std::env::temp_dir;
  4. use std::path::PathBuf;
  5. use std::sync::Arc;
  6. use bytes::Bytes;
  7. use collab::core::collab::MutexCollab;
  8. use collab::core::origin::CollabOrigin;
  9. use collab::preclude::updates::decoder::Decode;
  10. use collab::preclude::{merge_updates_v1, Update};
  11. use collab_document::blocks::DocumentData;
  12. use collab_document::document::Document;
  13. use nanoid::nanoid;
  14. use parking_lot::RwLock;
  15. use protobuf::ProtobufError;
  16. use tokio::sync::broadcast::{channel, Sender};
  17. use uuid::Uuid;
  18. use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
  19. use flowy_database2::entities::*;
  20. use flowy_database2::event_map::DatabaseEvent;
  21. use flowy_document2::entities::{DocumentDataPB, OpenDocumentPayloadPB};
  22. use flowy_document2::event_map::DocumentEvent;
  23. use flowy_folder2::entities::icon::UpdateViewIconPayloadPB;
  24. use flowy_folder2::entities::*;
  25. use flowy_folder2::event_map::FolderEvent;
  26. use flowy_notification::entities::SubscribeObject;
  27. use flowy_notification::{register_notification_sender, NotificationSender};
  28. use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
  29. use flowy_user::entities::{
  30. AuthTypePB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, UpdateCloudConfigPB,
  31. UserCloudConfigPB, UserProfilePB,
  32. };
  33. use flowy_user::errors::{FlowyError, FlowyResult};
  34. use flowy_user::event_map::UserEvent::*;
  35. use crate::document::document_event::{DocumentEventTest, OpenDocumentData};
  36. use crate::event_builder::EventBuilder;
  37. use crate::user_event::{async_sign_up, SignUpContext};
  38. pub mod document;
  39. pub mod event_builder;
  40. pub mod folder_event;
  41. pub mod user_event;
  42. #[derive(Clone)]
  43. pub struct FlowyCoreTest {
  44. auth_type: Arc<RwLock<AuthTypePB>>,
  45. inner: AppFlowyCore,
  46. #[allow(dead_code)]
  47. cleaner: Arc<Cleaner>,
  48. pub notification_sender: TestNotificationSender,
  49. }
  50. impl Default for FlowyCoreTest {
  51. fn default() -> Self {
  52. let temp_dir = temp_dir().join(nanoid!(6));
  53. std::fs::create_dir_all(&temp_dir).unwrap();
  54. Self::new_with_user_data_path(temp_dir, nanoid!(6))
  55. }
  56. }
  57. impl FlowyCoreTest {
  58. pub fn new() -> Self {
  59. Self::default()
  60. }
  61. pub async fn insert_document_text(&self, document_id: &str, text: &str, index: usize) {
  62. let document_event = DocumentEventTest::new_with_core(self.clone());
  63. document_event
  64. .insert_index(document_id, text, index, None)
  65. .await;
  66. }
  67. pub async fn get_document_data(&self, view_id: &str) -> DocumentData {
  68. let pb = EventBuilder::new(self.clone())
  69. .event(DocumentEvent::GetDocumentData)
  70. .payload(OpenDocumentPayloadPB {
  71. document_id: view_id.to_string(),
  72. })
  73. .async_send()
  74. .await
  75. .parse::<DocumentDataPB>();
  76. DocumentData::from(pb)
  77. }
  78. pub async fn get_document_update(&self, document_id: &str) -> Vec<u8> {
  79. let cloud_service = self.document_manager.get_cloud_service().clone();
  80. let remote_updates = cloud_service
  81. .get_document_updates(document_id)
  82. .await
  83. .unwrap();
  84. if remote_updates.is_empty() {
  85. return vec![];
  86. }
  87. let updates = remote_updates
  88. .iter()
  89. .map(|update| update.as_ref())
  90. .collect::<Vec<&[u8]>>();
  91. merge_updates_v1(&updates).unwrap()
  92. }
  93. pub fn new_with_user_data_path(path: PathBuf, name: String) -> Self {
  94. let config = AppFlowyCoreConfig::new(path.to_str().unwrap(), name).log_filter(
  95. "trace",
  96. vec![
  97. "flowy_test".to_string(),
  98. // "lib_dispatch".to_string()
  99. ],
  100. );
  101. let inner = std::thread::spawn(|| AppFlowyCore::new(config))
  102. .join()
  103. .unwrap();
  104. let notification_sender = TestNotificationSender::new();
  105. let auth_type = Arc::new(RwLock::new(AuthTypePB::Local));
  106. register_notification_sender(notification_sender.clone());
  107. std::mem::forget(inner.dispatcher());
  108. Self {
  109. inner,
  110. auth_type,
  111. notification_sender,
  112. cleaner: Arc::new(Cleaner(path)),
  113. }
  114. }
  115. pub async fn enable_encryption(&self) -> String {
  116. let config = EventBuilder::new(self.clone())
  117. .event(GetCloudConfig)
  118. .async_send()
  119. .await
  120. .parse::<UserCloudConfigPB>();
  121. let update = UpdateCloudConfigPB {
  122. enable_sync: None,
  123. enable_encrypt: Some(true),
  124. };
  125. let error = EventBuilder::new(self.clone())
  126. .event(SetCloudConfig)
  127. .payload(update)
  128. .async_send()
  129. .await
  130. .error();
  131. assert!(error.is_none());
  132. config.encrypt_secret
  133. }
  134. pub async fn get_user_profile(&self) -> Result<UserProfilePB, FlowyError> {
  135. EventBuilder::new(self.clone())
  136. .event(GetUserProfile)
  137. .async_send()
  138. .await
  139. .try_parse::<UserProfilePB>()
  140. }
  141. pub async fn new_with_guest_user() -> Self {
  142. let test = Self::default();
  143. test.sign_up_as_guest().await;
  144. test
  145. }
  146. pub async fn sign_up_as_guest(&self) -> SignUpContext {
  147. async_sign_up(self.inner.dispatcher(), AuthTypePB::Local).await
  148. }
  149. pub async fn supabase_party_sign_up(&self) -> UserProfilePB {
  150. let map = third_party_sign_up_param(Uuid::new_v4().to_string());
  151. let payload = OauthSignInPB {
  152. map,
  153. auth_type: AuthTypePB::Supabase,
  154. };
  155. EventBuilder::new(self.clone())
  156. .event(OauthSignIn)
  157. .payload(payload)
  158. .async_send()
  159. .await
  160. .parse::<UserProfilePB>()
  161. }
  162. pub async fn sign_out(&self) {
  163. EventBuilder::new(self.clone())
  164. .event(SignOut)
  165. .async_send()
  166. .await;
  167. }
  168. pub fn set_auth_type(&self, auth_type: AuthTypePB) {
  169. *self.auth_type.write() = auth_type;
  170. }
  171. pub async fn init_user(&self) -> UserProfilePB {
  172. self.sign_up_as_guest().await.user_profile
  173. }
  174. pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
  175. let payload = SignInUrlPayloadPB {
  176. email: email.to_string(),
  177. auth_type: AuthTypePB::AFCloud,
  178. };
  179. let sign_in_url = EventBuilder::new(self.clone())
  180. .event(GetSignInURL)
  181. .payload(payload)
  182. .async_send()
  183. .await
  184. .try_parse::<SignInUrlPB>()?
  185. .sign_in_url;
  186. let mut map = HashMap::new();
  187. map.insert(USER_SIGN_IN_URL.to_string(), sign_in_url);
  188. map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string());
  189. let payload = OauthSignInPB {
  190. map,
  191. auth_type: AuthTypePB::AFCloud,
  192. };
  193. let user_profile = EventBuilder::new(self.clone())
  194. .event(OauthSignIn)
  195. .payload(payload)
  196. .async_send()
  197. .await
  198. .try_parse::<UserProfilePB>()?;
  199. Ok(user_profile)
  200. }
  201. pub async fn supabase_sign_up_with_uuid(
  202. &self,
  203. uuid: &str,
  204. email: Option<String>,
  205. ) -> FlowyResult<UserProfilePB> {
  206. let mut map = HashMap::new();
  207. map.insert(USER_UUID.to_string(), uuid.to_string());
  208. map.insert(USER_DEVICE_ID.to_string(), uuid.to_string());
  209. map.insert(
  210. USER_EMAIL.to_string(),
  211. email.unwrap_or_else(|| format!("{}@appflowy.io", nanoid!(10))),
  212. );
  213. let payload = OauthSignInPB {
  214. map,
  215. auth_type: AuthTypePB::Supabase,
  216. };
  217. let user_profile = EventBuilder::new(self.clone())
  218. .event(OauthSignIn)
  219. .payload(payload)
  220. .async_send()
  221. .await
  222. .try_parse::<UserProfilePB>()?;
  223. Ok(user_profile)
  224. }
  225. // Must sign up/ sign in first
  226. pub async fn get_current_workspace(&self) -> WorkspaceSettingPB {
  227. EventBuilder::new(self.clone())
  228. .event(FolderEvent::GetCurrentWorkspace)
  229. .async_send()
  230. .await
  231. .parse::<flowy_folder2::entities::WorkspaceSettingPB>()
  232. }
  233. pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> {
  234. EventBuilder::new(self.clone())
  235. .event(FolderEvent::ReadWorkspaceViews)
  236. .async_send()
  237. .await
  238. .parse::<flowy_folder2::entities::RepeatedViewPB>()
  239. .items
  240. }
  241. pub async fn get_views(&self, parent_view_id: &str) -> ViewPB {
  242. EventBuilder::new(self.clone())
  243. .event(FolderEvent::ReadView)
  244. .payload(ViewIdPB {
  245. value: parent_view_id.to_string(),
  246. })
  247. .async_send()
  248. .await
  249. .parse::<flowy_folder2::entities::ViewPB>()
  250. }
  251. pub async fn delete_view(&self, view_id: &str) {
  252. let payload = RepeatedViewIdPB {
  253. items: vec![view_id.to_string()],
  254. };
  255. // delete the view. the view will be moved to trash
  256. EventBuilder::new(self.clone())
  257. .event(FolderEvent::DeleteView)
  258. .payload(payload)
  259. .async_send()
  260. .await;
  261. }
  262. pub async fn update_view(&self, changeset: UpdateViewPayloadPB) -> Option<FlowyError> {
  263. // delete the view. the view will be moved to trash
  264. EventBuilder::new(self.clone())
  265. .event(FolderEvent::UpdateView)
  266. .payload(changeset)
  267. .async_send()
  268. .await
  269. .error()
  270. }
  271. pub async fn update_view_icon(&self, payload: UpdateViewIconPayloadPB) -> Option<FlowyError> {
  272. EventBuilder::new(self.clone())
  273. .event(FolderEvent::UpdateViewIcon)
  274. .payload(payload)
  275. .async_send()
  276. .await
  277. .error()
  278. }
  279. pub async fn create_view(&self, parent_id: &str, name: String) -> ViewPB {
  280. let payload = CreateViewPayloadPB {
  281. parent_view_id: parent_id.to_string(),
  282. name,
  283. desc: "".to_string(),
  284. thumbnail: None,
  285. layout: Default::default(),
  286. initial_data: vec![],
  287. meta: Default::default(),
  288. set_as_current: false,
  289. index: None,
  290. };
  291. EventBuilder::new(self.clone())
  292. .event(FolderEvent::CreateView)
  293. .payload(payload)
  294. .async_send()
  295. .await
  296. .parse::<flowy_folder2::entities::ViewPB>()
  297. }
  298. pub async fn create_document(
  299. &self,
  300. parent_id: &str,
  301. name: String,
  302. initial_data: Vec<u8>,
  303. ) -> ViewPB {
  304. let payload = CreateViewPayloadPB {
  305. parent_view_id: parent_id.to_string(),
  306. name,
  307. desc: "".to_string(),
  308. thumbnail: None,
  309. layout: ViewLayoutPB::Document,
  310. initial_data,
  311. meta: Default::default(),
  312. set_as_current: true,
  313. index: None,
  314. };
  315. let view = EventBuilder::new(self.clone())
  316. .event(FolderEvent::CreateView)
  317. .payload(payload)
  318. .async_send()
  319. .await
  320. .parse::<ViewPB>();
  321. let payload = OpenDocumentPayloadPB {
  322. document_id: view.id.clone(),
  323. };
  324. let _ = EventBuilder::new(self.clone())
  325. .event(DocumentEvent::OpenDocument)
  326. .payload(payload)
  327. .async_send()
  328. .await
  329. .parse::<DocumentDataPB>();
  330. view
  331. }
  332. pub async fn create_grid(&self, parent_id: &str, name: String, initial_data: Vec<u8>) -> ViewPB {
  333. let payload = CreateViewPayloadPB {
  334. parent_view_id: parent_id.to_string(),
  335. name,
  336. desc: "".to_string(),
  337. thumbnail: None,
  338. layout: ViewLayoutPB::Grid,
  339. initial_data,
  340. meta: Default::default(),
  341. set_as_current: true,
  342. index: None,
  343. };
  344. EventBuilder::new(self.clone())
  345. .event(FolderEvent::CreateView)
  346. .payload(payload)
  347. .async_send()
  348. .await
  349. .parse::<flowy_folder2::entities::ViewPB>()
  350. }
  351. pub async fn open_database(&self, view_id: &str) {
  352. EventBuilder::new(self.clone())
  353. .event(DatabaseEvent::GetDatabase)
  354. .payload(DatabaseViewIdPB {
  355. value: view_id.to_string(),
  356. })
  357. .async_send()
  358. .await;
  359. }
  360. pub async fn open_document(&self, doc_id: String) -> OpenDocumentData {
  361. let payload = OpenDocumentPayloadPB {
  362. document_id: doc_id.clone(),
  363. };
  364. let data = EventBuilder::new(self.clone())
  365. .event(DocumentEvent::OpenDocument)
  366. .payload(payload)
  367. .async_send()
  368. .await
  369. .parse::<DocumentDataPB>();
  370. OpenDocumentData { id: doc_id, data }
  371. }
  372. pub async fn create_board(&self, parent_id: &str, name: String, initial_data: Vec<u8>) -> ViewPB {
  373. let payload = CreateViewPayloadPB {
  374. parent_view_id: parent_id.to_string(),
  375. name,
  376. desc: "".to_string(),
  377. thumbnail: None,
  378. layout: ViewLayoutPB::Board,
  379. initial_data,
  380. meta: Default::default(),
  381. set_as_current: true,
  382. index: None,
  383. };
  384. EventBuilder::new(self.clone())
  385. .event(FolderEvent::CreateView)
  386. .payload(payload)
  387. .async_send()
  388. .await
  389. .parse::<flowy_folder2::entities::ViewPB>()
  390. }
  391. pub async fn create_calendar(
  392. &self,
  393. parent_id: &str,
  394. name: String,
  395. initial_data: Vec<u8>,
  396. ) -> ViewPB {
  397. let payload = CreateViewPayloadPB {
  398. parent_view_id: parent_id.to_string(),
  399. name,
  400. desc: "".to_string(),
  401. thumbnail: None,
  402. layout: ViewLayoutPB::Calendar,
  403. initial_data,
  404. meta: Default::default(),
  405. set_as_current: true,
  406. index: None,
  407. };
  408. EventBuilder::new(self.clone())
  409. .event(FolderEvent::CreateView)
  410. .payload(payload)
  411. .async_send()
  412. .await
  413. .parse::<flowy_folder2::entities::ViewPB>()
  414. }
  415. pub async fn get_database(&self, view_id: &str) -> DatabasePB {
  416. EventBuilder::new(self.clone())
  417. .event(DatabaseEvent::GetDatabase)
  418. .payload(DatabaseViewIdPB {
  419. value: view_id.to_string(),
  420. })
  421. .async_send()
  422. .await
  423. .parse::<flowy_database2::entities::DatabasePB>()
  424. }
  425. pub async fn get_all_database_fields(&self, view_id: &str) -> RepeatedFieldPB {
  426. EventBuilder::new(self.clone())
  427. .event(DatabaseEvent::GetFields)
  428. .payload(GetFieldPayloadPB {
  429. view_id: view_id.to_string(),
  430. field_ids: None,
  431. })
  432. .async_send()
  433. .await
  434. .parse::<RepeatedFieldPB>()
  435. }
  436. pub async fn create_field(&self, view_id: &str, field_type: FieldType) -> FieldPB {
  437. EventBuilder::new(self.clone())
  438. .event(DatabaseEvent::CreateTypeOption)
  439. .payload(CreateFieldPayloadPB {
  440. view_id: view_id.to_string(),
  441. field_type,
  442. type_option_data: None,
  443. })
  444. .async_send()
  445. .await
  446. .parse::<TypeOptionPB>()
  447. .field
  448. }
  449. pub async fn update_field(&self, changeset: FieldChangesetPB) {
  450. EventBuilder::new(self.clone())
  451. .event(DatabaseEvent::UpdateField)
  452. .payload(changeset)
  453. .async_send()
  454. .await;
  455. }
  456. pub async fn delete_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
  457. EventBuilder::new(self.clone())
  458. .event(DatabaseEvent::DeleteField)
  459. .payload(DeleteFieldPayloadPB {
  460. view_id: view_id.to_string(),
  461. field_id: field_id.to_string(),
  462. })
  463. .async_send()
  464. .await
  465. .error()
  466. }
  467. pub async fn update_field_type(
  468. &self,
  469. view_id: &str,
  470. field_id: &str,
  471. field_type: FieldType,
  472. ) -> Option<FlowyError> {
  473. EventBuilder::new(self.clone())
  474. .event(DatabaseEvent::UpdateFieldType)
  475. .payload(UpdateFieldTypePayloadPB {
  476. view_id: view_id.to_string(),
  477. field_id: field_id.to_string(),
  478. field_type,
  479. })
  480. .async_send()
  481. .await
  482. .error()
  483. }
  484. pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
  485. EventBuilder::new(self.clone())
  486. .event(DatabaseEvent::DuplicateField)
  487. .payload(DuplicateFieldPayloadPB {
  488. view_id: view_id.to_string(),
  489. field_id: field_id.to_string(),
  490. })
  491. .async_send()
  492. .await
  493. .error()
  494. }
  495. pub async fn get_primary_field(&self, database_view_id: &str) -> FieldPB {
  496. EventBuilder::new(self.clone())
  497. .event(DatabaseEvent::GetPrimaryField)
  498. .payload(DatabaseViewIdPB {
  499. value: database_view_id.to_string(),
  500. })
  501. .async_send()
  502. .await
  503. .parse::<FieldPB>()
  504. }
  505. pub async fn create_row(
  506. &self,
  507. view_id: &str,
  508. start_row_id: Option<String>,
  509. data: Option<RowDataPB>,
  510. ) -> RowMetaPB {
  511. EventBuilder::new(self.clone())
  512. .event(DatabaseEvent::CreateRow)
  513. .payload(CreateRowPayloadPB {
  514. view_id: view_id.to_string(),
  515. start_row_id,
  516. group_id: None,
  517. data,
  518. })
  519. .async_send()
  520. .await
  521. .parse::<RowMetaPB>()
  522. }
  523. pub async fn delete_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
  524. EventBuilder::new(self.clone())
  525. .event(DatabaseEvent::DeleteRow)
  526. .payload(RowIdPB {
  527. view_id: view_id.to_string(),
  528. row_id: row_id.to_string(),
  529. group_id: None,
  530. })
  531. .async_send()
  532. .await
  533. .error()
  534. }
  535. pub async fn get_row(&self, view_id: &str, row_id: &str) -> OptionalRowPB {
  536. EventBuilder::new(self.clone())
  537. .event(DatabaseEvent::GetRow)
  538. .payload(RowIdPB {
  539. view_id: view_id.to_string(),
  540. row_id: row_id.to_string(),
  541. group_id: None,
  542. })
  543. .async_send()
  544. .await
  545. .parse::<OptionalRowPB>()
  546. }
  547. pub async fn get_row_meta(&self, view_id: &str, row_id: &str) -> RowMetaPB {
  548. EventBuilder::new(self.clone())
  549. .event(DatabaseEvent::GetRowMeta)
  550. .payload(RowIdPB {
  551. view_id: view_id.to_string(),
  552. row_id: row_id.to_string(),
  553. group_id: None,
  554. })
  555. .async_send()
  556. .await
  557. .parse::<RowMetaPB>()
  558. }
  559. pub async fn update_row_meta(&self, changeset: UpdateRowMetaChangesetPB) -> Option<FlowyError> {
  560. EventBuilder::new(self.clone())
  561. .event(DatabaseEvent::UpdateRowMeta)
  562. .payload(changeset)
  563. .async_send()
  564. .await
  565. .error()
  566. }
  567. pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
  568. EventBuilder::new(self.clone())
  569. .event(DatabaseEvent::DuplicateRow)
  570. .payload(RowIdPB {
  571. view_id: view_id.to_string(),
  572. row_id: row_id.to_string(),
  573. group_id: None,
  574. })
  575. .async_send()
  576. .await
  577. .error()
  578. }
  579. pub async fn move_row(&self, view_id: &str, row_id: &str, to_row_id: &str) -> Option<FlowyError> {
  580. EventBuilder::new(self.clone())
  581. .event(DatabaseEvent::MoveRow)
  582. .payload(MoveRowPayloadPB {
  583. view_id: view_id.to_string(),
  584. from_row_id: row_id.to_string(),
  585. to_row_id: to_row_id.to_string(),
  586. })
  587. .async_send()
  588. .await
  589. .error()
  590. }
  591. pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option<FlowyError> {
  592. EventBuilder::new(self.clone())
  593. .event(DatabaseEvent::UpdateCell)
  594. .payload(changeset)
  595. .async_send()
  596. .await
  597. .error()
  598. }
  599. pub async fn update_date_cell(&self, changeset: DateChangesetPB) -> Option<FlowyError> {
  600. EventBuilder::new(self.clone())
  601. .event(DatabaseEvent::UpdateDateCell)
  602. .payload(changeset)
  603. .async_send()
  604. .await
  605. .error()
  606. }
  607. pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB {
  608. EventBuilder::new(self.clone())
  609. .event(DatabaseEvent::GetCell)
  610. .payload(CellIdPB {
  611. view_id: view_id.to_string(),
  612. row_id: row_id.to_string(),
  613. field_id: field_id.to_string(),
  614. })
  615. .async_send()
  616. .await
  617. .parse::<CellPB>()
  618. }
  619. pub async fn get_date_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> DateCellDataPB {
  620. let cell = self.get_cell(view_id, row_id, field_id).await;
  621. DateCellDataPB::try_from(Bytes::from(cell.data)).unwrap()
  622. }
  623. pub async fn get_checklist_cell(
  624. &self,
  625. view_id: &str,
  626. field_id: &str,
  627. row_id: &str,
  628. ) -> ChecklistCellDataPB {
  629. EventBuilder::new(self.clone())
  630. .event(DatabaseEvent::GetChecklistCellData)
  631. .payload(CellIdPB {
  632. view_id: view_id.to_string(),
  633. row_id: row_id.to_string(),
  634. field_id: field_id.to_string(),
  635. })
  636. .async_send()
  637. .await
  638. .parse::<ChecklistCellDataPB>()
  639. }
  640. pub async fn update_checklist_cell(
  641. &self,
  642. changeset: ChecklistCellDataChangesetPB,
  643. ) -> Option<FlowyError> {
  644. EventBuilder::new(self.clone())
  645. .event(DatabaseEvent::UpdateChecklistCell)
  646. .payload(changeset)
  647. .async_send()
  648. .await
  649. .error()
  650. }
  651. pub async fn insert_option(
  652. &self,
  653. view_id: &str,
  654. field_id: &str,
  655. row_id: &str,
  656. name: &str,
  657. ) -> Option<FlowyError> {
  658. let option = EventBuilder::new(self.clone())
  659. .event(DatabaseEvent::CreateSelectOption)
  660. .payload(CreateSelectOptionPayloadPB {
  661. field_id: field_id.to_string(),
  662. view_id: view_id.to_string(),
  663. option_name: name.to_string(),
  664. })
  665. .async_send()
  666. .await
  667. .parse::<SelectOptionPB>();
  668. EventBuilder::new(self.clone())
  669. .event(DatabaseEvent::InsertOrUpdateSelectOption)
  670. .payload(RepeatedSelectOptionPayload {
  671. view_id: view_id.to_string(),
  672. field_id: field_id.to_string(),
  673. row_id: row_id.to_string(),
  674. items: vec![option],
  675. })
  676. .async_send()
  677. .await
  678. .error()
  679. }
  680. pub async fn get_groups(&self, view_id: &str) -> Vec<GroupPB> {
  681. EventBuilder::new(self.clone())
  682. .event(DatabaseEvent::GetGroups)
  683. .payload(DatabaseViewIdPB {
  684. value: view_id.to_string(),
  685. })
  686. .async_send()
  687. .await
  688. .parse::<RepeatedGroupPB>()
  689. .items
  690. }
  691. pub async fn move_group(&self, view_id: &str, from_id: &str, to_id: &str) -> Option<FlowyError> {
  692. EventBuilder::new(self.clone())
  693. .event(DatabaseEvent::MoveGroup)
  694. .payload(MoveGroupPayloadPB {
  695. view_id: view_id.to_string(),
  696. from_group_id: from_id.to_string(),
  697. to_group_id: to_id.to_string(),
  698. })
  699. .async_send()
  700. .await
  701. .error()
  702. }
  703. pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
  704. EventBuilder::new(self.clone())
  705. .event(DatabaseEvent::SetGroupByField)
  706. .payload(GroupByFieldPayloadPB {
  707. field_id: field_id.to_string(),
  708. view_id: view_id.to_string(),
  709. })
  710. .async_send()
  711. .await
  712. .error()
  713. }
  714. pub async fn update_group(
  715. &self,
  716. view_id: &str,
  717. group_id: &str,
  718. name: Option<String>,
  719. visible: Option<bool>,
  720. ) -> Option<FlowyError> {
  721. EventBuilder::new(self.clone())
  722. .event(DatabaseEvent::UpdateGroup)
  723. .payload(UpdateGroupPB {
  724. view_id: view_id.to_string(),
  725. group_id: group_id.to_string(),
  726. name,
  727. visible,
  728. })
  729. .async_send()
  730. .await
  731. .error()
  732. }
  733. pub async fn update_setting(&self, changeset: DatabaseSettingChangesetPB) -> Option<FlowyError> {
  734. EventBuilder::new(self.clone())
  735. .event(DatabaseEvent::UpdateDatabaseSetting)
  736. .payload(changeset)
  737. .async_send()
  738. .await
  739. .error()
  740. }
  741. pub async fn get_all_calendar_events(&self, view_id: &str) -> Vec<CalendarEventPB> {
  742. EventBuilder::new(self.clone())
  743. .event(DatabaseEvent::GetAllCalendarEvents)
  744. .payload(CalendarEventRequestPB {
  745. view_id: view_id.to_string(),
  746. })
  747. .async_send()
  748. .await
  749. .parse::<RepeatedCalendarEventPB>()
  750. .items
  751. }
  752. pub async fn get_view(&self, view_id: &str) -> ViewPB {
  753. EventBuilder::new(self.clone())
  754. .event(FolderEvent::ReadView)
  755. .payload(ViewIdPB {
  756. value: view_id.to_string(),
  757. })
  758. .async_send()
  759. .await
  760. .parse::<flowy_folder2::entities::ViewPB>()
  761. }
  762. }
  763. impl std::ops::Deref for FlowyCoreTest {
  764. type Target = AppFlowyCore;
  765. fn deref(&self) -> &Self::Target {
  766. &self.inner
  767. }
  768. }
  769. #[derive(Clone)]
  770. pub struct TestNotificationSender {
  771. sender: Arc<Sender<SubscribeObject>>,
  772. }
  773. impl Default for TestNotificationSender {
  774. fn default() -> Self {
  775. let (sender, _) = channel(1000);
  776. Self {
  777. sender: Arc::new(sender),
  778. }
  779. }
  780. }
  781. impl TestNotificationSender {
  782. pub fn new() -> Self {
  783. Self::default()
  784. }
  785. pub fn subscribe<T>(&self, id: &str, ty: impl Into<i32> + Send) -> tokio::sync::mpsc::Receiver<T>
  786. where
  787. T: TryFrom<Bytes, Error = ProtobufError> + Send + 'static,
  788. {
  789. let id = id.to_string();
  790. let (tx, rx) = tokio::sync::mpsc::channel::<T>(10);
  791. let mut receiver = self.sender.subscribe();
  792. let ty = ty.into();
  793. tokio::spawn(async move {
  794. // DatabaseNotification::DidUpdateDatabaseSnapshotState
  795. while let Ok(value) = receiver.recv().await {
  796. if value.id == id && value.ty == ty {
  797. if let Some(payload) = value.payload {
  798. match T::try_from(Bytes::from(payload)) {
  799. Ok(object) => {
  800. let _ = tx.send(object).await;
  801. },
  802. Err(e) => {
  803. panic!(
  804. "Failed to parse notification payload to type: {:?} with error: {}",
  805. std::any::type_name::<T>(),
  806. e
  807. );
  808. },
  809. }
  810. }
  811. }
  812. }
  813. });
  814. rx
  815. }
  816. pub fn subscribe_with_condition<T, F>(&self, id: &str, when: F) -> tokio::sync::mpsc::Receiver<T>
  817. where
  818. T: TryFrom<Bytes, Error = ProtobufError> + Send + 'static,
  819. F: Fn(&T) -> bool + Send + 'static,
  820. {
  821. let id = id.to_string();
  822. let (tx, rx) = tokio::sync::mpsc::channel::<T>(10);
  823. let mut receiver = self.sender.subscribe();
  824. tokio::spawn(async move {
  825. while let Ok(value) = receiver.recv().await {
  826. if value.id == id {
  827. if let Some(payload) = value.payload {
  828. if let Ok(object) = T::try_from(Bytes::from(payload)) {
  829. if when(&object) {
  830. let _ = tx.send(object).await;
  831. }
  832. }
  833. }
  834. }
  835. }
  836. });
  837. rx
  838. }
  839. }
  840. impl NotificationSender for TestNotificationSender {
  841. fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> {
  842. let _ = self.sender.send(subject);
  843. Ok(())
  844. }
  845. }
  846. pub struct Cleaner(PathBuf);
  847. impl Cleaner {
  848. pub fn new(dir: PathBuf) -> Self {
  849. Cleaner(dir)
  850. }
  851. fn cleanup(dir: &PathBuf) {
  852. let _ = std::fs::remove_dir_all(dir);
  853. }
  854. }
  855. impl Drop for Cleaner {
  856. fn drop(&mut self) {
  857. Self::cleanup(&self.0)
  858. }
  859. }
  860. pub fn third_party_sign_up_param(uuid: String) -> HashMap<String, String> {
  861. let mut params = HashMap::new();
  862. params.insert(USER_UUID.to_string(), uuid);
  863. params.insert(
  864. USER_EMAIL.to_string(),
  865. format!("{}@test.com", Uuid::new_v4()),
  866. );
  867. params.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
  868. params
  869. }
  870. pub fn assert_document_data_equal(collab_update: &[u8], doc_id: &str, expected: DocumentData) {
  871. let collab = MutexCollab::new(CollabOrigin::Server, doc_id, vec![]);
  872. collab.lock().with_origin_transact_mut(|txn| {
  873. let update = Update::decode_v1(collab_update).unwrap();
  874. txn.apply_update(update);
  875. });
  876. let document = Document::open(Arc::new(collab)).unwrap();
  877. let actual = document.get_document_data().unwrap();
  878. assert_eq!(actual, expected);
  879. }