mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-10 03:30:59 +00:00
Type the blob-write response hash as fabro_types::BlobHash
Promote BlobHash to a named OpenAPI schema with the ^[0-9a-f]{64}$
pattern, reference it from WriteBlobResponse.hash and the blobHash path
parameter, and map it to fabro_types::BlobHash via with_replacement.
The server now serializes the domain type directly and the client gets
a parsed BlobHash by construction, removing the to_string/parse adapter
pair across the wire boundary. Adds the JSON-parity test required for
new replacements.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f1c00a167e
commit
88b2a01af8
7 changed files with 64 additions and 17 deletions
|
|
@ -5980,8 +5980,7 @@ components:
|
|||
required: true
|
||||
description: Content-addressed blob hash.
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[0-9a-f]{64}$'
|
||||
$ref: "#/components/schemas/BlobHash"
|
||||
example: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
|
||||
|
||||
ArtifactFilename:
|
||||
|
|
@ -10283,6 +10282,12 @@ components:
|
|||
description: Assigned event sequence number.
|
||||
example: 42
|
||||
|
||||
BlobHash:
|
||||
description: Content-addressed SHA-256 hash of a stored blob.
|
||||
type: string
|
||||
pattern: "^[0-9a-f]{64}$"
|
||||
example: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
|
||||
|
||||
WriteBlobResponse:
|
||||
description: Content-addressed hash of a stored blob.
|
||||
type: object
|
||||
|
|
@ -10290,9 +10295,7 @@ components:
|
|||
- hash
|
||||
properties:
|
||||
hash:
|
||||
type: string
|
||||
description: Content-addressed hash of the stored blob.
|
||||
example: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
|
||||
$ref: "#/components/schemas/BlobHash"
|
||||
|
||||
CommandTermination:
|
||||
description: Terminal state for a command execution.
|
||||
|
|
|
|||
|
|
@ -105,10 +105,7 @@ async fn write_run_blob(
|
|||
}
|
||||
match state.stores.runs.open_run(&id).await {
|
||||
Ok(run_store) => match run_store.write_blob(&body).await {
|
||||
Ok(blob_hash) => Json(WriteBlobResponse {
|
||||
hash: blob_hash.to_string(),
|
||||
})
|
||||
.into_response(),
|
||||
Ok(blob_hash) => Json(WriteBlobResponse { hash: blob_hash }).into_response(),
|
||||
Err(err) => {
|
||||
ApiError::new(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -725,6 +725,7 @@ fn main() {
|
|||
("WorkflowVersion", "fabro_types::WorkflowVersion", &[]),
|
||||
("WorkflowPath", "fabro_types::WorkflowPath", &[]),
|
||||
("WorkflowVersionId", "fabro_types::WorkflowVersionId", &[]),
|
||||
("BlobHash", "fabro_types::BlobHash", &[]),
|
||||
("CostSource", "fabro_model::CostSource", &[]),
|
||||
];
|
||||
for (name, path, impls) in replacements {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ pub mod types {
|
|||
pub use fabro_types::{
|
||||
ActivatedSkill, AgentControlState, AgentMcpToolSummary, AgentSkillActivationSource,
|
||||
AgentSkillSummary, AgentToolCategory, AgentToolSource, AgentToolSummary,
|
||||
AgentToolsAvailableProps, AskFabro, AuthMethod, AutomationRef, BilledTokenCounts,
|
||||
AgentToolsAvailableProps, AskFabro, AuthMethod, AutomationRef, BilledTokenCounts, BlobHash,
|
||||
CommandTermination, Conclusion, ContentPart, CreateVariableRequest, DiffStats, DiffSummary,
|
||||
DirtyStatus, EventEnvelope, ExecOutputTail, FailureCategory, FailureDetail,
|
||||
FailureSignature, GitContext, IdpIdentity, IntegrationConnectionKind,
|
||||
|
|
|
|||
50
lib/foundation/fabro-api/tests/blob_hash_round_trip.rs
Normal file
50
lib/foundation/fabro-api/tests/blob_hash_round_trip.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use std::any::{TypeId, type_name};
|
||||
|
||||
use fabro_api::types::{BlobHash as ApiBlobHash, WriteBlobResponse};
|
||||
use fabro_types::BlobHash;
|
||||
use serde_json::json;
|
||||
|
||||
const BLOB_HASH: &str = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824";
|
||||
|
||||
#[test]
|
||||
fn blob_hash_schema_reuses_domain_type() {
|
||||
assert_same_type::<ApiBlobHash, BlobHash>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_blob_response_round_trips_exact_wire_shape() {
|
||||
let value = json!({ "hash": BLOB_HASH });
|
||||
|
||||
let response: WriteBlobResponse = serde_json::from_value(value.clone()).unwrap();
|
||||
assert_eq!(serde_json::to_value(&response).unwrap(), value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blob_hash_emits_the_documented_lowercase_pattern() {
|
||||
// Serialization must match the OpenAPI schema pattern `^[0-9a-f]{64}$`.
|
||||
let hash: ApiBlobHash = serde_json::from_value(json!(BLOB_HASH)).unwrap();
|
||||
let emitted = serde_json::to_value(hash).unwrap();
|
||||
assert_eq!(emitted, json!(BLOB_HASH));
|
||||
|
||||
let text = emitted.as_str().unwrap();
|
||||
assert_eq!(text.len(), 64);
|
||||
assert!(
|
||||
text.bytes()
|
||||
.all(|byte| matches!(byte, b'0'..=b'9' | b'a'..=b'f'))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blob_hash_rejects_non_hex_values() {
|
||||
assert!(serde_json::from_value::<ApiBlobHash>(json!("not-a-blob-hash")).is_err());
|
||||
}
|
||||
|
||||
fn assert_same_type<Api: 'static, Domain: 'static>() {
|
||||
assert_eq!(
|
||||
TypeId::of::<Api>(),
|
||||
TypeId::of::<Domain>(),
|
||||
"{} must be the domain type {}",
|
||||
type_name::<Api>(),
|
||||
type_name::<Domain>()
|
||||
);
|
||||
}
|
||||
|
|
@ -1839,11 +1839,7 @@ impl Client {
|
|||
.await
|
||||
})
|
||||
.await?;
|
||||
response
|
||||
.into_inner()
|
||||
.hash
|
||||
.parse()
|
||||
.context("write_run_blob returned invalid blob hash")
|
||||
Ok(response.into_inner().hash)
|
||||
}
|
||||
|
||||
pub async fn read_run_blob(
|
||||
|
|
@ -1856,7 +1852,7 @@ impl Client {
|
|||
.client
|
||||
.read_run_blob()
|
||||
.id(run_id.to_string())
|
||||
.blob_hash(blob_hash.to_string())
|
||||
.blob_hash(*blob_hash)
|
||||
.send()
|
||||
.await;
|
||||
match response {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
*/
|
||||
export interface WriteBlobResponse {
|
||||
/**
|
||||
* Content-addressed hash of the stored blob.
|
||||
* Content-addressed SHA-256 hash of a stored blob.
|
||||
*/
|
||||
'hash': string;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue