From c533d12df09ec1a7a85bb925ecd7519211920f8d Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 21 Feb 2024 22:42:10 +1100 Subject: [PATCH] media data sync (#2102) * basic sync operation backfill * media data sync * sync entry helpers * fix sync generator * nicer * re-add key_id --- core/crates/sync/src/backfill.rs | 150 +++++++++++++-------- core/crates/sync/tests/lib.rs | 13 +- core/prisma/schema.prisma | 1 + core/src/api/search/saved.rs | 86 +++--------- core/src/location/indexer/mod.rs | 118 ++++++---------- core/src/location/manager/watcher/utils.rs | 67 +++++---- core/src/object/media/mod.rs | 30 +++-- crates/sync-generator/src/sync_data.rs | 52 ++++++- crates/sync/src/factory.rs | 30 +++++ packages/client/src/core.ts | 4 +- 10 files changed, 293 insertions(+), 258 deletions(-) diff --git a/core/crates/sync/src/backfill.rs b/core/crates/sync/src/backfill.rs index c9d1023ce..708968caf 100644 --- a/core/crates/sync/src/backfill.rs +++ b/core/crates/sync/src/backfill.rs @@ -1,10 +1,11 @@ use sd_prisma::{ prisma::{ - file_path, label, label_on_object, location, object, tag, tag_on_object, PrismaClient, + file_path, label, label_on_object, location, media_data, object, tag, tag_on_object, + PrismaClient, }, prisma_sync, }; -use sd_sync::OperationFactory; +use sd_sync::{option_sync_entry, OperationFactory}; use sd_utils::chain_optional_iter; use serde_json::json; @@ -23,28 +24,26 @@ pub async fn backfill_operations(db: &PrismaClient, sync: &crate::Manager, insta locations .into_iter() .flat_map(|l| { + use location::*; + sync.shared_create( prisma_sync::location::SyncId { pub_id: l.pub_id }, chain_optional_iter( [], [ - l.name.map(|v| (location::name::NAME, json!(v))), - l.path.map(|v| (location::path::NAME, json!(v))), - l.total_capacity - .map(|v| (location::total_capacity::NAME, json!(v))), - l.available_capacity - .map(|v| (location::available_capacity::NAME, json!(v))), - l.size_in_bytes - .map(|v| (location::size_in_bytes::NAME, json!(v))), - l.is_archived - .map(|v| (location::is_archived::NAME, json!(v))), - l.generate_preview_media - .map(|v| (location::generate_preview_media::NAME, json!(v))), - l.sync_preview_media - .map(|v| (location::sync_preview_media::NAME, json!(v))), - l.hidden.map(|v| (location::hidden::NAME, json!(v))), - l.date_created - .map(|v| (location::date_created::NAME, json!(v))), + option_sync_entry!(l.name, name), + option_sync_entry!(l.path, path), + option_sync_entry!(l.total_capacity, total_capacity), + option_sync_entry!(l.available_capacity, available_capacity), + option_sync_entry!(l.size_in_bytes, size_in_bytes), + option_sync_entry!(l.is_archived, is_archived), + option_sync_entry!( + l.generate_preview_media, + generate_preview_media + ), + option_sync_entry!(l.sync_preview_media, sync_preview_media), + option_sync_entry!(l.hidden, hidden), + option_sync_entry!(l.date_created, date_created), ], ), ) @@ -62,20 +61,65 @@ pub async fn backfill_operations(db: &PrismaClient, sync: &crate::Manager, insta objects .into_iter() .flat_map(|o| { + use object::*; + sync.shared_create( prisma_sync::object::SyncId { pub_id: o.pub_id }, chain_optional_iter( [], [ - o.kind.map(|v| (object::kind::NAME, json!(v))), - o.hidden.map(|v| (object::hidden::NAME, json!(v))), - o.favorite.map(|v| (object::favorite::NAME, json!(v))), - o.important.map(|v| (object::important::NAME, json!(v))), - o.note.map(|v| (object::note::NAME, json!(v))), - o.date_created - .map(|v| (object::date_created::NAME, json!(v))), - o.date_accessed - .map(|v| (object::date_accessed::NAME, json!(v))), + option_sync_entry!(o.kind, kind), + option_sync_entry!(o.hidden, hidden), + option_sync_entry!(o.favorite, favorite), + option_sync_entry!(o.important, important), + option_sync_entry!(o.note, note), + option_sync_entry!(o.date_created, date_created), + option_sync_entry!(o.date_accessed, date_accessed), + ], + ), + ) + }) + .map(|o| crdt_op_unchecked_db(&o, instance_id)) + .collect(), + ) + .exec() + .await + .unwrap(); + + let media_datas = db + .media_data() + .find_many(vec![]) + .include(media_data::include!({ + object: select { pub_id } + })) + .exec() + .await + .unwrap(); + db.crdt_operation() + .create_many( + media_datas + .into_iter() + .flat_map(|md| { + use media_data::*; + + sync.shared_create( + prisma_sync::media_data::SyncId { + object: prisma_sync::object::SyncId { + pub_id: md.object.pub_id, + }, + }, + chain_optional_iter( + [], + [ + option_sync_entry!(md.resolution, resolution), + option_sync_entry!(md.media_date, media_date), + option_sync_entry!(md.media_location, media_location), + option_sync_entry!(md.camera_data, camera_data), + option_sync_entry!(md.artist, artist), + option_sync_entry!(md.description, description), + option_sync_entry!(md.copyright, copyright), + option_sync_entry!(md.exif_version, exif_version), + option_sync_entry!(md.epoch_time, epoch_time), ], ), ) @@ -103,35 +147,31 @@ pub async fn backfill_operations(db: &PrismaClient, sync: &crate::Manager, insta file_paths .into_iter() .flat_map(|fp| { + use file_path::*; + sync.shared_create( prisma_sync::file_path::SyncId { pub_id: fp.pub_id }, chain_optional_iter( [], [ - fp.is_dir.map(|v| (file_path::is_dir::NAME, json!(v))), - fp.cas_id.map(|v| (file_path::cas_id::NAME, json!(v))), - fp.integrity_checksum - .map(|v| (file_path::integrity_checksum::NAME, json!(v))), - fp.location.map(|l| { - ( - file_path::location::NAME, - json!(prisma_sync::location::SyncId { pub_id: l.pub_id }), - ) - }), - fp.materialized_path - .map(|v| (file_path::materialized_path::NAME, json!(v))), - fp.name.map(|v| (file_path::name::NAME, json!(v))), - fp.extension.map(|v| (file_path::extension::NAME, json!(v))), - fp.hidden.map(|v| (file_path::hidden::NAME, json!(v))), - fp.size_in_bytes_bytes - .map(|v| (file_path::size_in_bytes_bytes::NAME, json!(v))), - fp.inode.map(|v| (file_path::inode::NAME, json!(v))), - fp.date_created - .map(|v| (file_path::date_created::NAME, json!(v))), - fp.date_modified - .map(|v| (file_path::date_modified::NAME, json!(v))), - fp.date_indexed - .map(|v| (file_path::date_indexed::NAME, json!(v))), + option_sync_entry!(fp.is_dir, is_dir), + option_sync_entry!(fp.cas_id, cas_id), + option_sync_entry!(fp.integrity_checksum, integrity_checksum), + option_sync_entry!( + fp.location.map(|l| prisma_sync::location::SyncId { + pub_id: l.pub_id + }), + location + ), + option_sync_entry!(fp.materialized_path, materialized_path), + option_sync_entry!(fp.name, name), + option_sync_entry!(fp.extension, extension), + option_sync_entry!(fp.hidden, hidden), + option_sync_entry!(fp.size_in_bytes_bytes, size_in_bytes_bytes), + option_sync_entry!(fp.inode, inode), + option_sync_entry!(fp.date_created, date_created), + option_sync_entry!(fp.date_modified, date_modified), + option_sync_entry!(fp.date_indexed, date_indexed), ], ), ) @@ -194,8 +234,10 @@ pub async fn backfill_operations(db: &PrismaClient, sync: &crate::Manager, insta }, chain_optional_iter( [], - [t_o.date_created - .map(|v| (tag_on_object::date_created::NAME, json!(v)))], + [option_sync_entry!( + t_o.date_created, + tag_on_object::date_created + )], ), ) }) diff --git a/core/crates/sync/tests/lib.rs b/core/crates/sync/tests/lib.rs index e8521093b..bfbd73f02 100644 --- a/core/crates/sync/tests/lib.rs +++ b/core/crates/sync/tests/lib.rs @@ -169,18 +169,9 @@ async fn bruh() -> Result<(), Box> { use prisma::location; - macro_rules! item { - ($name:ident, $value:expr) => { - ( - (location::$name::NAME, json!($value)), - location::$name::set(Some($value.to_string())), - ) - }; - } - let (sync_ops, db_ops): (Vec<_>, Vec<_>) = [ - item!(name, "Location 0"), - item!(path, "/User/Brendan/Documents"), + sync_db_entry!(location::name, "Location 0"), + sync_db_entry!(location::path, "/User/Brendan/Documents"), ] .into_iter() .unzip(); diff --git a/core/prisma/schema.prisma b/core/prisma/schema.prisma index 73cc986de..08b559ddc 100644 --- a/core/prisma/schema.prisma +++ b/core/prisma/schema.prisma @@ -278,6 +278,7 @@ model Object { // @@map("key") // } +/// @shared(id: object) model MediaData { id Int @id @default(autoincrement()) diff --git a/core/src/api/search/saved.rs b/core/src/api/search/saved.rs index 38f6fac25..58e2b37d8 100644 --- a/core/src/api/search/saved.rs +++ b/core/src/api/search/saved.rs @@ -1,13 +1,12 @@ use crate::{api::utils::library, invalidate_query, library::Library}; use sd_prisma::{prisma::saved_search, prisma_sync}; -use sd_sync::OperationFactory; +use sd_sync::{option_sync_db_entry, sync_db_entry, OperationFactory}; use sd_utils::chain_optional_iter; use chrono::{DateTime, FixedOffset, Utc}; use rspc::alpha::AlphaRouter; use serde::{de::IgnoredAny, Deserialize, Serialize}; -use serde_json::json; use specta::Type; use tracing::error; use uuid::Uuid; @@ -39,18 +38,12 @@ pub(crate) fn mount() -> AlphaRouter { let (sync_params, db_params): (Vec<_>, Vec<_>) = chain_optional_iter( [ - ( - (saved_search::date_created::NAME, json!(date_created)), - saved_search::date_created::set(Some(date_created)), - ), - ( - (saved_search::name::NAME, json!(&args.name)), - saved_search::name::set(Some(args.name)), - ), + sync_db_entry!(date_created, saved_search::date_created), + sync_db_entry!(args.name, saved_search::name), ], [ - args.filters - .and_then(|s| { + option_sync_db_entry!( + args.filters.and_then(|s| { // https://github.com/serde-rs/json/issues/579 // https://docs.rs/serde/latest/serde/de/struct.IgnoredAny.html @@ -60,31 +53,12 @@ pub(crate) fn mount() -> AlphaRouter { } else { Some(s) } - }) - .map(|v| { - ( - (saved_search::filters::NAME, json!(&v)), - saved_search::filters::set(Some(v)), - ) }), - args.search.map(|v| { - ( - (saved_search::search::NAME, json!(&v)), - saved_search::search::set(Some(v)), - ) - }), - args.description.map(|v| { - ( - (saved_search::description::NAME, json!(&v)), - saved_search::description::set(Some(v)), - ) - }), - args.icon.map(|v| { - ( - (saved_search::icon::NAME, json!(&v)), - saved_search::icon::set(Some(v)), - ) - }), + saved_search::filters + ), + option_sync_db_entry!(args.search, saved_search::search), + option_sync_db_entry!(args.description, saved_search::description), + option_sync_db_entry!(args.icon, saved_search::icon), ], ) .into_iter() @@ -157,41 +131,13 @@ pub(crate) fn mount() -> AlphaRouter { })?; let (sync_params, db_params): (Vec<_>, Vec<_>) = chain_optional_iter( - [( - (saved_search::date_modified::NAME, json!(updated_at)), - saved_search::date_modified::set(Some(updated_at)), - )], + [sync_db_entry!(updated_at, saved_search::date_modified)], [ - args.name.map(|v| { - ( - (saved_search::name::NAME, json!(&v)), - saved_search::name::set(v), - ) - }), - args.description.map(|v| { - ( - (saved_search::name::NAME, json!(&v)), - saved_search::name::set(v), - ) - }), - args.icon.map(|v| { - ( - (saved_search::icon::NAME, json!(&v)), - saved_search::icon::set(v), - ) - }), - args.search.map(|v| { - ( - (saved_search::search::NAME, json!(&v)), - saved_search::search::set(v), - ) - }), - args.filters.map(|v| { - ( - (saved_search::filters::NAME, json!(&v)), - saved_search::filters::set(v), - ) - }), + option_sync_db_entry!(args.name.flatten(), saved_search::name), + option_sync_db_entry!(args.description.flatten(), saved_search::name), + option_sync_db_entry!(args.icon.flatten(), saved_search::icon), + option_sync_db_entry!(args.search.flatten(), saved_search::search), + option_sync_db_entry!(args.filters.flatten(), saved_search::filters), ], ) .into_iter() diff --git a/core/src/location/indexer/mod.rs b/core/src/location/indexer/mod.rs index 057c4aecd..a3a9c772d 100644 --- a/core/src/location/indexer/mod.rs +++ b/core/src/location/indexer/mod.rs @@ -116,45 +116,28 @@ async fn execute_indexer_save_step( ), location_id::set(Some(location.id)), ), - ( - (materialized_path::NAME, json!(materialized_path)), - materialized_path::set(Some(materialized_path.to_string())), - ), - ((name::NAME, json!(name)), name::set(Some(name.to_string()))), - ((is_dir::NAME, json!(*is_dir)), is_dir::set(Some(*is_dir))), - ( - (extension::NAME, json!(extension)), - extension::set(Some(extension.to_string())), - ), - ( - ( - size_in_bytes_bytes::NAME, - json!(entry.metadata.size_in_bytes.to_be_bytes().to_vec()), - ), - size_in_bytes_bytes::set(Some( - entry.metadata.size_in_bytes.to_be_bytes().to_vec(), - )), - ), - ( - (inode::NAME, json!(entry.metadata.inode.to_le_bytes())), - inode::set(Some(inode_to_db(entry.metadata.inode))), - ), - ( - (date_created::NAME, json!(entry.metadata.created_at)), - date_created::set(Some(entry.metadata.created_at.into())), - ), - ( - (date_modified::NAME, json!(entry.metadata.modified_at)), - date_modified::set(Some(entry.metadata.modified_at.into())), - ), - ( - (date_indexed::NAME, json!(Utc::now())), - date_indexed::set(Some(Utc::now().into())), - ), - ( - (hidden::NAME, json!(entry.metadata.hidden)), - hidden::set(Some(entry.metadata.hidden)), + sync_db_entry!(materialized_path.to_string(), materialized_path), + sync_db_entry!(name.to_string(), name), + sync_db_entry!(*is_dir, is_dir), + sync_db_entry!(extension.to_string(), extension), + sync_db_entry!( + entry.metadata.size_in_bytes.to_be_bytes().to_vec(), + size_in_bytes_bytes ), + sync_db_entry!(inode_to_db(entry.metadata.inode), inode), + { + let v = entry.metadata.created_at.into(); + sync_db_entry!(v, date_created) + }, + { + let v = entry.metadata.modified_at.into(); + sync_db_entry!(v, date_modified) + }, + { + let v = Utc::now().into(); + sync_db_entry!(v, date_indexed) + }, + sync_db_entry!(entry.metadata.hidden, hidden), ] .into_iter() .unzip(); @@ -212,48 +195,29 @@ async fn execute_indexer_update_step( let (sync_params, db_params): (Vec<_>, Vec<_>) = [ // As this file was updated while Spacedrive was offline, we mark the object_id and cas_id as null // So this file_path will be updated at file identifier job - ( + should_unlink_object.then_some(( (object_id::NAME, serde_json::Value::Null), - should_unlink_object.then_some(object::disconnect()), - ), - ( - (cas_id::NAME, serde_json::Value::Null), - Some(cas_id::set(None)), - ), - ( - (is_dir::NAME, json!(*is_dir)), - Some(is_dir::set(Some(*is_dir))), - ), - ( - ( - size_in_bytes_bytes::NAME, - json!(entry.metadata.size_in_bytes.to_be_bytes().to_vec()), - ), - Some(size_in_bytes_bytes::set(Some( - entry.metadata.size_in_bytes.to_be_bytes().to_vec(), - ))), - ), - ( - (inode::NAME, json!(entry.metadata.inode.to_le_bytes())), - Some(inode::set(Some(inode_to_db(entry.metadata.inode)))), - ), - ( - (date_created::NAME, json!(entry.metadata.created_at)), - Some(date_created::set(Some(entry.metadata.created_at.into()))), - ), - ( - (date_modified::NAME, json!(entry.metadata.modified_at)), - Some(date_modified::set(Some(entry.metadata.modified_at.into()))), - ), - ( - (hidden::NAME, json!(entry.metadata.hidden)), - Some(hidden::set(Some(entry.metadata.hidden))), - ), + object::disconnect(), + )), + Some(((cas_id::NAME, serde_json::Value::Null), cas_id::set(None))), + Some(sync_db_entry!(*is_dir, is_dir)), + Some(sync_db_entry!( + entry.metadata.size_in_bytes.to_be_bytes().to_vec(), + size_in_bytes_bytes + )), + Some(sync_db_entry!(inode_to_db(entry.metadata.inode), inode)), + Some({ + let v = entry.metadata.created_at.into(); + sync_db_entry!(v, date_created) + }), + Some({ + let v = entry.metadata.modified_at.into(); + sync_db_entry!(v, date_modified) + }), + Some(sync_db_entry!(entry.metadata.hidden, hidden)), ] .into_iter() - .filter_map(|(sync_param, maybe_db_param)| { - maybe_db_param.map(|db_param| (sync_param, db_param)) - }) + .flatten() .unzip(); Ok::<_, IndexerError>(( diff --git a/core/src/location/manager/watcher/utils.rs b/core/src/location/manager/watcher/utils.rs index 22c83ea99..6ef8c8cd0 100644 --- a/core/src/location/manager/watcher/utils.rs +++ b/core/src/location/manager/watcher/utils.rs @@ -306,7 +306,7 @@ async fn inner_create_file( db.file_path().update( file_path::pub_id::equals(created_file.pub_id.clone()), vec![file_path::object::connect(object::pub_id::equals( - object_pub_id, + object_pub_id.clone(), ))], ), ) @@ -342,22 +342,30 @@ async fn inner_create_file( .await .map_err(|e| error!("Failed to extract media data: {e:#?}")) { - if let Ok(media_data_params) = media_data_image_to_query_params(media_data) - .map_err(|e| { - error!("Failed to prepare media data create params: {e:#?}") - }) { - db.media_data() - .upsert( + let (sync_params, db_params) = media_data_image_to_query_params(media_data); + + sync.write_ops( + db, + ( + sync.shared_create( + prisma_sync::media_data::SyncId { + object: prisma_sync::object::SyncId { + pub_id: object_pub_id.clone(), + }, + }, + sync_params, + ), + db.media_data().upsert( media_data::object_id::equals(object_id), media_data::create( object::id::equals(object_id), - media_data_params.clone(), + db_params.clone(), ), - media_data_params, - ) - .exec() - .await?; - } + db_params, + ), + ), + ) + .await?; } } } @@ -682,22 +690,31 @@ async fn inner_update_file( .await .map_err(|e| error!("Failed to extract media data: {e:#?}")) { - if let Ok(media_data_params) = - media_data_image_to_query_params(media_data).map_err(|e| { - error!("Failed to prepare media data create params: {e:#?}") - }) { - db.media_data() - .upsert( + let (sync_params, db_params) = + media_data_image_to_query_params(media_data); + + sync.write_ops( + db, + ( + sync.shared_create( + prisma_sync::media_data::SyncId { + object: prisma_sync::object::SyncId { + pub_id: object.pub_id.clone(), + }, + }, + sync_params, + ), + db.media_data().upsert( media_data::object_id::equals(object.id), media_data::create( object::id::equals(object.id), - media_data_params.clone(), + db_params.clone(), ), - media_data_params, - ) - .exec() - .await?; - } + db_params, + ), + ), + ) + .await?; } } } diff --git a/core/src/object/media/mod.rs b/core/src/object/media/mod.rs index 5ab8e93f5..09833ce1c 100644 --- a/core/src/object/media/mod.rs +++ b/core/src/object/media/mod.rs @@ -31,18 +31,24 @@ pub fn media_data_image_to_query( #[cfg(feature = "location-watcher")] pub fn media_data_image_to_query_params( mdi: ImageMetadata, -) -> Result, MediaDataError> { - Ok(vec![ - camera_data::set(serde_json::to_vec(&mdi.camera_data).ok()), - media_date::set(serde_json::to_vec(&mdi.date_taken).ok()), - resolution::set(serde_json::to_vec(&mdi.resolution).ok()), - media_location::set(serde_json::to_vec(&mdi.location).ok()), - artist::set(mdi.artist), - description::set(mdi.description), - copyright::set(mdi.copyright), - exif_version::set(mdi.exif_version), - epoch_time::set(mdi.date_taken.map(|x| x.unix_timestamp())), - ]) +) -> (Vec<(&'static str, serde_json::Value)>, Vec) { + use sd_sync::option_sync_db_entry; + use sd_utils::chain_optional_iter; + + chain_optional_iter( + [], + [ + option_sync_db_entry!(serde_json::to_vec(&mdi.camera_data).ok(), camera_data), + option_sync_db_entry!(serde_json::to_vec(&mdi.date_taken).ok(), media_date), + option_sync_db_entry!(serde_json::to_vec(&mdi.location).ok(), media_location), + option_sync_db_entry!(mdi.artist, artist), + option_sync_db_entry!(mdi.description, description), + option_sync_db_entry!(mdi.copyright, copyright), + option_sync_db_entry!(mdi.exif_version, exif_version), + ], + ) + .into_iter() + .unzip() } pub fn media_data_image_from_prisma_data( diff --git a/crates/sync-generator/src/sync_data.rs b/crates/sync-generator/src/sync_data.rs index 81d7afa21..5b398f9a4 100644 --- a/crates/sync-generator/src/sync_data.rs +++ b/crates/sync-generator/src/sync_data.rs @@ -1,4 +1,7 @@ -use prisma_client_rust_sdk::{prelude::*, prisma::prisma_models::walkers::RelationFieldWalker}; +use prisma_client_rust_sdk::{ + prelude::*, + prisma::prisma_models::walkers::{RefinedFieldWalker, RelationFieldWalker}, +}; use crate::{ModelSyncType, ModelWithSyncType}; @@ -35,15 +38,50 @@ pub fn r#enum(models: Vec) -> TokenStream { let match_arms = match sync_type.as_ref()? { ModelSyncType::Shared { id } => { - let id_name_snake = snake_ident(id.name()); + let (get_id, equals_value, id_name_snake, create_id) = match id.refine() { + RefinedFieldWalker::Relation(rel) => { + let scalar_field = rel.referenced_fields().unwrap().next().unwrap(); + let id_name_snake = snake_ident(scalar_field.name()); + let field_name_snake = snake_ident(rel.name()); + let opposite_model_name_snake = + snake_ident(rel.opposite_relation_field().unwrap().model().name()); + + let relation_equals_condition = quote!(prisma::#opposite_model_name_snake::pub_id::equals( + id.#field_name_snake.pub_id.clone() + )); + + let rel_fetch = quote! { + let rel = db.#opposite_model_name_snake() + .find_unique(#relation_equals_condition) + .exec() + .await? + .unwrap(); + }; + + ( + Some(rel_fetch), + quote!(rel.id), + id_name_snake, + relation_equals_condition, + ) + } + RefinedFieldWalker::Scalar(s) => { + let field_name_snake = snake_ident(s.name()); + let thing = quote!(id.#field_name_snake.clone()); + + (None, thing.clone(), field_name_snake, thing) + } + }; quote! { + #get_id + match data { sd_sync::CRDTOperationData::Create => { db.#model_name_snake() .upsert( - prisma::#model_name_snake::#id_name_snake::equals(id.#id_name_snake.clone()), - prisma::#model_name_snake::create(id.#id_name_snake, vec![]), + prisma::#model_name_snake::#id_name_snake::equals(#equals_value), + prisma::#model_name_snake::create(#create_id, vec![]), vec![] ) .exec() @@ -56,8 +94,8 @@ pub fn r#enum(models: Vec) -> TokenStream { db.#model_name_snake() .upsert( - prisma::#model_name_snake::#id_name_snake::equals(id.#id_name_snake.clone()), - prisma::#model_name_snake::create(id.#id_name_snake, data.clone()), + prisma::#model_name_snake::#id_name_snake::equals(#equals_value), + prisma::#model_name_snake::create(#create_id, data.clone()), data, ) .exec() @@ -65,7 +103,7 @@ pub fn r#enum(models: Vec) -> TokenStream { }, sd_sync::CRDTOperationData::Delete => { db.#model_name_snake() - .delete(prisma::#model_name_snake::#id_name_snake::equals(id.#id_name_snake)) + .delete(prisma::#model_name_snake::#id_name_snake::equals(#equals_value)) .exec() .await?; }, diff --git a/crates/sync/src/factory.rs b/crates/sync/src/factory.rs index e3a5564c1..959509313 100644 --- a/crates/sync/src/factory.rs +++ b/crates/sync/src/factory.rs @@ -106,3 +106,33 @@ pub trait OperationFactory { self.new_op(&id, CRDTOperationData::Delete) } } + +#[macro_export] +macro_rules! sync_entry { + ($v:expr, $($m:tt)*) => {{ + let v = $v; + ($($m)*::NAME, serde_json::json!(&v)) + }} +} + +#[macro_export] +macro_rules! option_sync_entry { + ($v:expr, $($m:tt)*) => { + $v.map(|v| $crate::sync_entry!(v, $($m)*)) + } +} + +#[macro_export] +macro_rules! sync_db_entry { + ($v:expr, $($m:tt)*) => {{ + let v = $v; + ($crate::sync_entry!(&v, $($m)*), $($m)*::set(Some(v))) + }} +} + +#[macro_export] +macro_rules! option_sync_db_entry { + ($v:expr, $($m:tt)*) => { + $v.map(|v| $crate::sync_db_entry!(v, $($m)*)) + }; +} diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index a676394a0..dcda5139f 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -248,7 +248,7 @@ export type FileDeleterJobInit = { location_id: number; file_path_ids: number[] export type FileEraserJobInit = { location_id: number; file_path_ids: number[]; passes: string } -export type FilePath = { id: number; pub_id: number[]; is_dir: boolean | null; cas_id: string | null; integrity_checksum: string | null; location_id: number | null; materialized_path: string | null; name: string | null; extension: string | null; hidden: boolean | null; size_in_bytes: string | null; size_in_bytes_bytes: number[] | null; inode: number[] | null; object_id: number | null; key_id: number | null; date_created: string | null; date_modified: string | null; date_indexed: string | null } +export type FilePath = { id: number; pub_id: number[]; is_dir: boolean | null; cas_id: string | null; integrity_checksum: string | null; location_id: number | null; materialized_path: string | null; name: string | null; extension: string | null; hidden: boolean | null; size_in_bytes: string | null; size_in_bytes_bytes: number[] | null; inode: number[] | null; object_id: number | null; date_created: string | null; date_modified: string | null; date_indexed: string | null } export type FilePathCursor = { isDir: boolean; variant: FilePathCursorVariant } @@ -262,7 +262,7 @@ export type FilePathOrder = { field: "name"; value: SortOrder } | { field: "size export type FilePathSearchArgs = { take?: number | null; orderAndPagination?: OrderAndPagination | null; filters?: SearchFilterArgs[]; groupDirectories?: boolean } -export type FilePathWithObject = { id: number; pub_id: number[]; is_dir: boolean | null; cas_id: string | null; integrity_checksum: string | null; location_id: number | null; materialized_path: string | null; name: string | null; extension: string | null; hidden: boolean | null; size_in_bytes: string | null; size_in_bytes_bytes: number[] | null; inode: number[] | null; object_id: number | null; key_id: number | null; date_created: string | null; date_modified: string | null; date_indexed: string | null; object: { id: number; pub_id: number[]; kind: number | null; key_id: number | null; hidden: boolean | null; favorite: boolean | null; important: boolean | null; note: string | null; date_created: string | null; date_accessed: string | null } | null } +export type FilePathWithObject = { id: number; pub_id: number[]; is_dir: boolean | null; cas_id: string | null; integrity_checksum: string | null; location_id: number | null; materialized_path: string | null; name: string | null; extension: string | null; hidden: boolean | null; size_in_bytes: string | null; size_in_bytes_bytes: number[] | null; inode: number[] | null; object_id: number | null; date_created: string | null; date_modified: string | null; date_indexed: string | null; object: { id: number; pub_id: number[]; kind: number | null; key_id: number | null; hidden: boolean | null; favorite: boolean | null; important: boolean | null; note: string | null; date_created: string | null; date_accessed: string | null } | null } export type Flash = { /**