first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
@@ -14,45 +14,36 @@
//! that hit one of these panics have likely wired a non-preferences
//! code path through the fake by mistake.
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use anyhow::{anyhow, Result};
use async_channel::Sender;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use cloud_object_client::ObjectPermissionUpdateResult;
use galaxy_graphql::object_permissions::AccessLevel;
use crate::{
cloud_object::{
model::{
actions::{ObjectActionHistory, ObjectActionType},
generic_string_model::GenericStringObjectId,
},
BulkCreateCloudObjectResult, BulkCreateGenericStringObjectsRequest,
CreateCloudObjectResult, CreateObjectRequest, CreatedCloudObject,
GenericStringObjectFormat, GenericStringObjectUniqueKey, JsonObjectType,
ObjectDeleteResult, ObjectIdType, ObjectMetadataUpdateResult, ObjectPermissionUpdateResult,
ObjectPermissionsUpdateData, ObjectType, ObjectsToUpdate, Owner, Revision,
RevisionAndLastEditor, ServerFolder, ServerMetadata, ServerNotebook, ServerObject,
ServerPermissions, ServerPreference, ServerWorkflow, UpdateCloudObjectResult,
},
drive::{folders::FolderId, sharing::SharingAccessLevel},
notebooks::NotebookId,
server::{
cloud_objects::{
listener::ObjectUpdateMessage,
update_manager::{GetCloudObjectResponse, InitialLoadResponse},
},
ids::{ServerId, ServerIdAndType, SyncId},
server_api::object::{GuestIdentifier, ObjectClient},
sync_queue::SerializedModel,
},
settings::cloud_preferences::{CloudPreferenceModel, Platform, Preference},
workflows::WorkflowId,
use crate::cloud_object::model::actions::{ObjectActionHistory, ObjectActionType};
use crate::cloud_object::model::generic_string_model::GenericStringObjectId;
use crate::cloud_object::{
BulkCreateCloudObjectResult, BulkCreateGenericStringObjectsRequest, CreateCloudObjectResult,
CreateObjectRequest, CreatedCloudObject, GenericStringObjectFormat,
GenericStringObjectUniqueKey, JsonObjectType, ObjectDeleteResult, ObjectIdType,
ObjectMetadataUpdateResult, ObjectPermissionsUpdateData, ObjectType, ObjectsToUpdate, Owner,
Revision, RevisionAndLastEditor, ServerFolder, ServerMetadata, ServerNotebook, ServerObject,
ServerPermissions, ServerPreference, ServerWorkflow, UpdateCloudObjectResult,
};
use crate::drive::folders::FolderId;
use crate::drive::sharing::SharingAccessLevel;
use crate::notebooks::NotebookId;
use crate::server::cloud_objects::listener::ObjectUpdateMessage;
use crate::server::cloud_objects::update_manager::{GetCloudObjectResponse, InitialLoadResponse};
use crate::server::ids::{ServerId, ServerIdAndType, SyncId};
use crate::server::server_api::object::{GuestIdentifier, ObjectClient};
use crate::server::sync_queue::SerializedModel;
use crate::settings::cloud_preferences::{CloudPreferenceModel, Platform, Preference};
use crate::workflows::WorkflowId;
/// A stateful fake cloud preferences backend.
#[derive(Clone, Default)]
@@ -153,12 +144,12 @@ impl FakeObjectClient {
anyone_link_sharing: None,
permissions_last_updated_ts: stored.metadata_ts.into(),
};
let server_pref = ServerPreference {
id: SyncId::ServerId(ServerId::from(*id)),
model: stored.model.clone(),
let server_pref = ServerPreference::new(
SyncId::ServerId(ServerId::from(*id)),
stored.model.clone(),
metadata,
permissions,
};
);
Box::new(server_pref) as Box<dyn ServerObject>
})
.collect();
+28 -76
View File
@@ -1,35 +1,21 @@
use crate::network::{NetworkStatus, NetworkStatusEvent, NetworkStatusKind};
use crate::report_error;
use crate::server::{ids::ServerId, retry_strategies::LISTENER_RETRY_STRATEGY};
use crate::system::{SystemStats, SystemStatsEvent};
use crate::workspaces::{
user_profiles::UserProfileWithUID,
user_workspaces::{UserWorkspaces, UserWorkspacesEvent},
};
use crate::{
cloud_object::{
model::{
actions::ObjectActionHistory,
persistence::{CloudModel, CloudModelEvent},
},
ServerCloudObject, ServerMetadata, ServerPermissions,
},
server::server_api::object::ObjectClient,
};
use super::update_manager::UpdateManager;
use chrono::{DateTime, Utc};
use futures_util::stream::AbortHandle;
use galaxyui::r#async::Timer;
use std::sync::Arc;
use std::time::Duration;
use async_channel::Sender;
use galaxyui::{Entity, ModelContext, RequestState, SingletonEntity};
use std::sync::Arc;
pub use cloud_object_client::ObjectUpdateMessage;
use futures_util::stream::AbortHandle;
use instant::Instant;
use warpui::r#async::Timer;
use warpui::{Entity, ModelContext, ModelHandle, RequestState, SingletonEntity};
use super::update_manager::UpdateManager;
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent};
use crate::network::{NetworkStatus, NetworkStatusEvent, NetworkStatusKind};
use crate::report_error;
use crate::server::retry_strategies::LISTENER_RETRY_STRATEGY;
use crate::server::server_api::object::ObjectClient;
use crate::system::{SystemStats, SystemStatsEvent};
use crate::workspaces::user_workspaces::{UserWorkspaces, UserWorkspacesEvent};
lazy_static::lazy_static! {
/// Between successful websocket connections, we ensured at least this amount of time
@@ -86,52 +72,6 @@ pub struct Listener {
pending_refresh_abort_handle: Option<AbortHandle>,
}
#[derive(Debug, Clone)]
#[allow(clippy::enum_variant_names)]
pub enum ObjectUpdateMessage {
ObjectMetadataChanged {
metadata: ServerMetadata,
},
ObjectPermissionsChanged,
// TODO(CLD-2425): Replace `ObjectPermissionsChanged` with this.
ObjectPermissionsChangedV2 {
object_uid: ServerId,
permissions: ServerPermissions,
user_profiles: Vec<UserProfileWithUID>,
},
ObjectContentChanged {
server_object: Box<ServerCloudObject>,
last_editor: Option<UserProfileWithUID>,
},
ObjectDeleted {
object_uid: ServerId,
},
ObjectActionOccurred {
history: ObjectActionHistory,
},
TeamMembershipsChanged,
AmbientTaskUpdated {
task_id: String,
timestamp: DateTime<Utc>,
},
}
impl ObjectUpdateMessage {
fn as_str(&self) -> &'static str {
use ObjectUpdateMessage::*;
match self {
ObjectMetadataChanged { .. } => "ObjectMetadataChanged",
ObjectPermissionsChanged => "ObjectPermissionsChanged",
ObjectPermissionsChangedV2 { .. } => "ObjectPermissionsChanged (V2)",
ObjectContentChanged { .. } => "ObjectContentChanged",
ObjectDeleted { .. } => "ObjectDeleted",
ObjectActionOccurred { .. } => "ObjectActionOccurred",
TeamMembershipsChanged => "TeamMembershipsChanged",
AmbientTaskUpdated { .. } => "AmbientTaskUpdated",
}
}
}
impl Listener {
pub fn new(cloud_objects_client: Arc<dyn ObjectClient>, ctx: &mut ModelContext<Self>) -> Self {
let (subscription_ready_tx, subscription_ready_rx) = async_channel::unbounded();
@@ -197,6 +137,7 @@ impl Listener {
// If the user is part of a team, we should start subscribing for updates.
fn handle_user_workspaces_event(
&mut self,
_: ModelHandle<UserWorkspaces>,
event: &UserWorkspacesEvent,
ctx: &mut ModelContext<Self>,
) {
@@ -214,7 +155,12 @@ impl Listener {
}
// If the user has access to >= 1 cloud objects, we should subscribe for updates.
fn handle_cloud_model_event(&mut self, _event: &CloudModelEvent, ctx: &mut ModelContext<Self>) {
fn handle_cloud_model_event(
&mut self,
_: ModelHandle<CloudModel>,
_event: &CloudModelEvent,
ctx: &mut ModelContext<Self>,
) {
if self.has_non_welcome_cloud_objects(ctx) {
self.start_listener(ctx);
}
@@ -225,7 +171,12 @@ impl Listener {
// To get around this, we manually abort the future (effectively closing the websocket)
// when the CPU goes to sleep and restart it when it's awakened.
// https://linear.app/warpdotdev/issue/CLD-172/websocket-hangs-when-closed-during-cpu-sleep
fn handle_cpu_event(&mut self, event: &SystemStatsEvent, ctx: &mut ModelContext<Self>) {
fn handle_cpu_event(
&mut self,
_: ModelHandle<SystemStats>,
event: &SystemStatsEvent,
ctx: &mut ModelContext<Self>,
) {
match event {
SystemStatsEvent::CpuWasAwakened => {
if let Some(abort_handle) = self.current_subscription_abort_handle.take() {
@@ -252,6 +203,7 @@ impl Listener {
fn handle_network_status_changed_event(
&mut self,
_: ModelHandle<NetworkStatus>,
event: &NetworkStatusEvent,
ctx: &mut ModelContext<Self>,
) {
+18 -32
View File
@@ -1,39 +1,25 @@
use std::{
collections::HashMap,
sync::{
mpsc::{sync_channel, Receiver},
Arc,
},
};
use std::collections::HashMap;
use std::sync::mpsc::{sync_channel, Receiver};
use std::sync::Arc;
use galaxy_core::execution_mode::{AppExecutionMode, ExecutionMode};
use galaxyui::{App, ModelHandle, SingletonEntity};
use cloud_object_client::{MockObjectClient, ObjectClient};
use settings::manager::SettingsManager;
use crate::{
auth::{auth_manager::AuthManager, AuthStateProvider},
cloud_object::model::{
actions::ObjectActions,
persistence::{CloudModel, CloudModelEvent},
},
network::NetworkStatus,
persistence::ModelEvent,
server::{
server_api::{
object::{MockObjectClient, ObjectClient},
ServerApiProvider,
},
sync_queue::SyncQueue,
telemetry::context_provider::AppTelemetryContextProvider,
},
settings::{PrivacySettings, WarpDrivePrivacySettings},
workspaces::{
team_tester::TeamTesterStatus, update_manager::TeamUpdateManager,
user_profiles::UserProfiles, user_workspaces::UserWorkspaces,
},
};
use super::update_manager::UpdateManager;
use crate::auth::auth_manager::AuthManager;
use crate::auth::AuthStateProvider;
use crate::cloud_object::model::actions::ObjectActions;
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent};
use crate::network::NetworkStatus;
use crate::persistence::ModelEvent;
use crate::server::server_api::ServerApiProvider;
use crate::server::sync_queue::SyncQueue;
use crate::server::telemetry::context_provider::AppTelemetryContextProvider;
use crate::settings::{PrivacySettings, WarpDrivePrivacySettings};
use crate::workspaces::team_tester::TeamTesterStatus;
use crate::workspaces::update_manager::TeamUpdateManager;
use crate::workspaces::user_profiles::UserProfiles;
use crate::workspaces::user_workspaces::UserWorkspaces;
/// The size of the bounded channel that we use to queue persistence/sqlite-related events.
const CHANNEL_SIZE: usize = 128;
+147 -124
View File
@@ -1,75 +1,13 @@
#[cfg(not(target_family = "wasm"))]
use crate::ai::mcp::templatable::{CloudTemplatableMCPServerModel, TemplatableMCPServer};
use crate::{
ai::{
agent::conversation::AIConversationId,
ambient_agents::scheduled::{CloudScheduledAmbientAgentModel, ScheduledAmbientAgent},
blocklist::BlocklistAIHistoryModel,
cloud_environments::{AmbientAgentEnvironment, CloudAmbientAgentEnvironmentModel},
execution_profiles::{
profiles::AIExecutionProfilesModel, AIExecutionProfile, CloudAIExecutionProfileModel,
},
facts::{AIFact, CloudAIFactModel},
},
auth::{auth_manager::AuthManager, AuthStateProvider},
cloud_object::{
model::{
actions::{ObjectAction, ObjectActionHistory, ObjectActionType, ObjectActions},
generic_string_model::{
GenericStringModel, GenericStringObjectId, Serializer, StringModel,
},
persistence::{CloudModel, CloudModelEvent, UpdateSource},
view::{CloudViewModel, Editor, EditorState},
},
CloudLinkSharing, CloudModelType, CloudObject, CloudObjectEventEntrypoint,
CloudObjectLocation, CloudObjectSyncStatus, CreateCloudObjectResult, CreateObjectRequest,
GenericCloudObject, GenericServerObject, GenericStringObjectFormat, JsonObjectType,
NumInFlightRequests, ObjectDeleteResult, ObjectIdType, ObjectMetadataUpdateResult,
ObjectPermissionsUpdateData, ObjectType, Owner, Revision, RevisionAndLastEditor,
ServerAIExecutionProfile, ServerAIFact, ServerAmbientAgentEnvironment,
ServerCloudAgentConfig, ServerCloudObject, ServerEnvVarCollection, ServerFolder,
ServerMCPServer, ServerMetadata, ServerNotebook, ServerObject, ServerPermissions,
ServerPreference, ServerScheduledAmbientAgent, ServerTemplatableMCPServer, ServerWorkflow,
ServerWorkflowEnum, Space, UpdateCloudObjectResult,
},
drive::{
folders::{CloudFolderModel, FolderId},
sharing::SharingAccessLevel,
CloudObjectTypeAndId,
},
env_vars::{CloudEnvVarCollectionModel, EnvVarCollection},
network::{NetworkStatus, NetworkStatusEvent, NetworkStatusKind},
notebooks::{CloudNotebookModel, NotebookId},
persistence::ModelEvent,
server::{
ids::{
parse_sqlite_id_to_uid, ClientId, HashableId, HashedSqliteId, ObjectUid, ServerId,
SyncId, ToServerId,
},
retry_strategies::{
OUT_OF_BAND_REQUEST_RETRY_STRATEGY, PERIODIC_POLL, PERIODIC_POLL_RETRY_STRATEGY,
},
server_api::object::{GuestIdentifier, ObjectClient},
sync_queue::{
CreationFailureReason, GenericStringObjectToCreate, QueueItem, SyncQueue,
SyncQueueEvent,
},
},
settings::cloud_preferences::Preference,
util::sync::Condition,
workflows::{
workflow::Workflow,
workflow_enum::{CloudWorkflowEnum, CloudWorkflowEnumModel, WorkflowEnum},
CloudWorkflowModel, WorkflowId,
},
workspaces::{
team_tester::{TeamTesterStatus, TeamTesterStatusEvent},
update_manager::TeamUpdateManager,
user_profiles::{UserProfileWithUID, UserProfiles},
user_workspaces::UserWorkspaces,
},
};
use std::collections::HashSet;
use std::future::Future;
use std::sync::mpsc::SyncSender;
use std::sync::Arc;
use std::time::Duration;
use chrono::{DateTime, Utc};
#[cfg(test)]
pub use cloud_object_client::GetCloudObjectResponse;
pub use cloud_object_client::InitialLoadResponse;
use futures::channel::oneshot::{self, Receiver};
use futures::stream::AbortHandle;
use galaxy_core::features::FeatureFlag;
@@ -82,12 +20,78 @@ use galaxyui::{Entity, ModelContext, RequestState, RetryOption, SingletonEntity}
use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
use std::collections::{HashMap, HashSet};
use std::future::Future;
use std::sync::{mpsc::SyncSender, Arc};
use std::time::Duration;
use galaxy_core::report_error;
use galaxy_util::sync::Condition;
use galaxyui::{
duration_with_jitter, AppContext, Entity, ModelContext, ModelHandle, RequestState, RetryOption,
SingletonEntity,
};
use super::listener::ObjectUpdateMessage;
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::ambient_agents::scheduled::{
CloudScheduledAmbientAgentModel, ScheduledAmbientAgent,
};
use crate::ai::ambient_agents::AmbientAgentTaskId;
use crate::ai::blocklist::BlocklistAIHistoryModel;
use crate::ai::cloud_environments::{AmbientAgentEnvironment, CloudAmbientAgentEnvironmentModel};
use crate::ai::execution_profiles::profiles::AIExecutionProfilesModel;
use crate::ai::execution_profiles::{AIExecutionProfile, CloudAIExecutionProfileModel};
use crate::ai::facts::{AIFact, CloudAIFactModel};
#[cfg(not(target_family = "wasm"))]
use crate::ai::mcp::templatable::{CloudTemplatableMCPServerModel, TemplatableMCPServer};
use crate::auth::auth_manager::AuthManager;
use crate::auth::AuthStateProvider;
use crate::cloud_object::model::actions::{
ObjectAction, ObjectActionHistory, ObjectActionType, ObjectActions,
};
use crate::cloud_object::model::generic_string_model::{
GenericStringModel, GenericStringObjectId, Serializer, StringModel,
};
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent, UpdateSource};
use crate::cloud_object::model::view::{CloudViewModel, Editor, EditorState};
use crate::cloud_object::{
CloudLinkSharing, CloudModelType, CloudObject, CloudObjectEventEntrypoint, CloudObjectLocation,
CloudObjectSyncStatus, CreateCloudObjectResult, CreateObjectRequest, GenericCloudObject,
GenericServerObject, GenericStringObjectFormat, JsonObjectType, NumInFlightRequests,
ObjectDeleteResult, ObjectIdType, ObjectMetadataUpdateResult, ObjectPermissionsUpdateData,
ObjectType, Owner, Revision, RevisionAndLastEditor, ServerAIExecutionProfile, ServerAIFact,
ServerAmbientAgentEnvironment, ServerCloudAgentConfig, ServerCloudObject,
ServerEnvVarCollection, ServerMCPServer, ServerMetadata, ServerPermissions, ServerPreference,
ServerScheduledAmbientAgent, ServerTemplatableMCPServer, ServerWorkflowEnum, Space,
UpdateCloudObjectResult,
};
use crate::drive::drive_helpers::{
is_feature_gated_anonymous_user_past_env_var_limit,
is_feature_gated_anonymous_user_past_notebook_limit,
is_feature_gated_anonymous_user_past_workflow_limit,
};
use crate::drive::folders::{CloudFolderModel, FolderId};
use crate::drive::sharing::SharingAccessLevel;
use crate::drive::CloudObjectTypeAndId;
use crate::env_vars::{CloudEnvVarCollectionModel, EnvVarCollection};
use crate::network::{NetworkStatus, NetworkStatusEvent, NetworkStatusKind};
use crate::notebooks::{CloudNotebookModel, NotebookId};
use crate::persistence::ModelEvent;
use crate::server::ids::{
parse_sqlite_id_to_uid, ClientId, HashableId, HashedSqliteId, ObjectUid, ServerId, SyncId,
ToServerId,
};
use crate::server::retry_strategies::{
OUT_OF_BAND_REQUEST_RETRY_STRATEGY, PERIODIC_POLL, PERIODIC_POLL_RETRY_STRATEGY,
};
use crate::server::server_api::object::{GuestIdentifier, ObjectClient};
use crate::server::sync_queue::{
CreationFailureReason, GenericStringObjectToCreate, QueueItem, SyncQueue, SyncQueueEvent,
};
use crate::settings::cloud_preferences::Preference;
use crate::workflows::workflow::Workflow;
use crate::workflows::workflow_enum::{CloudWorkflowEnum, CloudWorkflowEnumModel, WorkflowEnum};
use crate::workflows::{CloudWorkflowModel, WorkflowId};
use crate::workspaces::team_tester::{TeamTesterStatus, TeamTesterStatusEvent};
use crate::workspaces::update_manager::TeamUpdateManager;
use crate::workspaces::user_profiles::{UserProfileWithUID, UserProfiles};
use crate::workspaces::user_workspaces::UserWorkspaces;
lazy_static! {
/// For online-only operations, we want to quickly determine if the operation can succeed,
@@ -135,10 +139,19 @@ pub struct ObjectOperationResult {
#[derive(Debug)]
pub enum UpdateManagerEvent {
ObjectOperationComplete { result: ObjectOperationResult },
CloudPreferencesUpdated { updated: Vec<Preference> },
MCPGalleryUpdated { templates: Vec<MCPGalleryTemplate> },
AmbientTaskUpdated { timestamp: DateTime<Utc> },
ObjectOperationComplete {
result: ObjectOperationResult,
},
CloudPreferencesUpdated {
updated: Vec<Preference>,
},
MCPGalleryUpdated {
templates: Vec<MCPGalleryTemplate>,
},
AmbientTaskUpdated {
task_id: AmbientAgentTaskId,
timestamp: DateTime<Utc>,
},
}
/// An enum for choosing the behavior of the fetch_single_cloud_object function.
@@ -159,27 +172,6 @@ pub enum InitiatedBy {
User,
System,
}
#[derive(Default)]
pub struct InitialLoadResponse {
pub updated_notebooks: Vec<ServerNotebook>,
pub deleted_notebooks: Vec<NotebookId>,
pub updated_workflows: Vec<ServerWorkflow>,
pub deleted_workflows: Vec<WorkflowId>,
pub updated_folders: Vec<ServerFolder>,
pub deleted_folders: Vec<FolderId>,
pub updated_generic_string_objects:
HashMap<GenericStringObjectFormat, Vec<Box<dyn ServerObject>>>,
pub deleted_generic_string_objects: Vec<GenericStringObjectId>,
pub user_profiles: Vec<UserProfileWithUID>,
pub action_histories: Vec<ObjectActionHistory>,
pub mcp_gallery: Vec<MCPGalleryTemplate>,
}
pub struct GetCloudObjectResponse {
pub object: ServerCloudObject,
pub descendants: Vec<ServerCloudObject>,
pub action_histories: Vec<ObjectActionHistory>,
}
#[derive(Debug)]
pub struct GenericStringObjectInput<T, S>
@@ -218,7 +210,7 @@ impl UpdateManager {
ctx: &mut ModelContext<Self>,
) -> Self {
let network_status = NetworkStatus::handle(ctx);
ctx.subscribe_to_model(&network_status, |me, event, ctx| {
ctx.subscribe_to_model(&network_status, |me, _, event, ctx| {
me.handle_network_status_changed(event, ctx);
});
@@ -226,7 +218,7 @@ impl UpdateManager {
ctx.subscribe_to_model(&team_tester_status, Self::handle_team_tester_status_changed);
let sync_queue = SyncQueue::handle(ctx);
ctx.subscribe_to_model(&sync_queue, |me, event, ctx| {
ctx.subscribe_to_model(&sync_queue, |me, _, event, ctx| {
me.handle_model_event(event, ctx);
});
@@ -316,6 +308,7 @@ impl UpdateManager {
fn handle_team_tester_status_changed(
&mut self,
_: ModelHandle<TeamTesterStatus>,
event: &TeamTesterStatusEvent,
ctx: &mut ModelContext<Self>,
) {
@@ -422,7 +415,7 @@ impl UpdateManager {
}
});
// Delete the actions on the client ID. Once we get a server ID for an object, we start dequeing any pending object actions and those
// Delete the actions on the client ID. Once we get a server ID for an object, we start dequeuing any pending object actions and those
// directly populate the ObjectActions model with the server ID, so we don't need to worry about any conversion or anything like that.
ObjectActions::handle(ctx).update(ctx, |object_actions, ctx| {
object_actions.delete_actions_for_object(&client_id.to_string(), ctx);
@@ -1118,11 +1111,20 @@ impl UpdateManager {
fn handle_ambient_task_changed(
&mut self,
_task_id: String,
task_id: String,
timestamp: DateTime<Utc>,
ctx: &mut ModelContext<UpdateManager>,
) {
ctx.emit(UpdateManagerEvent::AmbientTaskUpdated { timestamp });
let task_id = match task_id.parse::<AmbientAgentTaskId>() {
Ok(task_id) => task_id,
Err(err) => {
report_error!(anyhow::Error::from(err).context(format!(
"AmbientTaskUpdated has unparseable task_id: {task_id}"
)));
return;
}
};
ctx.emit(UpdateManagerEvent::AmbientTaskUpdated { task_id, timestamp });
}
/// Fetches environment "last used" timestamps from the server and merges them
@@ -1178,7 +1180,12 @@ impl UpdateManager {
)
});
GenericCloudObject::<K, M>::bulk_upsert_event(&objects_without_pending_changes)
M::bulk_upsert_event(
objects_without_pending_changes
.iter()
.map(|object| object.upsert_params(object.object_type()))
.collect(),
)
}
/// Generic handler deleting all objects of a given model type from the server (e.g. all updated/deleted notebooks or workflows).
@@ -2877,7 +2884,7 @@ impl UpdateManager {
let cloud_model = CloudModel::as_ref(ctx);
let object: Option<&CloudWorkflowEnum> = cloud_model.get_object_of_type(enum_id);
let Some(object) = object else {
log::error!("Could not find referenced worfklow enum to copy over to the new space, skipping");
log::error!("Could not find referenced workflow enum to copy over to the new space, skipping");
continue;
};
@@ -3216,6 +3223,25 @@ impl UpdateManager {
)
}
#[cfg_attr(target_family = "wasm", allow(dead_code))]
pub fn create_ambient_agent_environment_online(
&mut self,
ambient_agent_environment: AmbientAgentEnvironment,
client_id: ClientId,
owner: Owner,
ctx: &mut ModelContext<Self>,
) -> impl Future<Output = anyhow::Result<ServerId>> {
self.create_object_online(
CloudAmbientAgentEnvironmentModel::new(ambient_agent_environment),
owner,
client_id,
Default::default(),
false,
None,
ctx,
)
}
#[cfg_attr(target_family = "wasm", allow(dead_code))]
pub fn create_scheduled_ambient_agent_online(
&mut self,
@@ -3320,10 +3346,10 @@ impl UpdateManager {
.count()
});
if AuthStateProvider::handle(ctx).read(ctx, |auth_state_provider, _ctx| {
auth_state_provider
.get()
.is_anonymous_user_past_object_limit(ObjectType::Notebook, count + 1)
.unwrap_or_default()
is_feature_gated_anonymous_user_past_notebook_limit(
auth_state_provider.get(),
count + 1,
)
}) {
AuthManager::handle(ctx).update(ctx, |auth_manager: &mut AuthManager, ctx| {
auth_manager.anonymous_user_hit_drive_object_limit(ctx);
@@ -3391,10 +3417,10 @@ impl UpdateManager {
.count()
});
if AuthStateProvider::handle(ctx).read(ctx, |auth_state_provider, _ctx| {
auth_state_provider
.get()
.is_anonymous_user_past_object_limit(ObjectType::Workflow, count + 1)
.unwrap_or_default()
is_feature_gated_anonymous_user_past_workflow_limit(
auth_state_provider.get(),
count + 1,
)
}) {
AuthManager::handle(ctx).update(ctx, |auth_manager: &mut AuthManager, ctx| {
auth_manager.anonymous_user_hit_drive_object_limit(ctx);
@@ -3456,14 +3482,8 @@ impl UpdateManager {
.active_non_welcome_env_var_collections_in_space(Space::Personal, ctx)
.count()
});
let env_var_collection_type = ObjectType::GenericStringObject(
GenericStringObjectFormat::Json(JsonObjectType::EnvVarCollection),
);
if AuthStateProvider::handle(ctx).read(ctx, |auth_state_provider, _ctx| {
auth_state_provider
.get()
.is_anonymous_user_past_object_limit(env_var_collection_type, count + 1)
.unwrap_or_default()
is_feature_gated_anonymous_user_past_env_var_limit(auth_state_provider.get(), count + 1)
}) {
AuthManager::handle(ctx).update(ctx, |auth_manager: &mut AuthManager, ctx| {
auth_manager.anonymous_user_hit_drive_object_limit(ctx);
@@ -3571,7 +3591,10 @@ impl UpdateManager {
// Update sqlite with a single bulk request
self.save_to_db(vec![GenericStringModel::<T, S>::bulk_upsert_event(
&objects,
objects
.iter()
.map(|object| object.upsert_params(object.object_type()))
.collect(),
)]);
// Populate sync queue with a single bulk request
@@ -4825,5 +4848,5 @@ impl Entity for UpdateManager {
impl SingletonEntity for UpdateManager {}
#[cfg(test)]
#[path = "update_manager_test.rs"]
#[path = "update_manager_tests.rs"]
mod tests;
@@ -1,69 +1,62 @@
use std::{collections::HashMap, sync::Arc, time::Duration};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use chrono::{DateTime, Utc};
use cloud_object_client::MockObjectClient;
use cloud_object_models::JsonSerializer;
use futures_lite::future;
use settings::{RespectUserSyncSetting, SyncToCloud};
use galaxy_core::features::FeatureFlag;
use galaxy_graphql::{object_permissions::AccessLevel, scalars::time::ServerTimestamp};
use galaxy_graphql::object_permissions::AccessLevel;
use galaxy_graphql::scalars::time::ServerTimestamp;
use galaxyui::{App, ModelHandle, SingletonEntity};
use settings::SyncToCloud;
#[cfg(test)]
use crate::server::server_api::object::MockObjectClient;
use crate::{
ai::cloud_environments::{
AmbientAgentEnvironment, CloudAmbientAgentEnvironment, CloudAmbientAgentEnvironmentModel,
},
auth::{user::TEST_USER_UID, UserUid},
cloud_object::{
model::{
actions::{
ObjectAction, ObjectActionHistory, ObjectActionSubtype, ObjectActionType,
ObjectActions,
},
generic_string_model::GenericStringObjectId,
json_model::JsonSerializer,
persistence::{CloudModel, CloudModelEvent, UpdateSource},
},
BulkCreateCloudObjectResult, CloudModelType, CloudObjectEventEntrypoint, CloudObjectGuest,
CloudObjectLocation, ConflictStatus, CreateCloudObjectResult, CreatedCloudObject,
GenericCloudObject, GenericStringObjectFormat, JsonObjectType, ObjectDeleteResult,
ObjectIdType, ObjectMetadataUpdateResult, ObjectPermissionsUpdateData, ObjectType, Owner,
Revision, RevisionAndLastEditor, ServerCloudObject, ServerFolder, ServerGuestSubject,
ServerObject, ServerObjectGuest, ServerPreference, ServerWorkflow, ServerWorkflowEnum,
Space, UpdateCloudObjectResult,
},
drive::{
folders::{CloudFolder, CloudFolderModel, FolderId},
sharing::{SharingAccessLevel, Subject, UserKind},
CloudObjectTypeAndId,
},
notebooks::{CloudNotebook, CloudNotebookModel, NotebookId},
persistence::ModelEvent,
server::{
cloud_objects::{
listener::ObjectUpdateMessage,
test_utils::{
create_update_manager_struct, initialize_app, mock_server_api, UpdateManagerStruct,
},
update_manager::{
get_duplicate_object_name, FetchSingleObjectOption, GenericStringObjectInput,
InitiatedBy, ServerMetadata, ServerNotebook, ServerPermissions,
},
},
ids::{ClientId, HashableId, ObjectUid, ServerId, ServerIdAndType, SyncId, ToServerId},
sync_queue::SyncQueue,
},
settings::{CloudPreferenceModel, Preference},
workflows::{
workflow::{Argument, ArgumentType, Workflow},
workflow_enum::{CloudWorkflowEnum, CloudWorkflowEnumModel, EnumVariants, WorkflowEnum},
CloudWorkflow, CloudWorkflowModel, WorkflowId,
},
workspaces::user_profiles::{UserProfileWithUID, UserProfiles},
Assets,
};
use super::{GetCloudObjectResponse, InitialLoadResponse, UpdateManager};
use crate::ai::cloud_environments::{
AmbientAgentEnvironment, CloudAmbientAgentEnvironment, CloudAmbientAgentEnvironmentModel,
};
use crate::auth::user::TEST_USER_UID;
use crate::auth::UserUid;
use crate::cloud_object::model::actions::{
ObjectAction, ObjectActionHistory, ObjectActionSubtype, ObjectActionType, ObjectActions,
};
use crate::cloud_object::model::generic_string_model::GenericStringObjectId;
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent, UpdateSource};
use crate::cloud_object::{
BulkCreateCloudObjectResult, CloudModelType, CloudObjectEventEntrypoint, CloudObjectGuest,
CloudObjectLocation, ConflictStatus, CreateCloudObjectResult, CreatedCloudObject,
GenericCloudObject, GenericStringObjectFormat, JsonObjectType, ObjectDeleteResult,
ObjectIdType, ObjectMetadataUpdateResult, ObjectPermissionsUpdateData, ObjectType, Owner,
Revision, RevisionAndLastEditor, ServerCloudObject, ServerFolder, ServerGuestSubject,
ServerNotebook, ServerObject, ServerObjectGuest, ServerPreference, ServerWorkflow,
ServerWorkflowEnum, Space, UpdateCloudObjectResult,
};
use crate::drive::folders::{CloudFolder, CloudFolderModel, FolderId};
use crate::drive::sharing::{SharingAccessLevel, Subject, UserKind};
use crate::drive::CloudObjectTypeAndId;
use crate::notebooks::{CloudNotebook, CloudNotebookModel, NotebookId};
use crate::persistence::ModelEvent;
use crate::server::cloud_objects::listener::ObjectUpdateMessage;
use crate::server::cloud_objects::test_utils::{
create_update_manager_struct, initialize_app, mock_server_api, UpdateManagerStruct,
};
use crate::server::cloud_objects::update_manager::{
get_duplicate_object_name, FetchSingleObjectOption, GenericStringObjectInput, InitiatedBy,
ServerMetadata, ServerPermissions,
};
use crate::server::ids::{
ClientId, HashableId, ObjectUid, ServerId, ServerIdAndType, SyncId, ToServerId,
};
use crate::server::sync_queue::SyncQueue;
use crate::settings::{CloudPreferenceModel, Preference};
use crate::workflows::workflow::{Argument, ArgumentType, Workflow};
use crate::workflows::workflow_enum::{
CloudWorkflowEnum, CloudWorkflowEnumModel, EnumVariants, WorkflowEnum,
};
use crate::workflows::{CloudWorkflow, CloudWorkflowModel, WorkflowId};
use crate::workspaces::user_profiles::{UserProfileWithUID, UserProfiles};
use crate::ASSETS;
fn create_object<K, M>(
app: &mut App,
@@ -261,12 +254,12 @@ fn create_workflow_enum_internal(
}
fn mock_server_workflow(id: WorkflowId, owner: Owner, metadata: ServerMetadata) -> ServerWorkflow {
ServerWorkflow {
id: SyncId::ServerId(id.into()),
ServerWorkflow::new(
SyncId::ServerId(id.into()),
CloudWorkflowModel::new(Workflow::new(format!("w{id}"), format!("c{id}"))),
metadata,
permissions: mock_server_permissions(owner),
model: CloudWorkflowModel::new(Workflow::new(format!("w{id}"), format!("c{id}"))),
}
mock_server_permissions(owner),
)
}
fn mock_server_workflow_with_enum(
@@ -275,11 +268,9 @@ fn mock_server_workflow_with_enum(
owner: Owner,
metadata: ServerMetadata,
) -> (ServerWorkflow, ServerWorkflowEnum) {
let workflow = ServerWorkflow {
id: SyncId::ServerId(id.into()),
metadata: metadata.clone(),
permissions: mock_server_permissions(owner),
model: CloudWorkflowModel::new(
let workflow = ServerWorkflow::new(
SyncId::ServerId(id.into()),
CloudWorkflowModel::new(
Workflow::new(format!("w{id}"), format!("c{id}")).with_arguments(vec![Argument {
name: format!("e{enum_id}"),
default_value: None,
@@ -289,47 +280,49 @@ fn mock_server_workflow_with_enum(
},
}]),
),
};
metadata.clone(),
mock_server_permissions(owner),
);
let workflow_enum = ServerWorkflowEnum {
id: SyncId::ServerId(enum_id.into()),
metadata,
permissions: mock_server_permissions(owner),
model: CloudWorkflowEnumModel::new(WorkflowEnum {
let workflow_enum = ServerWorkflowEnum::new(
SyncId::ServerId(enum_id.into()),
CloudWorkflowEnumModel::new(WorkflowEnum {
name: format!("e{id}"),
is_shared: false,
variants: EnumVariants::Static(vec!["v1".to_string(), "v2".to_string()]),
}),
};
metadata,
mock_server_permissions(owner),
);
(workflow, workflow_enum)
}
fn mock_server_notebook(id: NotebookId, owner: Owner, metadata: ServerMetadata) -> ServerNotebook {
ServerNotebook {
id: SyncId::ServerId(id.into()),
metadata,
permissions: mock_server_permissions(owner),
model: CloudNotebookModel {
ServerNotebook::new(
SyncId::ServerId(id.into()),
CloudNotebookModel {
title: format!("n{id}"),
data: format!("n{id}"),
ai_document_id: None,
conversation_id: None,
},
}
metadata,
mock_server_permissions(owner),
)
}
fn mock_server_folder(id: FolderId, owner: Owner, metadata: ServerMetadata) -> ServerFolder {
ServerFolder {
id: SyncId::ServerId(id.into()),
metadata,
permissions: mock_server_permissions(owner),
model: CloudFolderModel {
ServerFolder::new(
SyncId::ServerId(id.into()),
CloudFolderModel {
name: format!("f{id}"),
is_open: false,
is_warp_pack: false,
},
}
metadata,
mock_server_permissions(owner),
)
}
fn update_notebook(
@@ -603,10 +596,10 @@ fn mock_fetch_single_cloud_object(
.times(1)
.return_once(move |_| {
Ok(GetCloudObjectResponse {
object: ServerCloudObject::Workflow(Box::new(ServerWorkflow {
id: SyncId::ServerId(workflow_id.into()),
model: CloudWorkflowModel::new(Workflow::new("server workflow", "echo server")),
metadata: ServerMetadata {
object: ServerCloudObject::Workflow(Box::new(ServerWorkflow::new(
SyncId::ServerId(workflow_id.into()),
CloudWorkflowModel::new(Workflow::new("server workflow", "echo server")),
ServerMetadata {
uid: server_id,
revision: Revision::now(),
metadata_last_updated_ts: Utc::now().into(),
@@ -617,13 +610,13 @@ fn mock_fetch_single_cloud_object(
last_editor_uid: None,
current_editor_uid: None,
},
permissions: ServerPermissions {
ServerPermissions {
space: Owner::mock_current_user(),
guests: Vec::new(),
anyone_link_sharing: None,
permissions_last_updated_ts: Utc::now().into(),
},
})),
))),
descendants: vec![],
action_histories: vec![ObjectActionHistory {
uid: server_id.uid(),
@@ -637,7 +630,7 @@ fn mock_fetch_single_cloud_object(
#[test]
fn test_sync_state_after_creation_item_not_in_sync_queue_folder() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
let client_id = ClientId::new();
initialize_app(&mut app);
let object_id: FolderId = 123.into();
@@ -665,7 +658,7 @@ fn test_sync_state_after_creation_item_not_in_sync_queue_folder() {
#[test]
fn test_sync_state_after_creation_item_not_in_sync_queue_workflow() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
let client_id = ClientId::new();
initialize_app(&mut app);
let object_id: WorkflowId = 123.into();
@@ -693,7 +686,7 @@ fn test_sync_state_after_creation_item_not_in_sync_queue_workflow() {
#[test]
fn test_sync_state_after_creation_item_not_in_sync_queue_notebook() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
let client_id = ClientId::new();
initialize_app(&mut app);
@@ -722,7 +715,7 @@ fn test_sync_state_after_creation_item_not_in_sync_queue_notebook() {
#[test]
fn test_sync_state_after_creation_item_not_in_sync_queue_generic_object() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
let client_id = ClientId::new();
initialize_app(&mut app);
let object_id: GenericStringObjectId = 123.into();
@@ -806,7 +799,9 @@ async fn run_sync_state_after_creation_item_not_in_sync_queue<K, M>(
// we created an object in the db
assert_eq!(
std::mem::discriminant(&events[0]),
std::mem::discriminant(&object.model().upsert_event(&object))
std::mem::discriminant(&M::upsert_event(
object.upsert_params(object.model().object_type())
))
);
// when we got the correct response back from the server,
// we updated the db with the server id
@@ -834,7 +829,7 @@ async fn run_sync_state_after_creation_item_not_in_sync_queue<K, M>(
#[test]
fn test_sync_state_after_creation_item_in_flight() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -921,7 +916,7 @@ fn test_sync_state_after_creation_item_in_flight() {
#[test]
fn test_sync_state_after_creation_fails_due_to_limit() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let client_id = ClientId::new();
@@ -1046,7 +1041,7 @@ fn test_sync_state_after_creation_fails_due_to_limit() {
#[test]
fn test_sync_state_after_update_item_not_in_sync_queue() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -1191,7 +1186,7 @@ fn test_sync_state_after_update_item_not_in_sync_queue() {
#[test]
fn test_create_sets_editor() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -1235,7 +1230,7 @@ fn test_create_sets_editor() {
#[test]
fn test_bulk_create_generic_string_objects() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let client_id_1 = ClientId::new();
@@ -1393,7 +1388,7 @@ fn test_bulk_create_generic_string_objects() {
#[test]
fn test_sync_state_after_update_item_not_in_sync_queue_generic_string_object() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let client_id = ClientId::new();
@@ -1524,7 +1519,7 @@ fn test_sync_state_after_update_item_not_in_sync_queue_generic_string_object() {
#[test]
fn test_sync_state_after_update_item_in_sync_queue() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -1641,7 +1636,7 @@ fn test_sync_state_after_update_item_in_sync_queue() {
#[test]
fn test_sync_state_after_creation_failure_item_not_in_sync_queue() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -1687,7 +1682,7 @@ fn test_sync_state_after_creation_failure_item_not_in_sync_queue() {
#[test]
fn test_sync_state_after_update_failure_item_in_sync_queue() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -1808,7 +1803,7 @@ fn test_sync_state_after_update_failure_item_in_sync_queue() {
#[test]
fn test_sync_state_after_object_with_dependencies_created() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -1958,7 +1953,7 @@ fn test_sync_state_after_object_with_dependencies_created() {
#[test]
fn test_fetch_single_cloud_object_not_pending_no_overwrite() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let client_id = ClientId::new();
@@ -2054,7 +2049,7 @@ fn test_fetch_single_cloud_object_not_pending_no_overwrite() {
#[test]
fn test_fetch_single_cloud_object_pending_no_overwrite() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -2154,7 +2149,7 @@ fn test_fetch_single_cloud_object_pending_no_overwrite() {
#[test]
fn test_fetch_single_cloud_object_pending_with_overwrite() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -2260,7 +2255,7 @@ fn test_fetch_single_cloud_object_pending_with_overwrite() {
#[test]
fn test_metadata_after_trash_item_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -2330,7 +2325,7 @@ fn test_metadata_after_trash_item_success() {
#[test]
fn test_pending_metadata_update_with_rtc() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -2397,7 +2392,7 @@ fn test_pending_metadata_update_with_rtc() {
mocked_metadata_update_message,
);
// Assert we don't have a pending change now that we've updated the metadat
// Assert we don't have a pending change now that we've updated the metadata
assert_pending_online_only_change_for_object(
&mut app,
&notebook_id.to_server_id().uid(),
@@ -2437,7 +2432,7 @@ fn test_pending_metadata_update_with_rtc() {
#[test]
fn test_metadata_update_with_rtc_no_pending() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -2535,7 +2530,7 @@ fn test_metadata_update_with_rtc_no_pending() {
#[test]
fn test_metadata_after_trash_item_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -2604,7 +2599,7 @@ fn test_metadata_after_trash_item_failure() {
#[test]
fn test_pending_metadata_update_with_polling() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -2673,9 +2668,9 @@ fn test_pending_metadata_update_with_polling() {
> = HashMap::new();
updated_generic_string_objects.insert(
GenericStringObjectFormat::Json(JsonObjectType::Preference),
vec![Box::new(ServerPreference {
id: SyncId::ServerId(generic_object_server_id),
model: CloudPreferenceModel::new(
vec![Box::new(ServerPreference::new(
SyncId::ServerId(generic_object_server_id),
CloudPreferenceModel::new(
Preference::new(
"test_storage_key".to_string(),
"{\"test_key\": \"test_value\"}",
@@ -2683,23 +2678,23 @@ fn test_pending_metadata_update_with_polling() {
)
.expect("error creating preference"),
),
metadata: mocked_metadata.clone(),
permissions: mock_server_permissions(Owner::mock_current_user()),
})],
mocked_metadata.clone(),
mock_server_permissions(Owner::mock_current_user()),
))],
);
let mocked_response = InitialLoadResponse {
updated_notebooks: vec![ServerNotebook {
id: SyncId::ServerId(notebood_server_id),
model: CloudNotebookModel {
updated_notebooks: vec![ServerNotebook::new(
SyncId::ServerId(notebood_server_id),
CloudNotebookModel {
title: "".into(),
data: "".into(),
ai_document_id: None,
conversation_id: None,
},
metadata: mocked_metadata.clone(),
permissions: mock_server_permissions(Owner::mock_current_user()),
}],
mocked_metadata.clone(),
mock_server_permissions(Owner::mock_current_user()),
)],
deleted_notebooks: vec![],
updated_workflows: vec![],
deleted_workflows: vec![],
@@ -2757,7 +2752,7 @@ fn test_pending_metadata_update_with_polling() {
#[test]
fn test_metadata_update_with_polling_no_pending() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -2806,17 +2801,17 @@ fn test_metadata_update_with_polling_no_pending() {
current_editor_uid: Some("ian@warp.dev".to_string()),
};
let mocked_response = InitialLoadResponse {
updated_notebooks: vec![ServerNotebook {
id: SyncId::ServerId(server_id),
model: CloudNotebookModel {
updated_notebooks: vec![ServerNotebook::new(
SyncId::ServerId(server_id),
CloudNotebookModel {
title: "".into(),
data: "".into(),
ai_document_id: None,
conversation_id: None,
},
metadata: mocked_metadata.clone(),
permissions: mock_server_permissions(Owner::mock_current_user()),
}],
mocked_metadata.clone(),
mock_server_permissions(Owner::mock_current_user()),
)],
deleted_notebooks: vec![],
updated_workflows: vec![],
deleted_workflows: vec![],
@@ -2874,7 +2869,7 @@ fn test_metadata_update_with_polling_no_pending() {
#[test]
fn test_metadata_after_untrash_item_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -2944,7 +2939,7 @@ fn test_metadata_after_untrash_item_success() {
#[test]
fn test_metadata_after_untrash_item_and_move_to_root() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -3017,7 +3012,7 @@ fn test_metadata_after_untrash_item_and_move_to_root() {
#[test]
fn test_metadata_after_untrash_item_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -3071,7 +3066,7 @@ fn test_metadata_after_untrash_item_failure() {
#[test]
fn test_metadata_after_optimistic_grab_baton_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -3143,7 +3138,7 @@ fn test_metadata_after_optimistic_grab_baton_success() {
#[test]
fn test_metadata_after_optimistic_grab_baton_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -3202,7 +3197,7 @@ fn test_metadata_after_optimistic_grab_baton_failure() {
#[test]
fn test_metadata_after_non_optimistic_grab_baton_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -3274,7 +3269,7 @@ fn test_metadata_after_non_optimistic_grab_baton_success() {
#[test]
fn test_metadata_after_non_optimistic_grab_baton_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -3332,7 +3327,7 @@ fn test_metadata_after_non_optimistic_grab_baton_failure() {
#[test]
fn test_report_initial_load() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
let update_manager_struct = create_update_manager_struct(&mut app, Arc::new(server_api));
@@ -3417,7 +3412,7 @@ fn test_get_duplicate_object_name() {
#[test]
fn test_duplicate_workflow_not_pending_no_overwrite() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -3497,7 +3492,7 @@ fn test_duplicate_workflow_not_pending_no_overwrite() {
#[test]
fn test_replace_object_with_conflicts() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -3614,7 +3609,7 @@ fn test_replace_object_with_conflicts() {
#[test]
fn test_pending_conflict_correctly_clears_after_edits() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -3673,7 +3668,7 @@ fn test_pending_conflict_correctly_clears_after_edits() {
ModelEvent::UpsertNotebook { .. }
));
// While we still have edits in flight, recieve an RTC message, that has a TS before the object update
// While we still have edits in flight, receive an RTC message, that has a TS before the object update
// response. This should put the notebook conflict status as pending
let mocked_metadata = ServerMetadata {
uid: server_id,
@@ -3746,7 +3741,7 @@ fn test_pending_conflict_correctly_clears_after_edits() {
#[test]
fn test_pending_conflict_correctly_stays_after_edits() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -3805,7 +3800,7 @@ fn test_pending_conflict_correctly_stays_after_edits() {
ModelEvent::UpsertNotebook { .. }
));
// While we still have edits in flight, recieve an RTC message, that has a TS after the object update
// While we still have edits in flight, receive an RTC message, that has a TS after the object update
// response. This should put the notebook conflict status as pending
let mocked_metadata = ServerMetadata {
uid: server_id,
@@ -3877,7 +3872,7 @@ fn test_pending_self_conflict_clears_out_of_order() {
// This tests the case where the client makes an edit and receives the RTC update for it
// _before_ the server response to their edit.
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = MockObjectClient::new();
@@ -3972,7 +3967,7 @@ fn test_pending_self_conflict_clears_out_of_order() {
fn test_pending_newer_conflict_remains_out_of_order() {
// This tests the case where the client makes an edit which is eventually accepted by the
// server, but receives an RTC update about a _newer_ edit in the meantime.
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = MockObjectClient::new();
@@ -4067,7 +4062,7 @@ fn test_pending_newer_conflict_remains_out_of_order() {
#[test]
fn test_accepts_new_metadata_with_force_refresh() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
let server_id: ServerId = 123.into();
@@ -4107,17 +4102,17 @@ fn test_accepts_new_metadata_with_force_refresh() {
current_editor_uid: None,
};
let mocked_response = InitialLoadResponse {
updated_notebooks: vec![ServerNotebook {
id: SyncId::ServerId(server_id),
model: CloudNotebookModel {
updated_notebooks: vec![ServerNotebook::new(
SyncId::ServerId(server_id),
CloudNotebookModel {
title: "".into(),
data: "".into(),
ai_document_id: None,
conversation_id: None,
},
metadata: mocked_metadata.clone(),
permissions: mock_server_permissions(Owner::mock_current_user()),
}],
mocked_metadata.clone(),
mock_server_permissions(Owner::mock_current_user()),
)],
deleted_notebooks: vec![],
updated_workflows: vec![],
deleted_workflows: vec![],
@@ -4160,7 +4155,7 @@ fn test_accepts_new_metadata_with_force_refresh() {
#[test]
fn test_delete_single_object() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4232,7 +4227,7 @@ fn test_delete_single_object() {
#[test]
fn test_empty_trash() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let ts = Utc::now();
@@ -4344,7 +4339,7 @@ fn test_empty_trash() {
#[test]
fn test_leave_shared_object() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4451,7 +4446,7 @@ fn test_leave_shared_object() {
#[test]
fn test_create_object_online_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4551,7 +4546,7 @@ fn test_create_object_online_success() {
#[test]
fn test_create_object_online_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4610,7 +4605,7 @@ fn test_create_object_online_failure() {
#[test]
fn test_create_object_online_user_facing_error() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4673,7 +4668,7 @@ fn test_create_object_online_user_facing_error() {
#[test]
fn test_create_object_online_with_folder_id() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4756,7 +4751,7 @@ fn test_create_object_online_with_folder_id() {
#[test]
fn test_create_object_online_with_client_folder_id_fails() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4808,7 +4803,7 @@ fn test_create_object_online_with_client_folder_id_fails() {
#[test]
fn test_record_object_action() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -4895,7 +4890,7 @@ fn test_record_object_action() {
#[test]
fn test_overwrite_object_action_history_no_actions_on_client() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -4963,7 +4958,7 @@ fn test_overwrite_object_action_history_no_actions_on_client() {
#[test]
fn test_overwrite_object_action_history_reject() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -5117,7 +5112,7 @@ fn test_overwrite_object_action_history_reject() {
#[test]
fn test_overwrite_object_action_history_ignores_pending_local_actions() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -5271,7 +5266,7 @@ fn test_overwrite_object_action_history_ignores_pending_local_actions() {
#[test]
fn test_object_action_histories_with_initial_load() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -5457,7 +5452,7 @@ fn test_object_action_histories_with_initial_load() {
#[test]
fn test_delete_single_object_with_actions() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -5581,7 +5576,7 @@ fn test_delete_single_object_with_actions() {
/// changing the owner of an object, which implicitly also clears its parent folder.
#[test]
fn test_move_object_personal_to_team_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -5681,7 +5676,7 @@ fn test_move_object_personal_to_team_success() {
/// optimistically update the model, and then roll those updates back on failure.
#[test]
fn test_move_object_personal_to_team_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -5787,7 +5782,7 @@ fn test_move_object_personal_to_team_failure() {
/// space to a team drive.
#[test]
fn test_move_cloud_environment_personal_to_team_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -5886,7 +5881,7 @@ fn test_move_cloud_environment_personal_to_team_success() {
/// and change the reference stored within the workflow to point to that enum.
#[test]
fn test_move_workflow_with_enums_personal_to_team_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let team = Space::Team {
@@ -6023,7 +6018,7 @@ fn test_move_workflow_with_enums_personal_to_team_success() {
#[test]
fn test_move_workflow_with_enums_personal_to_team_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
let team = Space::Team {
@@ -6174,7 +6169,7 @@ fn test_move_workflow_with_enums_personal_to_team_failure() {
/// `Some`.
#[test]
fn test_move_object_root_to_folder_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -6267,7 +6262,7 @@ fn test_move_object_root_to_folder_success() {
/// optimistically update the model, and then roll back the updates on failure.
#[test]
fn test_move_object_root_to_folder_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -6366,7 +6361,7 @@ fn test_move_object_root_to_folder_failure() {
/// during, and after a move where the object's parent folder changes from `Some` to `None`.
#[test]
fn test_move_object_folder_to_root_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -6459,7 +6454,7 @@ fn test_move_object_folder_to_root_success() {
/// we optimistically clear its parent folder, and then restore it on failure.
#[test]
fn test_move_object_folder_to_root_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -6558,7 +6553,7 @@ fn test_move_object_folder_to_root_failure() {
/// checks that we update the object's metadata and emit events correctly.
#[test]
fn test_move_object_folder_to_folder_success() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -6653,7 +6648,7 @@ fn test_move_object_folder_to_folder_success() {
/// we optimistically apply the move in-memory and then undo it on failure.
#[test]
fn test_move_object_folder_to_folder_failure() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -6754,7 +6749,7 @@ fn test_move_object_folder_to_folder_failure() {
/// object was trashed by another client.
#[test]
fn test_trash_object_over_rtc() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
let workflow_id: WorkflowId = 123.into();
@@ -6831,7 +6826,7 @@ fn test_trash_object_over_rtc() {
/// object was un-trashed by another client.
#[test]
fn test_untrash_object_over_rtc() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
let workflow_id: WorkflowId = 123.into();
@@ -6908,7 +6903,7 @@ fn test_untrash_object_over_rtc() {
/// object was moved from one folder to another in the same space by another client.
#[test]
fn test_move_object_from_folder_to_folder_over_rtc() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
let workflow_id: WorkflowId = 123.into();
@@ -6989,7 +6984,7 @@ fn test_move_object_from_folder_to_folder_over_rtc() {
/// object was moved from a folder to the root of its space by another client.
#[test]
fn test_move_object_from_folder_to_root_over_rtc() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
let workflow_id: WorkflowId = 123.into();
@@ -7069,7 +7064,7 @@ fn test_move_object_from_folder_to_root_over_rtc() {
/// an object was moved from the root of its space into a folder by another client.
#[test]
fn test_move_object_from_root_to_folder_over_rtc() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
let workflow_id: WorkflowId = 123.into();
@@ -7147,7 +7142,7 @@ fn test_move_object_from_root_to_folder_over_rtc() {
#[test]
fn test_permissions_update_grants_access() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -7261,7 +7256,7 @@ fn test_permissions_update_grants_access() {
fn test_permissions_update_existing_object() {
let _guard = FeatureFlag::SharedWithMe.override_enabled(true);
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let server_api = mock_server_api();
@@ -7322,7 +7317,7 @@ fn test_permissions_update_existing_object() {
#[test]
fn test_add_guest_success() {
let _guard = FeatureFlag::SharedWithMe.override_enabled(true);
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();
@@ -7436,7 +7431,7 @@ fn test_add_guest_success() {
#[test]
fn test_add_guest_failure() {
let _guard = FeatureFlag::SharedWithMe.override_enabled(true);
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let mut server_api = mock_server_api();