Merge pull request #273 from AppFlowy-IO/create_workspace_with_template
refactor: template
This commit is contained in:
commit
2a7b0ad75f
|
|
@ -0,0 +1,55 @@
|
|||
use crate::document::parser::JsonToDocumentParser;
|
||||
use crate::hierarchy_builder::WorkspaceViewBuilder;
|
||||
use crate::{TemplateData, WorkspaceTemplate};
|
||||
use async_trait::async_trait;
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab::core::origin::CollabOrigin;
|
||||
use collab_document::document::Document;
|
||||
use collab_entity::CollabType;
|
||||
use collab_folder::ViewLayout;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
/// This template generates a document containing a 'read me' guide.
|
||||
/// It ensures that at least one view is created for the document.
|
||||
pub struct GetStartedDocumentTemplate;
|
||||
|
||||
#[async_trait]
|
||||
impl WorkspaceTemplate for GetStartedDocumentTemplate {
|
||||
fn layout(&self) -> ViewLayout {
|
||||
ViewLayout::Document
|
||||
}
|
||||
|
||||
async fn create_workspace_view(
|
||||
&self,
|
||||
_uid: i64,
|
||||
workspace_view_builder: Arc<RwLock<WorkspaceViewBuilder>>,
|
||||
) -> anyhow::Result<TemplateData> {
|
||||
let view_id = workspace_view_builder
|
||||
.write()
|
||||
.await
|
||||
.with_view_builder(|view_builder| async {
|
||||
view_builder
|
||||
.with_name("Getting started")
|
||||
.with_icon("⭐️")
|
||||
.build()
|
||||
})
|
||||
.await;
|
||||
|
||||
// create a empty document
|
||||
let data = tokio::task::spawn_blocking(|| {
|
||||
let json_str = include_str!("../../assets/read_me.json");
|
||||
let document_data = JsonToDocumentParser::json_str_to_document(json_str).unwrap();
|
||||
let collab = Arc::new(MutexCollab::new(CollabOrigin::Empty, &view_id, vec![]));
|
||||
let document = Document::create_with_data(collab, document_data)?;
|
||||
let data = document.get_collab().encode_collab_v1();
|
||||
Ok::<_, anyhow::Error>(TemplateData {
|
||||
object_id: view_id,
|
||||
object_type: CollabType::Document,
|
||||
object_data: data,
|
||||
})
|
||||
})
|
||||
.await??;
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +1,2 @@
|
|||
pub mod get_started;
|
||||
mod parser;
|
||||
|
||||
use crate::document::parser::JsonToDocumentParser;
|
||||
use crate::hierarchy_builder::WorkspaceViewBuilder;
|
||||
use crate::{TemplateData, WorkspaceTemplate};
|
||||
use async_trait::async_trait;
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab::core::origin::CollabOrigin;
|
||||
use collab_document::document::Document;
|
||||
use collab_entity::CollabType;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
/// A default template for creating documents.
|
||||
///
|
||||
/// This template generates a document containing a 'read me' guide.
|
||||
/// It ensures that at least one view is created for the document.
|
||||
pub struct DocumentTemplate;
|
||||
|
||||
#[async_trait]
|
||||
impl WorkspaceTemplate for DocumentTemplate {
|
||||
async fn create_workspace_view(
|
||||
&self,
|
||||
_uid: i64,
|
||||
workspace_view_builder: Arc<RwLock<WorkspaceViewBuilder>>,
|
||||
) -> anyhow::Result<TemplateData> {
|
||||
let view_id = workspace_view_builder
|
||||
.write()
|
||||
.await
|
||||
.with_view_builder(|view_builder| async {
|
||||
view_builder
|
||||
.with_name("Getting started")
|
||||
.with_icon("⭐️")
|
||||
.build()
|
||||
})
|
||||
.await;
|
||||
|
||||
// create a empty document
|
||||
let data = tokio::task::spawn_blocking(|| {
|
||||
let json_str = include_str!("../../assets/read_me.json");
|
||||
let document_data = JsonToDocumentParser::json_str_to_document(json_str).unwrap();
|
||||
let collab = Arc::new(MutexCollab::new(CollabOrigin::Empty, &view_id, vec![]));
|
||||
let document = Document::create_with_data(collab, document_data)?;
|
||||
let data = document.get_collab().encode_collab_v1();
|
||||
Ok::<_, anyhow::Error>(TemplateData {
|
||||
object_id: view_id,
|
||||
object_type: CollabType::Document,
|
||||
object_data: data,
|
||||
})
|
||||
})
|
||||
.await??;
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
mod document;
|
||||
pub mod document;
|
||||
mod hierarchy_builder;
|
||||
|
||||
use crate::hierarchy_builder::{FlattedViews, WorkspaceViewBuilder};
|
||||
|
|
@ -17,6 +17,8 @@ use tokio::sync::RwLock;
|
|||
|
||||
#[async_trait]
|
||||
pub trait WorkspaceTemplate {
|
||||
fn layout(&self) -> ViewLayout;
|
||||
|
||||
async fn create_workspace_view(
|
||||
&self,
|
||||
uid: i64,
|
||||
|
|
@ -32,6 +34,8 @@ pub struct TemplateData {
|
|||
|
||||
pub type WorkspaceTemplateHandlers = HashMap<ViewLayout, Arc<dyn WorkspaceTemplate + Send + Sync>>;
|
||||
|
||||
/// A builder for creating a workspace template.
|
||||
/// workspace template is a set of views that are created when a workspace is created.
|
||||
pub struct WorkspaceTemplateBuilder {
|
||||
pub uid: i64,
|
||||
pub workspace_id: String,
|
||||
|
|
@ -40,9 +44,7 @@ pub struct WorkspaceTemplateBuilder {
|
|||
|
||||
impl WorkspaceTemplateBuilder {
|
||||
pub fn new(uid: i64, workspace_id: &str) -> Self {
|
||||
let mut handlers = WorkspaceTemplateHandlers::default();
|
||||
// register the document template handler
|
||||
handlers.insert(ViewLayout::Document, Arc::new(document::DocumentTemplate));
|
||||
let handlers = WorkspaceTemplateHandlers::default();
|
||||
Self {
|
||||
uid,
|
||||
workspace_id: workspace_id.to_string(),
|
||||
|
|
@ -50,7 +52,25 @@ impl WorkspaceTemplateBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn default_workspace(&self) -> Result<Vec<TemplateData>> {
|
||||
pub fn with_template<T>(mut self, template: T) -> Self
|
||||
where
|
||||
T: WorkspaceTemplate + Send + Sync + 'static,
|
||||
{
|
||||
self.handlers.insert(template.layout(), Arc::new(template));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_templates<T>(mut self, templates: Vec<T>) -> Self
|
||||
where
|
||||
T: WorkspaceTemplate + Send + Sync + 'static,
|
||||
{
|
||||
for template in templates {
|
||||
self.handlers.insert(template.layout(), Arc::new(template));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn build(&self) -> Result<Vec<TemplateData>> {
|
||||
let workspace_view_builder = Arc::new(RwLock::new(WorkspaceViewBuilder::new(
|
||||
self.workspace_id.clone(),
|
||||
self.uid,
|
||||
|
|
|
|||
100
src/biz/user.rs
100
src/biz/user.rs
|
|
@ -22,10 +22,11 @@ use database::user::{create_user, is_user_exist};
|
|||
use realtime::entities::RealtimeUser;
|
||||
use shared_entity::dto::auth_dto::UpdateUserParams;
|
||||
use snowflake::Snowflake;
|
||||
use sqlx::{types::uuid, PgPool};
|
||||
use sqlx::{types::uuid, PgPool, Transaction};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, event, instrument};
|
||||
use workspace_template::WorkspaceTemplateBuilder;
|
||||
use workspace_template::document::get_started::GetStartedDocumentTemplate;
|
||||
use workspace_template::{WorkspaceTemplate, WorkspaceTemplateBuilder};
|
||||
|
||||
/// Verify the token from the gotrue server and create the user if it is a new user
|
||||
/// Return true if the user is a new user
|
||||
|
|
@ -86,41 +87,15 @@ where
|
|||
)
|
||||
.await?;
|
||||
|
||||
// Create the default workspace for the user. A default workspace might contain multiple
|
||||
// templates, e.g. a document template, a database template, etc.
|
||||
let templates = WorkspaceTemplateBuilder::new(new_uid, &workspace_id)
|
||||
.default_workspace()
|
||||
.await?;
|
||||
|
||||
debug!("create {} templates for user:{}", templates.len(), new_uid);
|
||||
for template in templates {
|
||||
let object_id = template.object_id;
|
||||
let encoded_collab_v1 = template
|
||||
.object_data
|
||||
.encode_to_bytes()
|
||||
.map_err(|err| AppError::Internal(anyhow::Error::from(err)))?;
|
||||
|
||||
collab_access_control
|
||||
.cache_collab_access_level(
|
||||
realtime::collaborate::CollabUserId::UserId(&new_uid),
|
||||
&object_id,
|
||||
AFAccessLevel::FullAccess,
|
||||
)
|
||||
.await?;
|
||||
|
||||
insert_into_af_collab(
|
||||
&mut txn,
|
||||
&new_uid,
|
||||
&workspace_id,
|
||||
&CollabParams {
|
||||
object_id,
|
||||
encoded_collab_v1,
|
||||
collab_type: template.object_type,
|
||||
override_if_exist: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
// Create a workspace with the GetStarted template
|
||||
create_workspace_for_user(
|
||||
new_uid,
|
||||
&workspace_id,
|
||||
collab_access_control,
|
||||
&mut txn,
|
||||
vec![GetStartedDocumentTemplate],
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
txn
|
||||
.commit()
|
||||
|
|
@ -129,6 +104,57 @@ where
|
|||
Ok(is_new)
|
||||
}
|
||||
|
||||
/// Create a workspace for a user.
|
||||
/// This function generates a workspace along with its templates and stores them in the database.
|
||||
/// Each template is stored as an individual collaborative object.
|
||||
async fn create_workspace_for_user<C, T>(
|
||||
new_uid: i64,
|
||||
workspace_id: &str,
|
||||
collab_access_control: &C,
|
||||
txn: &mut Transaction<'_, sqlx::Postgres>,
|
||||
templates: Vec<T>,
|
||||
) -> Result<(), AppError>
|
||||
where
|
||||
C: CollabAccessControl,
|
||||
T: WorkspaceTemplate + Send + Sync + 'static,
|
||||
{
|
||||
let templates = WorkspaceTemplateBuilder::new(new_uid, workspace_id)
|
||||
.with_templates(templates)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
debug!("create {} templates for user:{}", templates.len(), new_uid);
|
||||
for template in templates {
|
||||
let object_id = template.object_id;
|
||||
let encoded_collab_v1 = template
|
||||
.object_data
|
||||
.encode_to_bytes()
|
||||
.map_err(|err| AppError::Internal(anyhow::Error::from(err)))?;
|
||||
|
||||
collab_access_control
|
||||
.cache_collab_access_level(
|
||||
realtime::collaborate::CollabUserId::UserId(&new_uid),
|
||||
&object_id,
|
||||
AFAccessLevel::FullAccess,
|
||||
)
|
||||
.await?;
|
||||
|
||||
insert_into_af_collab(
|
||||
txn,
|
||||
&new_uid,
|
||||
workspace_id,
|
||||
&CollabParams {
|
||||
object_id,
|
||||
encoded_collab_v1,
|
||||
collab_type: template.object_type,
|
||||
override_if_exist: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_profile(pg_pool: &PgPool, uuid: &Uuid) -> Result<AFUserProfile, AppError> {
|
||||
let row = select_user_profile(pg_pool, uuid)
|
||||
.await?
|
||||
|
|
|
|||
Loading…
Reference in New Issue