mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-10-26 07:31:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			278 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::fs::{create_dir_all, File, OpenOptions};
 | |
| use std::io::copy;
 | |
| use std::ops::Deref;
 | |
| use std::path::{Path, PathBuf};
 | |
| use std::sync::Arc;
 | |
| use std::time::Duration;
 | |
| use std::{fs, io};
 | |
| 
 | |
| use anyhow::Error;
 | |
| use collab_folder::FolderData;
 | |
| use collab_plugins::cloud_storage::RemoteCollabStorage;
 | |
| use nanoid::nanoid;
 | |
| use tokio::sync::mpsc::Receiver;
 | |
| 
 | |
| use tokio::time::timeout;
 | |
| use uuid::Uuid;
 | |
| use walkdir::WalkDir;
 | |
| use zip::write::FileOptions;
 | |
| use zip::{CompressionMethod, ZipArchive, ZipWriter};
 | |
| 
 | |
| use event_integration_test::event_builder::EventBuilder;
 | |
| use event_integration_test::Cleaner;
 | |
| use event_integration_test::EventIntegrationTest;
 | |
| use flowy_database_pub::cloud::DatabaseCloudService;
 | |
| use flowy_folder_pub::cloud::{FolderCloudService, FolderSnapshot};
 | |
| use flowy_server::supabase::api::*;
 | |
| use flowy_server::{AppFlowyEncryption, EncryptionImpl};
 | |
| use flowy_server_pub::supabase_config::SupabaseConfiguration;
 | |
| use flowy_user::entities::{AuthenticatorPB, UpdateUserProfilePayloadPB};
 | |
| use flowy_user::errors::FlowyError;
 | |
| 
 | |
| use flowy_user::event_map::UserEvent::*;
 | |
| use flowy_user_pub::cloud::UserCloudService;
 | |
| use flowy_user_pub::entities::Authenticator;
 | |
| 
 | |
| pub fn get_supabase_config() -> Option<SupabaseConfiguration> {
 | |
|   dotenv::from_path(".env.ci").ok()?;
 | |
|   SupabaseConfiguration::from_env().ok()
 | |
| }
 | |
| 
 | |
| pub struct FlowySupabaseTest {
 | |
|   event_test: EventIntegrationTest,
 | |
| }
 | |
| 
 | |
| impl FlowySupabaseTest {
 | |
|   pub async fn new() -> Option<Self> {
 | |
|     let _ = get_supabase_config()?;
 | |
|     let event_test = EventIntegrationTest::new().await;
 | |
|     event_test.set_auth_type(AuthenticatorPB::Supabase);
 | |
|     event_test
 | |
|       .server_provider
 | |
|       .set_authenticator(Authenticator::Supabase);
 | |
| 
 | |
|     Some(Self { event_test })
 | |
|   }
 | |
| 
 | |
|   pub async fn update_user_profile(
 | |
|     &self,
 | |
|     payload: UpdateUserProfilePayloadPB,
 | |
|   ) -> Option<FlowyError> {
 | |
|     EventBuilder::new(self.event_test.clone())
 | |
|       .event(UpdateUserProfile)
 | |
|       .payload(payload)
 | |
|       .async_send()
 | |
|       .await
 | |
|       .error()
 | |
|   }
 | |
| }
 | |
| 
 | |
| impl Deref for FlowySupabaseTest {
 | |
|   type Target = EventIntegrationTest;
 | |
| 
 | |
|   fn deref(&self) -> &Self::Target {
 | |
|     &self.event_test
 | |
|   }
 | |
| }
 | |
| 
 | |
| pub async fn receive_with_timeout<T>(mut receiver: Receiver<T>, duration: Duration) -> Option<T> {
 | |
|   timeout(duration, receiver.recv()).await.ok()?
 | |
| }
 | |
| 
 | |
| pub fn get_supabase_ci_config() -> Option<SupabaseConfiguration> {
 | |
|   dotenv::from_filename("./.env.ci").ok()?;
 | |
|   SupabaseConfiguration::from_env().ok()
 | |
| }
 | |
| 
 | |
| #[allow(dead_code)]
 | |
| pub fn get_supabase_dev_config() -> Option<SupabaseConfiguration> {
 | |
|   dotenv::from_filename("./.env.dev").ok()?;
 | |
|   SupabaseConfiguration::from_env().ok()
 | |
| }
 | |
| 
 | |
| pub fn collab_service() -> Arc<dyn RemoteCollabStorage> {
 | |
|   let (server, encryption_impl) = appflowy_server(None);
 | |
|   Arc::new(SupabaseCollabStorageImpl::new(
 | |
|     server,
 | |
|     None,
 | |
|     Arc::downgrade(&encryption_impl),
 | |
|   ))
 | |
| }
 | |
| 
 | |
| pub fn database_service() -> Arc<dyn DatabaseCloudService> {
 | |
|   let (server, _encryption_impl) = appflowy_server(None);
 | |
|   Arc::new(SupabaseDatabaseServiceImpl::new(server))
 | |
| }
 | |
| 
 | |
| pub fn user_auth_service() -> Arc<dyn UserCloudService> {
 | |
|   let (server, _encryption_impl) = appflowy_server(None);
 | |
|   Arc::new(SupabaseUserServiceImpl::new(server, vec![], None))
 | |
| }
 | |
| 
 | |
| pub fn folder_service() -> Arc<dyn FolderCloudService> {
 | |
|   let (server, _encryption_impl) = appflowy_server(None);
 | |
|   Arc::new(SupabaseFolderServiceImpl::new(server))
 | |
| }
 | |
| 
 | |
| #[allow(dead_code)]
 | |
| pub fn encryption_folder_service(
 | |
|   secret: Option<String>,
 | |
| ) -> (Arc<dyn FolderCloudService>, Arc<dyn AppFlowyEncryption>) {
 | |
|   let (server, encryption_impl) = appflowy_server(secret);
 | |
|   let service = Arc::new(SupabaseFolderServiceImpl::new(server));
 | |
|   (service, encryption_impl)
 | |
| }
 | |
| 
 | |
| pub fn encryption_collab_service(
 | |
|   secret: Option<String>,
 | |
| ) -> (Arc<dyn RemoteCollabStorage>, Arc<dyn AppFlowyEncryption>) {
 | |
|   let (server, encryption_impl) = appflowy_server(secret);
 | |
|   let service = Arc::new(SupabaseCollabStorageImpl::new(
 | |
|     server,
 | |
|     None,
 | |
|     Arc::downgrade(&encryption_impl),
 | |
|   ));
 | |
|   (service, encryption_impl)
 | |
| }
 | |
| 
 | |
| pub async fn get_folder_data_from_server(
 | |
|   uid: &i64,
 | |
|   folder_id: &str,
 | |
|   encryption_secret: Option<String>,
 | |
| ) -> Result<Option<FolderData>, Error> {
 | |
|   let (cloud_service, _encryption) = encryption_folder_service(encryption_secret);
 | |
|   cloud_service.get_folder_data(folder_id, uid).await
 | |
| }
 | |
| 
 | |
| pub async fn get_folder_snapshots(
 | |
|   folder_id: &str,
 | |
|   encryption_secret: Option<String>,
 | |
| ) -> Vec<FolderSnapshot> {
 | |
|   let (cloud_service, _encryption) = encryption_folder_service(encryption_secret);
 | |
|   cloud_service
 | |
|     .get_folder_snapshots(folder_id, 10)
 | |
|     .await
 | |
|     .unwrap()
 | |
| }
 | |
| 
 | |
| pub fn appflowy_server(
 | |
|   encryption_secret: Option<String>,
 | |
| ) -> (SupabaseServerServiceImpl, Arc<dyn AppFlowyEncryption>) {
 | |
|   let config = SupabaseConfiguration::from_env().unwrap();
 | |
|   let encryption_impl: Arc<dyn AppFlowyEncryption> =
 | |
|     Arc::new(EncryptionImpl::new(encryption_secret));
 | |
|   let encryption = Arc::downgrade(&encryption_impl);
 | |
|   let server = Arc::new(RESTfulPostgresServer::new(config, encryption));
 | |
|   (SupabaseServerServiceImpl::new(server), encryption_impl)
 | |
| }
 | |
| 
 | |
| /// zip the asset to the destination
 | |
| /// Zips the specified directory into a zip file.
 | |
| ///
 | |
| /// # Arguments
 | |
| /// - `src_dir`: Path to the directory to zip.
 | |
| /// - `output_file`: Path to the output zip file.
 | |
| ///
 | |
| /// # Errors
 | |
| /// Returns `io::Result<()>` indicating the operation's success or failure.
 | |
| pub fn zip(src_dir: PathBuf, output_file_path: PathBuf) -> io::Result<()> {
 | |
|   // Ensure the output directory exists
 | |
|   if let Some(parent) = output_file_path.parent() {
 | |
|     if !parent.exists() {
 | |
|       fs::create_dir_all(parent)?;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Open or create the output file, truncating it if it exists
 | |
|   let file = OpenOptions::new()
 | |
|     .create(true)
 | |
|     .write(true)
 | |
|     .truncate(true)
 | |
|     .open(&output_file_path)?;
 | |
| 
 | |
|   let options = FileOptions::default().compression_method(CompressionMethod::Deflated);
 | |
| 
 | |
|   let mut zip = ZipWriter::new(file);
 | |
| 
 | |
|   // Calculate the name of the new folder within the ZIP file based on the last component of the output path
 | |
|   let new_folder_name = output_file_path
 | |
|     .file_stem()
 | |
|     .and_then(|name| name.to_str())
 | |
|     .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid output file name"))?;
 | |
| 
 | |
|   let src_dir_str = src_dir.to_str().expect("Invalid source directory path");
 | |
| 
 | |
|   for entry in WalkDir::new(&src_dir).into_iter().filter_map(|e| e.ok()) {
 | |
|     let path = entry.path();
 | |
|     let relative_path = path
 | |
|       .strip_prefix(src_dir_str)
 | |
|       .map_err(|_| io::Error::new(io::ErrorKind::Other, "Error calculating relative path"))?;
 | |
| 
 | |
|     // Construct the path within the ZIP, prefixing with the new folder's name
 | |
|     let zip_path = Path::new(new_folder_name).join(relative_path);
 | |
| 
 | |
|     if path.is_file() {
 | |
|       zip.start_file(
 | |
|         zip_path
 | |
|           .to_str()
 | |
|           .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid file name"))?,
 | |
|         options,
 | |
|       )?;
 | |
| 
 | |
|       let mut f = File::open(path)?;
 | |
|       io::copy(&mut f, &mut zip)?;
 | |
|     } else if entry.file_type().is_dir() && !relative_path.as_os_str().is_empty() {
 | |
|       zip.add_directory(
 | |
|         zip_path
 | |
|           .to_str()
 | |
|           .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid directory name"))?,
 | |
|         options,
 | |
|       )?;
 | |
|     }
 | |
|   }
 | |
|   zip.finish()?;
 | |
|   Ok(())
 | |
| }
 | |
| pub fn unzip_test_asset(folder_name: &str) -> io::Result<(Cleaner, PathBuf)> {
 | |
|   unzip("./tests/asset", folder_name)
 | |
| }
 | |
| 
 | |
| pub fn unzip(root: &str, folder_name: &str) -> io::Result<(Cleaner, PathBuf)> {
 | |
|   // Open the zip file
 | |
|   let zip_file_path = format!("{}/{}.zip", root, folder_name);
 | |
|   let reader = File::open(zip_file_path)?;
 | |
|   let output_folder_path = format!("{}/unit_test_{}", root, nanoid!(6));
 | |
| 
 | |
|   // Create a ZipArchive from the file
 | |
|   let mut archive = ZipArchive::new(reader)?;
 | |
| 
 | |
|   // Iterate through each file in the zip
 | |
|   for i in 0..archive.len() {
 | |
|     let mut file = archive.by_index(i)?;
 | |
|     let output_path = Path::new(&output_folder_path).join(file.mangled_name());
 | |
| 
 | |
|     if file.name().ends_with('/') {
 | |
|       // Create directory
 | |
|       create_dir_all(&output_path)?;
 | |
|     } else {
 | |
|       // Write file
 | |
|       if let Some(p) = output_path.parent() {
 | |
|         if !p.exists() {
 | |
|           create_dir_all(p)?;
 | |
|         }
 | |
|       }
 | |
|       let mut outfile = File::create(&output_path)?;
 | |
|       copy(&mut file, &mut outfile)?;
 | |
|     }
 | |
|   }
 | |
|   let path = format!("{}/{}", output_folder_path, folder_name);
 | |
|   Ok((
 | |
|     Cleaner::new(PathBuf::from(output_folder_path)),
 | |
|     PathBuf::from(path),
 | |
|   ))
 | |
| }
 | |
| 
 | |
| pub fn generate_test_email() -> String {
 | |
|   format!("{}@test.com", Uuid::new_v4())
 | |
| }
 | 
