mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-08 02:52:12 +00:00
extract payloads
This commit is contained in:
1 parent
b8895fbae9
commit
eb8301ab43
9 files changed
+184
-12
No files matched your search
@@ -1,3 +1,4 @@
|
||||
pub mod parse_webhook_inner_payload;
|
||||
pub mod parse_webhook_payload;
|
||||
pub mod payload;
|
||||
pub mod payload;
|
||||
pub mod success_case_extractors;
|
||||
@@ -1,8 +1,9 @@
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::webhook_api::payload::webhook_error_type::WebhookErrorType;
|
||||
use crate::webhook_api::payload::webhook_inner_payload::{ErrorData, ExtractedContents, PayloadErrorData, SuccessData, WebhookInnerPayload};
|
||||
use crate::webhook_api::payload::webhook_inner_payload::{ErrorData, PayloadErrorData, SuccessData, WebhookInnerPayload};
|
||||
use crate::webhook_api::payload::webhook_payload::{WebhookPayload, WebhookStatus};
|
||||
use crate::webhook_api::success_case_extractors::extract_contents_from_payload;
|
||||
|
||||
/// Parse the inner payload of a FAL webhook into one of three cases.
|
||||
///
|
||||
@@ -28,18 +29,12 @@ pub fn parse_webhook_inner_payload(webhook: &WebhookPayload) -> WebhookInnerPayl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let value = webhook.payload
|
||||
.clone()
|
||||
.unwrap_or(Value::Null);
|
||||
|
||||
let mut extracted_contents = ExtractedContents {
|
||||
image: None,
|
||||
images: None,
|
||||
video: None,
|
||||
model_glb: None,
|
||||
model_mesh: None,
|
||||
};
|
||||
|
||||
let extracted_contents = extract_contents_from_payload(&value);
|
||||
|
||||
WebhookInnerPayload::Success(SuccessData {
|
||||
payload: value,
|
||||
|
||||
@@ -28,7 +28,7 @@ pub struct SuccessData {
|
||||
/// If there are any extracted sub-payload contents, such as "images" or "video",
|
||||
/// then they are included here. This may not be fully inclusive of future
|
||||
/// payload types.
|
||||
pub extracted_contents: ExtractedContents,
|
||||
pub extracted_contents: Option<ExtractedContents>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// Extract the `image` key from a webhook success payload object.
|
||||
/// Returns a clone of the value if the key exists.
|
||||
pub fn extract_image(obj: &Map<String, Value>) -> Option<Value> {
|
||||
obj.get("image").cloned()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::webhook_api::parse_webhook_inner_payload::parse_webhook_inner_payload;
|
||||
use crate::webhook_api::payload::webhook_inner_payload::WebhookInnerPayload;
|
||||
use crate::webhook_api::payload::webhook_payload::{WebhookPayload, WebhookStatus};
|
||||
|
||||
fn load_test_webhook(filename: &str) -> WebhookPayload {
|
||||
let path = format!("test_data/webhooks/{}", filename);
|
||||
let json = std::fs::read_to_string(&path)
|
||||
.unwrap_or_else(|e| panic!("Failed to read {}: {}", path, e));
|
||||
serde_json::from_str(&json)
|
||||
.unwrap_or_else(|e| panic!("Failed to parse {}: {}", path, e))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn images_payload_populates_images_field() {
|
||||
let webhook = load_test_webhook("success/images_payload_1.json");
|
||||
let result = parse_webhook_inner_payload(&webhook);
|
||||
|
||||
let WebhookInnerPayload::Success(data) = result else {
|
||||
panic!("Expected Success, got {:?}", result);
|
||||
};
|
||||
|
||||
// The raw payload should have "images".
|
||||
assert!(data.payload.get("images").is_some());
|
||||
|
||||
// extracted_contents should be populated.
|
||||
let contents = data.extracted_contents
|
||||
.expect("extracted_contents should be Some for an images payload");
|
||||
|
||||
// The "images" field should be an array with 2 items.
|
||||
let images = contents.images.expect("images should be Some");
|
||||
let images_arr = images.as_array().expect("images should be an array");
|
||||
assert_eq!(images_arr.len(), 2);
|
||||
|
||||
// First image should have a url.
|
||||
let first = &images_arr[0];
|
||||
assert_eq!(
|
||||
first.get("url").and_then(|v| v.as_str()),
|
||||
Some("https://v3b.fal.media/files/b/01234567/name1.png"),
|
||||
);
|
||||
assert_eq!(
|
||||
first.get("content_type").and_then(|v| v.as_str()),
|
||||
Some("image/png"),
|
||||
);
|
||||
|
||||
// Second image.
|
||||
let second = &images_arr[1];
|
||||
assert_eq!(
|
||||
second.get("url").and_then(|v| v.as_str()),
|
||||
Some("https://v3b.fal.media/files/b/01234567/name2.png"),
|
||||
);
|
||||
|
||||
// The "image" (singular) field should be None since the payload has "images" not "image".
|
||||
assert!(contents.image.is_none());
|
||||
|
||||
// The video/model fields should be None.
|
||||
assert!(contents.video.is_none());
|
||||
assert!(contents.model_glb.is_none());
|
||||
assert!(contents.model_mesh.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payload_without_known_keys_has_none_extracted_contents() {
|
||||
let webhook = WebhookPayload {
|
||||
request_id: "test-no-keys".to_string(),
|
||||
gateway_request_id: "test-no-keys".to_string(),
|
||||
status: WebhookStatus::Ok,
|
||||
error: None,
|
||||
payload: Some(serde_json::json!({"some_other_key": "value"})),
|
||||
payload_error: None,
|
||||
};
|
||||
|
||||
let result = parse_webhook_inner_payload(&webhook);
|
||||
|
||||
let WebhookInnerPayload::Success(data) = result else {
|
||||
panic!("Expected Success, got {:?}", result);
|
||||
};
|
||||
|
||||
assert!(data.extracted_contents.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_payload_has_none_extracted_contents() {
|
||||
let webhook = WebhookPayload {
|
||||
request_id: "test-null".to_string(),
|
||||
gateway_request_id: "test-null".to_string(),
|
||||
status: WebhookStatus::Ok,
|
||||
error: None,
|
||||
payload: None,
|
||||
payload_error: None,
|
||||
};
|
||||
|
||||
let result = parse_webhook_inner_payload(&webhook);
|
||||
|
||||
let WebhookInnerPayload::Success(data) = result else {
|
||||
panic!("Expected Success, got {:?}", result);
|
||||
};
|
||||
|
||||
assert!(data.extracted_contents.is_none());
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// Extract the `images` key from a webhook success payload object.
|
||||
/// Returns a clone of the value if the key exists.
|
||||
pub fn extract_images(obj: &Map<String, Value>) -> Option<Value> {
|
||||
obj.get("images").cloned()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// Extract the `model_glb` key from a webhook success payload object.
|
||||
/// Returns a clone of the value if the key exists.
|
||||
pub fn extract_model_glb(obj: &Map<String, Value>) -> Option<Value> {
|
||||
obj.get("model_glb").cloned()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// Extract the `model_mesh` key from a webhook success payload object.
|
||||
/// Returns a clone of the value if the key exists.
|
||||
pub fn extract_model_mesh(obj: &Map<String, Value>) -> Option<Value> {
|
||||
obj.get("model_mesh").cloned()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// Extract the `video` key from a webhook success payload object.
|
||||
/// Returns a clone of the value if the key exists.
|
||||
pub fn extract_video(obj: &Map<String, Value>) -> Option<Value> {
|
||||
obj.get("video").cloned()
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
pub mod extract_image_payload;
|
||||
pub mod extract_images_payload;
|
||||
pub mod extract_video_payload;
|
||||
pub mod extract_model_glb_payload;
|
||||
pub mod extract_model_mesh_payload;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::webhook_api::payload::webhook_inner_payload::ExtractedContents;
|
||||
|
||||
/// Try to extract known content keys from a success payload.
|
||||
///
|
||||
/// Checks the payload (as a JSON object) for any of: `image`, `images`,
|
||||
/// `video`, `model_glb`, `model_mesh`. If at least one is found, returns
|
||||
/// `Some(ExtractedContents)` with copies of the relevant values. Multiple
|
||||
/// keys can be populated simultaneously. Returns `None` if the payload is
|
||||
/// not an object or none of the known keys are present.
|
||||
pub fn extract_contents_from_payload(payload: &Value) -> Option<ExtractedContents> {
|
||||
let obj = payload.as_object()?;
|
||||
|
||||
let image = extract_image_payload::extract_image(obj);
|
||||
let images = extract_images_payload::extract_images(obj);
|
||||
let video = extract_video_payload::extract_video(obj);
|
||||
let model_glb = extract_model_glb_payload::extract_model_glb(obj);
|
||||
let model_mesh = extract_model_mesh_payload::extract_model_mesh(obj);
|
||||
|
||||
if image.is_none() && images.is_none() && video.is_none() && model_glb.is_none() && model_mesh.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(ExtractedContents {
|
||||
image,
|
||||
images,
|
||||
video,
|
||||
model_glb,
|
||||
model_mesh,
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user