1075 lines · 39.1 KB · Rust
RawDownload
use std::borrow::Cow;use std::fmt;use std::str::FromStr; use anyhow::{Result, anyhow};use chrono::{DateTime, Utc};use derivative::Derivative;use pathfinder_geometry::vector::vec2f;use serde::{Deserialize, Serialize};use warp_core::features::FeatureFlag;use warp_core::ui::Icon;use warp_core::ui::appearance::Appearance;use warp_core::ui::theme::Fill;use warp_graphql::object_permissions::AccessLevel;use warp_graphql::scalars::time::ServerTimestamp;use warpui_core::Element;use warpui_core::elements::{    Align, ChildAnchor, ConstrainedBox, Hoverable, MouseStateHandle, OffsetPositioning,    ParentAnchor, ParentElement, ParentOffsetBounds, Stack,};use warpui_core::ui_components::components::UiComponent; use crate::auth::UserUid;use crate::drive::sharing::{SharingAccessLevel, Subject, TeamKind, UserKind};use crate::ids::{FolderId, ServerId, SyncId}; mod creation;mod generic_cloud_object;mod generic_string_model;pub mod models;mod server_object;mod update; pub use creation::*;pub use generic_cloud_object::*;pub use generic_string_model::*;pub use server_object::*;pub use update::*;/// The type of object id each ObjectType corresponds to.#[derive(Copy, Clone, Debug, Eq, PartialEq)]pub enum ObjectIdType {    Notebook,    Workflow,    Folder,    GenericStringObject,} impl ObjectIdType {    /// Returns the prefix for server IDs as we store them in sqlite. The prefix for these    /// objects is in title case unlike how we store the object types, which is why two different    /// APIs are needed.    pub fn sqlite_prefix(&self) -> &'static str {        match self {            ObjectIdType::Notebook => "Notebook",            ObjectIdType::Workflow => "Workflow",            ObjectIdType::Folder => "Folder",            ObjectIdType::GenericStringObject => "GenericStringObject",        }    }} /// A type for communicating the type of cloud object to/from the server, absent of the object itself.#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]pub enum ObjectType {    Notebook,    Workflow,    Folder,    GenericStringObject(GenericStringObjectFormat),} impl ObjectType {    /// Returns the serialized string for the object type, to be used for storing object_type in sqlite.    pub fn sqlite_object_type_as_str(&self) -> Cow<'_, str> {        match self {            ObjectType::Notebook => "NOTEBOOK".into(),            ObjectType::Workflow => "WORKFLOW".into(),            ObjectType::Folder => "FOLDER".into(),            ObjectType::GenericStringObject(format) => format.to_string().into(),        }    }} const NOTEBOOK_OBJECT_STRING: &str = "notebook";const WORKFLOW_OBJECT_STRING: &str = "workflow";const PROMPT_OBJECT_STRING: &str = "prompt";const FOLDER_OBJECT_STRING: &str = "folder";const ENV_VAR_COLLECTION_STRING: &str = "env-vars"; impl FromStr for ObjectType {    type Err = anyhow::Error;     fn from_str(s: &str) -> Result<Self> {        match s {            NOTEBOOK_OBJECT_STRING => Ok(Self::Notebook),            WORKFLOW_OBJECT_STRING => Ok(Self::Workflow),            PROMPT_OBJECT_STRING => Ok(Self::Workflow),            FOLDER_OBJECT_STRING => Ok(Self::Folder),            ENV_VAR_COLLECTION_STRING => Ok(Self::GenericStringObject(                GenericStringObjectFormat::Json(JsonObjectType::EnvVarCollection),            )),            _ => Err(anyhow!("Unexpected object type")),        }    }} impl fmt::Display for ObjectType {    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {        match self {            ObjectType::Notebook => write!(f, "{NOTEBOOK_OBJECT_STRING}"),            ObjectType::Workflow => write!(f, "{WORKFLOW_OBJECT_STRING}"),            ObjectType::Folder => write!(f, "{FOLDER_OBJECT_STRING}"),            ObjectType::GenericStringObject(GenericStringObjectFormat::Json(                JsonObjectType::EnvVarCollection,            )) => write!(f, "{ENV_VAR_COLLECTION_STRING}"),            ObjectType::GenericStringObject(GenericStringObjectFormat::Json(                JsonObjectType::AIFact,            )) => write!(f, "rule"),            ObjectType::GenericStringObject(_) => write!(f, "string_object_placeholder"), // placeholder value        }    }} impl From<ObjectType> for ObjectIdType {    fn from(value: ObjectType) -> Self {        match value {            ObjectType::Notebook => ObjectIdType::Notebook,            ObjectType::Workflow => ObjectIdType::Workflow,            ObjectType::Folder => ObjectIdType::Folder,            ObjectType::GenericStringObject(_) => ObjectIdType::GenericStringObject,        }    }} /// The object type prefix for generic string objects.pub const GENERIC_STRING_OBJECT_PREFIX: &str = "GENERIC_STRING_"; /// The object type prefix for json objects.pub const JSON_OBJECT_PREFIX: &str = "JSON_"; /// The data format for the generic string object type./// Right now we only support json, but this is left/// open to support markdown, yaml and other text based types.#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]pub enum GenericStringObjectFormat {    Json(JsonObjectType),} /// Represents a unique key for a generic string object. The server enforces that/// no two generic string objects have the same key.#[derive(PartialEq, Eq, Debug, Clone)]pub struct GenericStringObjectUniqueKey {    /// The unique key. E.g. for cloud prefs this is the storage key of the pref.    pub key: String,     /// Whether this key is unique for all generic string objects, or unique per user.    pub unique_per: UniquePer,} #[derive(PartialEq, Eq, Debug, Clone)]pub enum UniquePer {    User,} // Temporarily suppress clippy warnings about the `ToString` impl until we// move `ObjectType` away from using `std::fmt::Display` for serialization.#[allow(clippy::to_string_trait_impl)]impl ToString for GenericStringObjectFormat {    fn to_string(&self) -> String {        match self {            GenericStringObjectFormat::Json(json_object_type) => format!(                "{}{}{}",                GENERIC_STRING_OBJECT_PREFIX,                JSON_OBJECT_PREFIX,                json_object_type.as_str()            ),        }    }} /// An object sub-type for objects that implement the JsonModel trait.#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]pub enum JsonObjectType {    Preference,    EnvVarCollection,    WorkflowEnum,    AIFact,    MCPServer,    AIExecutionProfile,    TemplatableMCPServer,    CloudEnvironment,    ScheduledAmbientAgent,    CloudAgentConfig,} impl JsonObjectType {    pub fn as_str(&self) -> &'static str {        match self {            JsonObjectType::Preference => "PREFERENCE",            JsonObjectType::EnvVarCollection => "ENVVARCOLLECTION",            JsonObjectType::WorkflowEnum => "WORKFLOWENUM",            JsonObjectType::AIFact => "AIFACT",            JsonObjectType::MCPServer => "MCPSERVER",            JsonObjectType::AIExecutionProfile => "AIEXECUTIONPROFILE",            JsonObjectType::TemplatableMCPServer => "TEMPLATABLEMCPSERVER",            JsonObjectType::CloudEnvironment => "CLOUDENVIRONMENT",            JsonObjectType::ScheduledAmbientAgent => "SCHEDULEDAMBIENTAGENT",            JsonObjectType::CloudAgentConfig => "CLOUDAGENTCONFIG",        }    }} impl TryFrom<&str> for JsonObjectType {    type Error = anyhow::Error;     fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {        match value {            "PREFERENCE" => Ok(JsonObjectType::Preference),            "ENVVARCOLLECTION" => Ok(JsonObjectType::EnvVarCollection),            "WORKFLOWENUM" => Ok(JsonObjectType::WorkflowEnum),            "AIFACT" => Ok(JsonObjectType::AIFact),            "MCPSERVER" => Ok(JsonObjectType::MCPServer),            "AIEXECUTIONPROFILE" => Ok(JsonObjectType::AIExecutionProfile),            "TEMPLATABLEMCPSERVER" => Ok(JsonObjectType::TemplatableMCPServer),            "CLOUDENVIRONMENT" => Ok(JsonObjectType::CloudEnvironment),            "SCHEDULEDAMBIENTAGENT" => Ok(JsonObjectType::ScheduledAmbientAgent),            "CLOUDAGENTCONFIG" => Ok(JsonObjectType::CloudAgentConfig),            _ => Err(anyhow!("could not convert unknown json object type")),        }    }} impl TryFrom<warp_graphql::object::ObjectType> for ObjectIdType {    type Error = anyhow::Error;    fn try_from(object_type: warp_graphql::object::ObjectType) -> Result<Self, Self::Error> {        match object_type {            warp_graphql::object::ObjectType::AIConversation => Err(anyhow!(                "AIConversation is not a supported object type for this operation"            )),            warp_graphql::object::ObjectType::Notebook => Ok(ObjectIdType::Notebook),            warp_graphql::object::ObjectType::Workflow => Ok(ObjectIdType::Workflow),            warp_graphql::object::ObjectType::Folder => Ok(ObjectIdType::Folder),            warp_graphql::object::ObjectType::GenericStringObject => {                Ok(ObjectIdType::GenericStringObject)            }            warp_graphql::object::ObjectType::Unknown => {                Err(anyhow!("could not convert unknown cloud object type"))            }        }    }} impl From<ObjectType> for warp_graphql::object::ObjectType {    fn from(value: ObjectType) -> Self {        match value {            ObjectType::Notebook => warp_graphql::object::ObjectType::Notebook,            ObjectType::Workflow => warp_graphql::object::ObjectType::Workflow,            ObjectType::Folder => warp_graphql::object::ObjectType::Folder,            ObjectType::GenericStringObject(GenericStringObjectFormat::Json(                JsonObjectType::EnvVarCollection,            )) => warp_graphql::object::ObjectType::GenericStringObject,            ObjectType::GenericStringObject(gso) => {                todo!("Moving is not implemented for {:?}", gso);            }        }    }} /// The revision timestamp at which an object was edited. This is used by the server/// to determine if an edit to an object was at the latest revision. Edits at older/// revisions are rejected by the server.#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, PartialOrd, Ord)]pub struct Revision(ServerTimestamp); impl Revision {    pub fn from_unix_timestamp_micros(ms_since_epoch: i64) -> Result<Self> {        let ts = ServerTimestamp::from_unix_timestamp_micros(ms_since_epoch)?;        Ok(Self(ts))    }     pub fn timestamp_micros(&self) -> i64 {        self.0.timestamp_micros()    }     pub fn utc(&self) -> DateTime<Utc> {        self.0.utc()    }     /// Returns the inner `ServerTimestamp`.    pub fn timestamp(&self) -> ServerTimestamp {        self.0    }     #[cfg(any(test, feature = "test-util"))]    pub fn now() -> Self {        Self(ServerTimestamp::new(Utc::now()))    }} impl From<Revision> for ServerTimestamp {    fn from(revision: Revision) -> Self {        revision.0    }} impl From<ServerTimestamp> for Revision {    fn from(time: ServerTimestamp) -> Self {        Revision(time)    }} #[cfg(any(test, feature = "test-util"))]impl From<DateTime<Utc>> for Revision {    fn from(time: DateTime<Utc>) -> Self {        Self(ServerTimestamp::new(time))    }} /// The owner for a given object.#[derive(Copy, Clone, Debug, Eq, Serialize, Deserialize, Derivative)]#[derivative(PartialEq)]pub enum Owner {    /// The owner of the object is a user (the object is in their personal drive).    User { user_uid: UserUid },    /// The owner of the object is a team (the object is in a team drive).    Team { team_uid: ServerId },} impl Owner {    /// A mock [`Owner`] ID for testing.    #[cfg(any(test, feature = "test-util"))]    pub fn mock_current_user() -> Owner {        use crate::auth::TEST_USER_UID;         Owner::User {            user_uid: UserUid::new(TEST_USER_UID),        }    }} impl From<Owner> for Option<ServerId> {    fn from(owner: Owner) -> Option<ServerId> {        match owner {            Owner::User { .. } => None,            Owner::Team { team_uid, .. } => Some(team_uid),        }    }} /// Server representation of an object's container. This corresponds to the `Container` GraphQL/// type.////// Containers are similar to, but not quite the same as, the [`CloudObjectLocation`] type./// Locations depend on object and user state - an object might currently be in the trash, or/// it could be in one user's [shared space](Space::Shared) but another's/// [team space](Space::Team). Containers, on the other hand, represent an object's canonical/// parent - its one parent folder or drive that permissions are inherited from.#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]pub enum ServerObjectContainer {    Folder { folder_uid: ServerId },    Drive { owner: Owner },} /// Server representation of a user object guest, as part of [`ServerObjectGuest`].#[derive(Clone, Debug, PartialEq)]pub enum ServerGuestSubject {    User { firebase_uid: String },    PendingUser { email: Option<String> },    Team { team_uid: ServerId },} /// Server representation of a link-sharing setting.#[derive(Clone, Debug, PartialEq)]pub struct ServerLinkSharing {    pub access_level: AccessLevel,    pub source: Option<ServerObjectContainer>,} /// Server representation of an object guest. This corresponds to the `ObjectGuest` GraphQL type.#[derive(Clone, Debug, PartialEq)]pub struct ServerObjectGuest {    pub subject: ServerGuestSubject,    pub access_level: AccessLevel,    /// If this guest is inherited, this is the ancestor that it's inherited from.    pub source: Option<ServerObjectContainer>,} /// Metadata for a cloud object that was fetched from the server.#[derive(Clone, Debug)]pub struct ServerMetadata {    pub uid: ServerId,    pub revision: Revision,    pub metadata_last_updated_ts: ServerTimestamp,    pub trashed_ts: Option<ServerTimestamp>,    pub folder_id: Option<FolderId>,    pub is_welcome_object: bool,    pub creator_uid: Option<String>,    pub last_editor_uid: Option<String>,    pub current_editor_uid: Option<String>,} /// Permissions for a cloud object that was fetched from the server.#[derive(Clone, Debug, PartialEq)]pub struct ServerPermissions {    /// The GraphQL definition of a `Space` is closer to the client's definition of an `Owner` (due    /// to sharing). This is also going to migrate back to [ServerMetadata] as part of the    /// `Container` migration.    pub space: Owner,    pub guests: Vec<ServerObjectGuest>,    pub anyone_link_sharing: Option<ServerLinkSharing>,    pub permissions_last_updated_ts: ServerTimestamp,} impl ServerPermissions {    #[cfg(any(test, feature = "test-util"))]    pub fn mock_personal() -> Self {        Self {            space: Owner::mock_current_user(),            guests: Vec::new(),            anyone_link_sharing: None,            permissions_last_updated_ts: DateTime::<Utc>::default().into(),        }    }} #[derive(Clone, Debug)]pub struct NumInFlightRequests(pub usize); #[derive(Clone, Debug)]/// An enum representing what state a local cloud object's content changes can be in,/// in relation to the server.pub enum CloudObjectSyncStatus {    /// The object's content hasn't changed from what we believe the server's representation    /// to be.    NoLocalChanges,    /// The object's content has been modified locally, and is currently in the sync queue    /// attempting to sync up with the server.    InFlight(NumInFlightRequests),    /// The object's content has been modified locally but has unresolved conflict with the server    /// revision.    InConflict,    /// The object's content has been modified locally, but persisting the change on the server    /// could not complete for some reason.    Errored,} const SYNC_ICON_DIMENSIONS: f32 = 16.; const SYNC_STATUS_TOOLTIP_LOCAL_ONLY: &str = "Saved locally";const SYNC_STATUS_TOOLTIP_INFLIGHT: &str = "Saving";const SYNC_STATUS_TOOLTIP_ERROR: &str = "Failed to save"; #[derive(Debug, Clone, PartialEq)]pub struct CloudObjectPermissions {    pub owner: Owner,    pub permissions_last_updated_ts: Option<ServerTimestamp>,    pub anyone_with_link: Option<CloudLinkSharing>,    pub guests: Vec<CloudObjectGuest>,} impl CloudObjectPermissions {    pub fn new_from_server(server_permissions: ServerPermissions) -> Self {        let guests = if FeatureFlag::SharedWithMe.is_enabled() {            server_permissions                .guests                .into_iter()                .map(CloudObjectGuest::from_server)                .collect()        } else {            Vec::new()        };         let anyone_with_link = if FeatureFlag::SharedWithMe.is_enabled() {            server_permissions                .anyone_link_sharing                .map(CloudLinkSharing::from_server)        } else {            None        };         Self {            owner: server_permissions.space,            permissions_last_updated_ts: Some(server_permissions.permissions_last_updated_ts),            guests,            anyone_with_link,        }    }     /// Mock permissions for a personal object.    #[cfg(any(test, feature = "test-util"))]    pub fn mock_personal() -> Self {        Self {            owner: Owner::mock_current_user(),            permissions_last_updated_ts: Some(Utc::now().into()),            guests: Vec::new(),            anyone_with_link: None,        }    }     /// Returns `true` if the given user has direct personal access to this object —    /// either via an explicit user guest ACL entry or via link sharing.

Lines 1–500 of 1075.