AppFlowy/frontend/rust-lib/flowy-user/src/migrations/document_empty_content.rs
Nathan.fooo d86431dfbc
fix: migrate cloud document when the document content is not sync to … (#4108)
* fix: migrate cloud document when the document content is not sync to local

* chore: clippy
2023-12-07 01:14:02 +08:00

91 lines
3.1 KiB
Rust

use std::sync::Arc;
use collab::core::collab::MutexCollab;
use collab::core::origin::{CollabClient, CollabOrigin};
use collab_document::document::Document;
use collab_document::document_data::default_document_data;
use collab_folder::{Folder, View};
use tracing::{event, instrument};
use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_user_deps::entities::Authenticator;
use crate::migrations::migration::UserDataMigration;
use crate::migrations::util::load_collab;
use crate::services::entities::Session;
/// Migrate the first level documents of the workspace by inserting documents
pub struct HistoricalEmptyDocumentMigration;
impl UserDataMigration for HistoricalEmptyDocumentMigration {
fn name(&self) -> &str {
"historical_empty_document"
}
#[instrument(name = "HistoricalEmptyDocumentMigration", skip_all, err)]
fn run(
&self,
session: &Session,
collab_db: &Arc<RocksCollabDB>,
authenticator: &Authenticator,
) -> FlowyResult<()> {
// - The `empty document` struct has already undergone refactoring prior to the launch of the AppFlowy cloud version.
// - Consequently, if a user is utilizing the AppFlowy cloud version, there is no need to perform any migration for the `empty document` struct.
// - This migration step is only necessary for users who are transitioning from a local version of AppFlowy to the cloud version.
if !matches!(authenticator, Authenticator::Local) {
return Ok(());
}
let write_txn = collab_db.write_txn();
let origin = CollabOrigin::Client(CollabClient::new(session.user_id, "phantom"));
let folder_collab = match load_collab(session.user_id, &write_txn, &session.user_workspace.id) {
Ok(fc) => fc,
Err(_) => return Ok(()),
};
let folder = Folder::open(session.user_id, folder_collab, None)?;
let migration_views = folder.get_workspace_views();
// For historical reasons, the first level documents are empty. So migrate them by inserting
// the default document data.
for view in migration_views {
if migrate_empty_document(&write_txn, &origin, &view, session.user_id).is_err() {
event!(
tracing::Level::ERROR,
"Failed to migrate document {}",
view.id
);
}
}
write_txn.commit_transaction().map_err(internal_error)?;
Ok(())
}
}
fn migrate_empty_document<'a, W>(
write_txn: &W,
origin: &CollabOrigin,
view: &View,
user_id: i64,
) -> Result<(), FlowyError>
where
W: YrsDocAction<'a>,
PersistenceError: From<W::Error>,
{
// If the document is not exist, we don't need to migrate it.
if load_collab(user_id, write_txn, &view.id).is_err() {
let collab = Arc::new(MutexCollab::new(origin.clone(), &view.id, vec![]));
let document = Document::create_with_data(collab, default_document_data())?;
let encode = document.get_collab().encode_collab_v1();
write_txn.flush_doc_with(user_id, &view.id, &encode.doc_state, &encode.state_vector)?;
event!(
tracing::Level::INFO,
"Did migrate empty document {}",
view.id
);
}
Ok(())
}