diff --git a/crates/api_clients/world_labs_client/src/api/api_types/mod.rs b/crates/api_clients/world_labs_client/src/api/api_types/mod.rs index 5cf1c7360c..c86d929d8f 100644 --- a/crates/api_clients/world_labs_client/src/api/api_types/mod.rs +++ b/crates/api_clients/world_labs_client/src/api/api_types/mod.rs @@ -1,4 +1,5 @@ +pub mod run_object; pub mod run_object_id; -pub mod upload_object_id; pub mod upload_mime_type; +pub mod upload_object_id; pub mod world_object_id; diff --git a/crates/api_clients/world_labs_client/src/api/api_types/run_object.rs b/crates/api_clients/world_labs_client/src/api/api_types/run_object.rs new file mode 100644 index 0000000000..ac84c1a0de --- /dev/null +++ b/crates/api_clients/world_labs_client/src/api/api_types/run_object.rs @@ -0,0 +1,80 @@ +use std::collections::HashMap; +use chrono::Utc; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +const RUN_MIME_TYPE: &str = "application/run+json"; + +const RUN_VERSION : &str = "0.0.1"; + +/// World Labs' API is very frontend driven +/// They send whole objects back to the frontend that the frontend is in charge of mutatingbjects or PATCH them back to the server (yuck): +/// - request #1 - create run +/// - request #6 - patch run after image upload +/// Gotta go fast? +#[derive(Serialize, Deserialize, Clone)] + +pub struct RunObject { + /// The primary ID of the run. + /// This won't exist until we post it to the server. + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + pub metadata: RunObjectMetadata, + pub mime_type: String, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct RunObjectMetadata { + pub version: String, + + #[serde(rename = "createdAt")] + pub created_at: u64, + + #[serde(rename = "updatedAt")] + pub updated_at: u64, + + #[serde(rename = "usesAdvancedEditing")] + pub uses_advanced_editing: bool, + + #[serde(rename = "draftMode")] + pub draft_mode: bool, + + /// Polymorphic set of ID-to-object mappings. + pub nodes: HashMap, +} + +impl Default for RunObject { + fn default() -> Self { + let now = Utc::now(); + let now = now.timestamp().unsigned_abs(); + Self { + id: None, + metadata: RunObjectMetadata{ + version: RUN_VERSION.to_string(), + created_at: now, + updated_at: now, + uses_advanced_editing: false, + draft_mode: false, + nodes: HashMap::new(), + }, + mime_type: RUN_MIME_TYPE.to_string(), + } + } +} + +#[cfg(test)] +mod tests { + use crate::api::api_types::run_object::RunObject; + + #[test] + fn default_runobject() { + let object = RunObject::default(); + assert!(object.id.is_none()); + assert_eq!(&object.metadata.version, "0.0.1"); + assert!(object.metadata.created_at > 1765433415549); + assert!(object.metadata.updated_at > 1765433415549); + assert_eq!(&object.mime_type, "application/run+json"); + } +} + diff --git a/crates/api_clients/world_labs_client/src/api/requests/objects/create_run_object.rs b/crates/api_clients/world_labs_client/src/api/requests/objects/create_run_object.rs index e8dfed28c5..05a4da77e9 100644 --- a/crates/api_clients/world_labs_client/src/api/requests/objects/create_run_object.rs +++ b/crates/api_clients/world_labs_client/src/api/requests/objects/create_run_object.rs @@ -1,3 +1,4 @@ +use crate::api::api_types::run_object::RunObject; use crate::api::api_types::run_object_id::RunObjectId; use crate::api::common::common_header_values::{ORIGIN_VALUE, REFERER_VALUE}; use crate::credentials::world_labs_bearer_token::WorldLabsBearerToken; @@ -5,6 +6,7 @@ use crate::credentials::world_labs_cookies::WorldLabsCookies; use crate::error::world_labs_client_error::WorldLabsClientError; use crate::error::world_labs_error::WorldLabsError; use crate::error::world_labs_generic_api_error::WorldLabsGenericApiError; +use crate::error::world_labs_specific_api_error::WorldLabsSpecificApiError; use chrono::Utc; use http_headers::names::{PRIORITY, SEC_FETCH_DEST, SEC_FETCH_MODE, SEC_FETCH_SITE, SEC_GPC}; use http_headers::values::accept::ACCEPT_ALL; @@ -32,6 +34,7 @@ pub struct CreateRunObjectArgs<'a> { pub struct CreateRunObjectResponse { pub id: RunObjectId, + pub run_object: RunObject, } /// Marble Image-to-World @@ -67,9 +70,9 @@ pub async fn create_run_object(args: CreateRunObjectArgs<'_>) -> Result) -> Result RunObjectId(id.to_string()), + None => { + return Err(WorldLabsSpecificApiError::NoRunIdInResponse { response_body }.into()) + } + }; + Ok(CreateRunObjectResponse { - id: RunObjectId(response.id), + id: run_id, + run_object: response, }) } - -#[derive(Serialize)] -struct RawRequest { - pub metadata: RawRequestMetadata, - pub mime_type: String, -} - -#[derive(Serialize)] -struct RawRequestMetadata { - pub version: String, - - #[serde(rename = "createdAt")] - pub created_at: u64, - - #[serde(rename = "updatedAt")] - pub updated_at: u64, - - #[serde(rename = "usesAdvancedEditing")] - pub uses_advanced_editing: bool, - - #[serde(rename = "draftMode")] - pub draft_mode: bool, - - pub nodes: RawRequestNodes, -} - -#[derive(Serialize)] -struct RawRequestNodes { -} - -impl Default for RawRequest { - fn default() -> Self { - let now = Utc::now(); - let now = now.timestamp().unsigned_abs(); - Self { - metadata: RawRequestMetadata { - version: "0.0.1".to_string(), - created_at: now, - updated_at: now, - uses_advanced_editing: false, - draft_mode: false, - nodes: RawRequestNodes {}, - }, - mime_type: "application/run+json".to_string(), - } - } -} - -#[derive(Deserialize)] -struct RawResponse { - pub id: String, - //pub owner_id: String, -} diff --git a/crates/api_clients/world_labs_client/src/error/world_labs_specific_api_error.rs b/crates/api_clients/world_labs_client/src/error/world_labs_specific_api_error.rs index ffd939775f..2f8262fc37 100644 --- a/crates/api_clients/world_labs_client/src/error/world_labs_specific_api_error.rs +++ b/crates/api_clients/world_labs_client/src/error/world_labs_specific_api_error.rs @@ -3,6 +3,9 @@ use std::fmt::{Display, Formatter}; #[derive(Debug)] pub enum WorldLabsSpecificApiError { + /// If the API drops run ids (unlikely) + NoRunIdInResponse { response_body: String }, + /// The Google GCP upload failed because the signature does not match GoogleUploadSignatureDoesNotMatch, @@ -62,6 +65,7 @@ impl Error for WorldLabsSpecificApiError {} impl Display for WorldLabsSpecificApiError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { + Self::NoRunIdInResponse { response_body } => write!(f, "no run.id found in response: {}", response_body), Self::GoogleUploadSignatureDoesNotMatch => write!(f, "Google upload signature does not match"), Self::AutomationBlocked => write!(f, "Automation blocked"), Self::TooManyVideos => write!(f, "Too many videos are being generated"), diff --git a/crates/api_clients/world_labs_client/src/recipes/upload_image_and_create_world_with_retry.rs b/crates/api_clients/world_labs_client/src/recipes/upload_image_and_create_world_with_retry.rs index b2630eda6a..36b4d7444c 100644 --- a/crates/api_clients/world_labs_client/src/recipes/upload_image_and_create_world_with_retry.rs +++ b/crates/api_clients/world_labs_client/src/recipes/upload_image_and_create_world_with_retry.rs @@ -41,6 +41,7 @@ pub async fn upload_image_and_create_world_with_retry(args: UploadImageAndCreate }).await?; let run_object_id = response.id; + let run_object = response.run_object; info!("Object id: {}", run_object_id.0);