From b8d892878cd4fa69fe3711a4c6da502f1db4ea6c Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 11 Oct 2021 09:01:37 -0400 Subject: [PATCH] document sync data formats --- docs/src/sync-protocol.md | 35 ++++++++++++++ taskchampion/src/storage/operation.rs | 66 +++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/docs/src/sync-protocol.md b/docs/src/sync-protocol.md index 437f44b1e..1f111312c 100644 --- a/docs/src/sync-protocol.md +++ b/docs/src/sync-protocol.md @@ -38,6 +38,41 @@ This observation allows the server to discard older versions. The third invariant prevents the server from discarding versions if there is no snapshot. The fourth invariant prevents the server from discarding versions newer than the snapshot. +## Data Formats + +### Encryption + +TBD (#299) + +### Version + +The decrypted form of a version is a JSON array containing operations in the order they should be applied. +Each operation has the form `{TYPE: DATA}`, for example: + + * `{"Create":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7"}}` + * `{"Delete":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7"}}` + * `{"Update":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7","property":"prop","value":"v","timestamp":"2021-10-11T12:47:07.188090948Z"}}` + * `{"Update":{"uuid":"56e0be07-c61f-494c-a54c-bdcfdd52d2a7","property":"prop","value":null,"timestamp":"2021-10-11T12:47:07.188090948Z"}}` (to delete a property) + +Timestamps are in RFC3339 format with a `Z` suffix. + +### Snapshot + +The decrypted form of a snapshot is a JSON object mapping task IDs to task properties. +For example (pretty-printed for clarity): + +```json +{ + "56e0be07-c61f-494c-a54c-bdcfdd52d2a7": { + "description": "a task", + "priority": "H" + }, + "4b7ed904-f7b0-4293-8a10-ad452422c7b3": { + "description": "another task" + } +} +``` + ## Transactions ### AddVersion diff --git a/taskchampion/src/storage/operation.rs b/taskchampion/src/storage/operation.rs index 68f5fe7d0..ed9a5182d 100644 --- a/taskchampion/src/storage/operation.rs +++ b/taskchampion/src/storage/operation.rs @@ -274,6 +274,72 @@ mod test { ); } + #[test] + fn test_json_create() -> anyhow::Result<()> { + let uuid = Uuid::new_v4(); + let op = Create { uuid }; + assert_eq!( + serde_json::to_string(&op)?, + format!(r#"{{"Create":{{"uuid":"{}"}}}}"#, uuid), + ); + Ok(()) + } + + #[test] + fn test_json_delete() -> anyhow::Result<()> { + let uuid = Uuid::new_v4(); + let op = Delete { uuid }; + assert_eq!( + serde_json::to_string(&op)?, + format!(r#"{{"Delete":{{"uuid":"{}"}}}}"#, uuid), + ); + Ok(()) + } + + #[test] + fn test_json_update() -> anyhow::Result<()> { + let uuid = Uuid::new_v4(); + let timestamp = Utc::now(); + + let op = Update { + uuid, + property: "abc".into(), + value: Some("false".into()), + timestamp, + }; + + assert_eq!( + serde_json::to_string(&op)?, + format!( + r#"{{"Update":{{"uuid":"{}","property":"abc","value":"false","timestamp":"{:?}"}}}}"#, + uuid, timestamp, + ), + ); + Ok(()) + } + + #[test] + fn test_json_update_none() -> anyhow::Result<()> { + let uuid = Uuid::new_v4(); + let timestamp = Utc::now(); + + let op = Update { + uuid, + property: "abc".into(), + value: None, + timestamp, + }; + + assert_eq!( + serde_json::to_string(&op)?, + format!( + r#"{{"Update":{{"uuid":"{}","property":"abc","value":null,"timestamp":"{:?}"}}}}"#, + uuid, timestamp, + ), + ); + Ok(()) + } + fn uuid_strategy() -> impl Strategy { prop_oneof![ Just(Uuid::parse_str("83a2f9ef-f455-4195-b92e-a54c161eebfc").unwrap()),