mirror of
https://github.com/narrrl/proton-sdk-rs.git
synced 2026-11-03 16:41:26 +01:00
Merge pull request #1 from narrrl/feat/node-active-revision-id
implemented revisions
This commit is contained in:
6 files changed
+58
-4
No files matched your search
Generated
+2
-2
@@ -2030,7 +2030,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proton-drive-rs"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
@@ -2055,7 +2055,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proton-sdk"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"aes-gcm 0.11.0",
|
||||
"async-trait",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ resolver = "2"
|
||||
members = ["crates/proton-sdk", "crates/proton-drive-rs"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/narrrl/proton-sdk-rs"
|
||||
|
||||
@@ -14,7 +14,7 @@ readme = "../../README.md"
|
||||
documentation = "https://docs.rs/proton-drive-rs"
|
||||
|
||||
[dependencies]
|
||||
proton-sdk = { path = "../proton-sdk", version = "0.2.0" }
|
||||
proton-sdk = { path = "../proton-sdk", version = "0.2.2" }
|
||||
tokio.workspace = true
|
||||
reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@@ -5770,6 +5770,7 @@ impl ProtonDriveClient {
|
||||
// leaves the claimed metadata absent rather than failing the node.
|
||||
let mut claimed_size = None;
|
||||
let mut claimed_modification_time = None;
|
||||
let mut content_sha1 = None;
|
||||
if let Some(node_key) = node_key.as_ref()
|
||||
&& let Some(rev) = file.active_revision.as_ref()
|
||||
&& let Some(xattr) = rev.extended_attributes.as_deref()
|
||||
@@ -5787,6 +5788,7 @@ impl ProtonDriveClient {
|
||||
if let Some(common) = attrs.common {
|
||||
claimed_size = common.size;
|
||||
claimed_modification_time = common.modification_time;
|
||||
content_sha1 = common.digests.and_then(|d| d.sha1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -5801,8 +5803,10 @@ impl ProtonDriveClient {
|
||||
.active_revision
|
||||
.as_ref()
|
||||
.map(|rev| RevisionState::from_raw(rev.state)),
|
||||
active_revision_id: file.active_revision.as_ref().map(|rev| rev.id.clone()),
|
||||
claimed_size,
|
||||
claimed_modification_time,
|
||||
content_sha1,
|
||||
}
|
||||
}
|
||||
LinkType::Unknown => {
|
||||
|
||||
@@ -111,6 +111,14 @@ pub enum NodeKind {
|
||||
/// the file has no active revision (an unsealed draft).
|
||||
#[serde(default)]
|
||||
active_revision_state: Option<RevisionState>,
|
||||
/// Server-assigned identifier of the file's active revision
|
||||
/// (`ActiveRevision.RevisionID`); `None` for an unsealed draft, or when
|
||||
/// read through a surface that does not carry it. Unlike
|
||||
/// `modification_time` this is a stable revision identity: it advances iff
|
||||
/// a *new* revision was sealed, so a consumer can tell "the same revision,
|
||||
/// re-stamped" from "someone wrote a new revision" without a download.
|
||||
#[serde(default)]
|
||||
active_revision_id: Option<String>,
|
||||
/// Authoritative plaintext size from the active revision's decrypted
|
||||
/// extended attributes (C# `ClaimedSize`). `None` when the revision has
|
||||
/// no `XAttr` or it failed to decrypt.
|
||||
@@ -118,6 +126,12 @@ pub enum NodeKind {
|
||||
/// ISO-8601 modification time from the decrypted extended attributes
|
||||
/// (C# `ClaimedModificationTime`), verbatim as written by the uploader.
|
||||
claimed_modification_time: Option<String>,
|
||||
/// Lowercase-hex SHA-1 of the full plaintext, from the active revision's
|
||||
/// decrypted extended attributes (`Digests.SHA1`). A download-free content
|
||||
/// fingerprint: two files with the same size *and* the same digest hold
|
||||
/// the same bytes. `None` when the revision carries no digest (some
|
||||
/// clients omit it) or the `XAttr` failed to decrypt.
|
||||
content_sha1: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -222,6 +236,38 @@ impl FileThumbnail {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn file_node_carries_active_revision_id_and_defaults_it() {
|
||||
// A File node round-trips its revision id.
|
||||
let kind = NodeKind::File {
|
||||
media_type: "text/plain".into(),
|
||||
total_size_on_storage: 10,
|
||||
active_revision_state: Some(RevisionState::Active),
|
||||
active_revision_id: Some("rev-abc".into()),
|
||||
claimed_size: Some(4),
|
||||
claimed_modification_time: None,
|
||||
content_sha1: Some("da39a3ee".into()),
|
||||
};
|
||||
let json = serde_json::to_string(&kind).unwrap();
|
||||
let back: NodeKind = serde_json::from_str(&json).unwrap();
|
||||
match back {
|
||||
NodeKind::File {
|
||||
active_revision_id, ..
|
||||
} => assert_eq!(active_revision_id.as_deref(), Some("rev-abc")),
|
||||
NodeKind::Folder => panic!("expected a file"),
|
||||
}
|
||||
|
||||
// A sidecar written before the field existed deserializes to `None`.
|
||||
let legacy = r#"{"File":{"media_type":"text/plain","total_size_on_storage":10,"claimed_size":null,"claimed_modification_time":null}}"#;
|
||||
let back: NodeKind = serde_json::from_str(legacy).unwrap();
|
||||
match back {
|
||||
NodeKind::File {
|
||||
active_revision_id, ..
|
||||
} => assert!(active_revision_id.is_none()),
|
||||
NodeKind::Folder => panic!("expected a file"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revision_state_maps_the_wire_value() {
|
||||
assert_eq!(RevisionState::from_raw(Some(2)), RevisionState::Superseded);
|
||||
|
||||
@@ -528,10 +528,14 @@ impl ProtonDrivePublicLinkClient {
|
||||
active_revision_state: file
|
||||
.and_then(|f| f.active_revision.as_ref())
|
||||
.map(|revision| RevisionState::from_raw(revision.state)),
|
||||
active_revision_id: file
|
||||
.and_then(|f| f.active_revision.as_ref())
|
||||
.map(|revision| revision.id.clone()),
|
||||
// Claimed metadata lives in the revision's extended attributes,
|
||||
// which this read surface does not fetch.
|
||||
claimed_size: None,
|
||||
claimed_modification_time: None,
|
||||
content_sha1: None,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user