From f1403a69840da55fdad3cca55c2e7d472150637b Mon Sep 17 00:00:00 2001 From: muji Date: Wed, 21 Aug 2024 10:33:02 +0800 Subject: [PATCH] Sync result (#547) * Remove sync_account() returns MergeOutcome. * Remove execute_sync() returns Option. * Split sync traits for single remote and multiple remotes. * Use enum for remote sync result. * Use RemoteResult in listen channel. * Bump sos-net version, prepare for next release. * Use type def for RemoteResult. * Include Origin in RemoteResult. * Prefer named fields for RemoteResult. * Define SyncResult. * Update sdk crate types to NetworkResult. * Update net crate to use SyncResult type. * Update sos cli for new SyncResult type. * Update test specs. * Bump minor version, prepare for next release. * Remove obsolete into_option() function. * Remove SyncError type. * Add has_errors() and first_error_ref(). * Rename to has_error(). * Update dependencies. --- Cargo.lock | 188 +++++++++++------- crates/account_extras/Cargo.toml | 4 +- crates/integration_tests/Cargo.toml | 1 - .../tests/access_control/allow.rs | 8 +- .../tests/access_control/deny.rs | 8 +- .../tests/auto_merge/create_secrets.rs | 14 +- .../tests/auto_merge/delete_secrets.rs | 17 +- .../tests/auto_merge/edit_secrets.rs | 20 +- .../tests/auto_merge/scan_commits.rs | 2 +- .../tests/file_transfers/multi_server.rs | 4 +- .../tests/file_transfers/offline_multi.rs | 6 +- .../tests/file_transfers/single_server.rs | 4 +- .../network_account/archive_unarchive.rs | 10 +- .../network_account/authenticator_sync.rs | 10 +- .../change_account_password.rs | 10 +- .../tests/network_account/change_cipher.rs | 14 +- .../network_account/change_folder_password.rs | 8 +- .../tests/network_account/compact_account.rs | 8 +- .../tests/network_account/compact_folder.rs | 8 +- .../network_account/folder_description.rs | 4 +- .../network_account/listen_folder_create.rs | 6 +- .../network_account/listen_folder_delete.rs | 8 +- .../network_account/listen_folder_import.rs | 6 +- .../network_account/listen_folder_rename.rs | 4 +- .../tests/network_account/listen_multiple.rs | 2 +- .../network_account/listen_secret_create.rs | 2 +- .../network_account/listen_secret_delete.rs | 6 +- .../network_account/listen_secret_update.rs | 10 +- .../tests/network_account/multiple_remotes.rs | 4 +- .../multiple_remotes_fallback.rs | 10 +- .../tests/network_account/no_sync.rs | 8 +- .../tests/network_account/offline_manual.rs | 16 +- .../network_account/recover_remote_folder.rs | 16 +- .../tests/network_account/rename_account.rs | 4 +- .../network_account/send_folder_create.rs | 4 +- .../network_account/send_folder_delete.rs | 8 +- .../network_account/send_folder_import.rs | 4 +- .../network_account/send_folder_rename.rs | 4 +- .../network_account/send_secret_create.rs | 4 +- .../network_account/send_secret_delete.rs | 6 +- .../tests/network_account/send_secret_move.rs | 10 +- .../network_account/send_secret_update.rs | 6 +- .../tests/pairing/device_revoke.rs | 14 +- .../tests/pairing/pairing_account_name.rs | 6 +- .../tests/pairing/pairing_inverted.rs | 6 +- .../tests/pairing/pairing_protocol.rs | 6 +- crates/net/Cargo.toml | 8 +- crates/net/src/account/auto_merge.rs | 4 +- crates/net/src/account/listen.rs | 14 +- crates/net/src/account/network_account.rs | 141 ++++++------- crates/net/src/account/remote.rs | 111 +++++------ crates/net/src/account/sync.rs | 70 ++++--- crates/net/src/error.rs | 6 +- crates/net/src/lib.rs | 4 +- crates/net/src/pairing/error.rs | 5 +- crates/net/src/pairing/websocket.rs | 10 +- crates/net/src/sync.rs | 104 ++++++++-- crates/protocol/Cargo.toml | 4 +- crates/protocol/src/sync/primitives.rs | 40 +--- crates/sdk/Cargo.toml | 2 +- crates/sdk/src/account/account.rs | 150 +++++++------- crates/server/Cargo.toml | 4 +- crates/sos/Cargo.toml | 4 +- crates/sos/src/commands/server.rs | 6 +- crates/sos/src/commands/sync.rs | 14 +- crates/sos/src/error.rs | 7 +- crates/test_utils/src/network.rs | 8 +- 67 files changed, 645 insertions(+), 599 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ec89f1e7e..cc53586867 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" version = "0.5.2" @@ -289,9 +295,9 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "asn1-rs" @@ -317,7 +323,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", "synstructure", ] @@ -329,7 +335,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -359,7 +365,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -381,7 +387,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -392,7 +398,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -512,7 +518,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -549,7 +555,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -740,9 +746,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68064e60dbf1f17005c2fde4d07c16d8baa506fd7ffed8ccab702d93617975c7" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "shlex", ] @@ -871,7 +877,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1155,7 +1161,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1203,7 +1209,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", "strsim 0.11.1", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1225,7 +1231,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1331,7 +1337,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1351,7 +1357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core 0.20.0", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1380,7 +1386,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1471,7 +1477,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1627,12 +1633,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -1703,7 +1709,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1796,7 +1802,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -1892,9 +1898,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", @@ -2166,7 +2172,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", "strsim 0.10.0", - "syn 2.0.74", + "syn 2.0.75", "unic-langid", ] @@ -2180,7 +2186,7 @@ dependencies = [ "i18n-config", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2445,7 +2451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5e25f9b861a88faa9d272ca4376e1a13c9a37d36de623f013c7bbb0ae2baa1" dependencies = [ "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2484,9 +2490,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.156" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libm" @@ -2554,7 +2560,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", "regex-syntax 0.8.4", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -2634,6 +2640,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.0.2" @@ -2935,7 +2950,7 @@ dependencies = [ "proc-macro2 1.0.86", "proc-macro2-diagnostics", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -3063,7 +3078,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -3111,7 +3126,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.7.4", ] [[package]] @@ -3169,7 +3184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2 1.0.86", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -3264,7 +3279,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", "version_check", "yansi 1.0.1", ] @@ -3296,7 +3311,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.74", + "syn 2.0.75", "tempfile", ] @@ -3310,7 +3325,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -3601,9 +3616,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", @@ -3640,7 +3655,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "winreg", + "windows-registry", ] [[package]] @@ -3716,7 +3731,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", "rust-embed-utils", - "syn 2.0.74", + "syn 2.0.75", "walkdir", ] @@ -3876,7 +3891,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4018,7 +4033,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4091,7 +4106,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4242,7 +4257,7 @@ dependencies = [ [[package]] name = "sos" -version = "0.14.12" +version = "0.15.0" dependencies = [ "arboard", "async-recursion", @@ -4280,7 +4295,7 @@ dependencies = [ [[package]] name = "sos-account-extras" -version = "0.14.5" +version = "0.15.0" dependencies = [ "once_cell", "rustc_version", @@ -4348,7 +4363,7 @@ dependencies = [ [[package]] name = "sos-net" -version = "0.14.11" +version = "0.15.0" dependencies = [ "anyhow", "async-recursion", @@ -4388,7 +4403,7 @@ dependencies = [ [[package]] name = "sos-protocol" -version = "0.14.11" +version = "0.15.0" dependencies = [ "anyhow", "async-trait", @@ -4410,7 +4425,7 @@ dependencies = [ [[package]] name = "sos-sdk" -version = "0.14.11" +version = "0.15.0" dependencies = [ "aes-gcm", "age", @@ -4485,7 +4500,7 @@ dependencies = [ [[package]] name = "sos-server" -version = "0.14.12" +version = "0.15.0" dependencies = [ "async-trait", "axum", @@ -4643,9 +4658,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.74" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", @@ -4663,6 +4678,9 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -4672,7 +4690,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4754,7 +4772,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -4867,9 +4885,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.2" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", @@ -4891,7 +4909,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -5137,7 +5155,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -5436,7 +5454,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", "uuid", ] @@ -5562,7 +5580,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", "wasm-bindgen-shared", ] @@ -5596,7 +5614,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5705,6 +5723,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -5937,16 +5985,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if 1.0.0", - "windows-sys 0.48.0", -] - [[package]] name = "wyz" version = "0.5.1" @@ -6047,7 +6085,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] @@ -6067,7 +6105,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.36", - "syn 2.0.74", + "syn 2.0.75", ] [[package]] diff --git a/crates/account_extras/Cargo.toml b/crates/account_extras/Cargo.toml index 12f82e811c..e3fa22ad83 100644 --- a/crates/account_extras/Cargo.toml +++ b/crates/account_extras/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sos-account-extras" -version = "0.14.5" +version = "0.15.0" edition = "2021" description = "Extra features for Save Our Secrets local accounts." homepage = "https://saveoursecrets.com" @@ -21,7 +21,7 @@ tokio.workspace = true once_cell.workspace = true [dependencies.sos-sdk] -version = "0.14.5" +version = "0.15" path = "../sdk" [build-dependencies] diff --git a/crates/integration_tests/Cargo.toml b/crates/integration_tests/Cargo.toml index 93c3691521..5e327f6915 100644 --- a/crates/integration_tests/Cargo.toml +++ b/crates/integration_tests/Cargo.toml @@ -38,7 +38,6 @@ pretty_assertions = "1.4" anticipate-runner = { version = "0.5.1" } [dev-dependencies.sos-net] -version = "0.14.0" features = ["full"] path = "../net" diff --git a/crates/integration_tests/tests/access_control/allow.rs b/crates/integration_tests/tests/access_control/allow.rs index 64c577e2d9..bc3dd6d8ea 100644 --- a/crates/integration_tests/tests/access_control/allow.rs +++ b/crates/integration_tests/tests/access_control/allow.rs @@ -6,8 +6,7 @@ use crate::test_utils::{ }; use http::StatusCode; use sos_net::{ - protocol::SyncError, sdk::prelude::*, Error as ClientError, - NetworkAccount, RemoteSync, + sdk::prelude::*, AccountSync, Error as ClientError, NetworkAccount, }; use sos_server::AccessControlConfig; @@ -52,10 +51,9 @@ async fn access_control_allow() -> Result<()> { allowed.owner.add_server(origin.clone()).await?; denied.add_server(origin.clone()).await?; - assert!(allowed.owner.sync().await.is_none()); + assert!(allowed.owner.sync().await.first_error().is_none()); let sync_error = denied.sync().await; - if let Some(SyncError { mut errors }) = sync_error { - let (_, err) = errors.remove(0); + if let Some(err) = sync_error.first_error() { assert!(matches!( err, ClientError::ResponseCode(StatusCode::FORBIDDEN) diff --git a/crates/integration_tests/tests/access_control/deny.rs b/crates/integration_tests/tests/access_control/deny.rs index 58fb9d09ce..b335663ea8 100644 --- a/crates/integration_tests/tests/access_control/deny.rs +++ b/crates/integration_tests/tests/access_control/deny.rs @@ -6,8 +6,7 @@ use crate::test_utils::{ }; use http::StatusCode; use sos_net::{ - protocol::SyncError, sdk::prelude::*, Error as ClientError, - NetworkAccount, RemoteSync, + sdk::prelude::*, AccountSync, Error as ClientError, NetworkAccount, }; use sos_server::AccessControlConfig; @@ -51,10 +50,9 @@ async fn access_control_deny() -> Result<()> { allowed.owner.add_server(origin.clone()).await?; denied.add_server(origin.clone()).await?; - assert!(allowed.owner.sync().await.is_none()); + assert!(allowed.owner.sync().await.first_error().is_none()); let sync_error = denied.sync().await; - if let Some(SyncError { mut errors }) = sync_error { - let (_, err) = errors.remove(0); + if let Some(err) = sync_error.first_error() { assert!(matches!( err, ClientError::ResponseCode(StatusCode::FORBIDDEN) diff --git a/crates/integration_tests/tests/auto_merge/create_secrets.rs b/crates/integration_tests/tests/auto_merge/create_secrets.rs index 4d9156ed11..acd859ef8a 100644 --- a/crates/integration_tests/tests/auto_merge/create_secrets.rs +++ b/crates/integration_tests/tests/auto_merge/create_secrets.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests making conflicting changes to a folder whilst /// a server is offline and resolving the conflicts with @@ -35,7 +35,7 @@ async fn auto_merge_create_secrets() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result1.sync_error.is_some()); + assert!(result1.sync_result.first_error().is_some()); // Create a secret on second device and fail to sync let (meta, secret) = mock::note("note_2", "offline_secret_2"); @@ -43,7 +43,7 @@ async fn auto_merge_create_secrets() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result2.sync_error.is_some()); + assert!(result2.sync_result.first_error().is_some()); let device1_folder_state = device1.owner.commit_state(&default_folder).await?; @@ -59,12 +59,12 @@ async fn auto_merge_create_secrets() -> Result<()> { let _server = spawn(TEST_ID, Some(addr), None).await?; // Sync the first device - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); // Sync the second device which will auto merge local // changes with the remote so it has both secrets - let sync_error = device2.owner.sync().await; - assert!(sync_error.is_none()); + let sync_result = device2.owner.sync().await; + assert!(sync_result.first_error().is_none()); // Second device now has both secrets let (s1, _) = device2 @@ -94,7 +94,7 @@ async fn auto_merge_create_secrets() -> Result<()> { // Sync the first device again to fetch the remote commits // that were changed when the auto merge executed - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); // First device now has both secrets let (s1, _) = device1 diff --git a/crates/integration_tests/tests/auto_merge/delete_secrets.rs b/crates/integration_tests/tests/auto_merge/delete_secrets.rs index 6d34947cef..1b57447f0c 100644 --- a/crates/integration_tests/tests/auto_merge/delete_secrets.rs +++ b/crates/integration_tests/tests/auto_merge/delete_secrets.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests making deletes to a folder whilst /// a server is offline and resolving the conflicts with @@ -30,7 +30,7 @@ async fn auto_merge_delete_secrets() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result1.sync_error.is_none()); + assert!(result1.sync_result.first_error().is_none()); // Create a secret (device2) which will auto merge // @@ -43,8 +43,7 @@ async fn auto_merge_delete_secrets() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - println!("{:#?}", result2.sync_error); - assert!(result2.sync_error.is_none()); + assert!(result2.sync_result.first_error().is_none()); // Oh no, the server has gone offline! drop(server); @@ -56,14 +55,14 @@ async fn auto_merge_delete_secrets() -> Result<()> { .owner .delete_secret(&result1.id, Default::default()) .await?; - assert!(result.sync_error.is_some()); + assert!(result.sync_result.first_error().is_some()); // Second device deletes it's secret let result = device2 .owner .delete_secret(&result2.id, Default::default()) .await?; - assert!(result.sync_error.is_some()); + assert!(result.sync_result.first_error().is_some()); let device1_folder_state = device1.owner.commit_state(&default_folder).await?; @@ -83,13 +82,13 @@ async fn auto_merge_delete_secrets() -> Result<()> { // This brings the first client and server into sync // with both create secrets and the deletion on the // first client but the second client is out of sync. - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); // Sync second device to auto merge and push their offline changes - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Sync first device again to fetch the pushed changes - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); // Folder commits are back in sync let device1_folder_state = diff --git a/crates/integration_tests/tests/auto_merge/edit_secrets.rs b/crates/integration_tests/tests/auto_merge/edit_secrets.rs index e00d0c1f88..5c47c30146 100644 --- a/crates/integration_tests/tests/auto_merge/edit_secrets.rs +++ b/crates/integration_tests/tests/auto_merge/edit_secrets.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests making conflicting edits to a folder whilst /// a server is offline and resolving the conflicts with @@ -30,10 +30,10 @@ async fn auto_merge_edit_secrets() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Sync to fetch the new secret on the second device - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Oh no, the server has gone offline! drop(server); @@ -42,7 +42,7 @@ async fn auto_merge_edit_secrets() -> Result<()> { // Update the secret whilst offline on first device let (meta, secret) = mock::note("edit_1", TEST_ID); - let SecretChange { sync_error, .. } = device1 + let SecretChange { sync_result, .. } = device1 .owner .update_secret( &result.id, @@ -52,11 +52,11 @@ async fn auto_merge_edit_secrets() -> Result<()> { None, ) .await?; - assert!(sync_error.is_some()); + assert!(sync_result.first_error().is_some()); // Update the secret whilst offline on second device let (meta, secret) = mock::note("edit_2", TEST_ID); - let SecretChange { sync_error, .. } = device2 + let SecretChange { sync_result, .. } = device2 .owner .update_secret( &result.id, @@ -66,7 +66,7 @@ async fn auto_merge_edit_secrets() -> Result<()> { None, ) .await?; - assert!(sync_error.is_some()); + assert!(sync_result.first_error().is_some()); let device1_folder_state = device1.owner.commit_state(&default_folder).await?; @@ -82,13 +82,13 @@ async fn auto_merge_edit_secrets() -> Result<()> { let _server = spawn(TEST_ID, Some(addr), None).await?; // Sync first device to push changes - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); // Sync second device to auto merge - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Sync first device again to fetch auto merged changes - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); let device1_folder_state = device1.owner.commit_state(&default_folder).await?; diff --git a/crates/integration_tests/tests/auto_merge/scan_commits.rs b/crates/integration_tests/tests/auto_merge/scan_commits.rs index ecbb931338..e9e42b69e7 100644 --- a/crates/integration_tests/tests/auto_merge/scan_commits.rs +++ b/crates/integration_tests/tests/auto_merge/scan_commits.rs @@ -32,7 +32,7 @@ async fn auto_merge_scan_commits() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); let (meta, secret) = mock::note("note_edited", TEST_ID); device .owner diff --git a/crates/integration_tests/tests/file_transfers/multi_server.rs b/crates/integration_tests/tests/file_transfers/multi_server.rs index 1a53e912ba..3860542264 100644 --- a/crates/integration_tests/tests/file_transfers/multi_server.rs +++ b/crates/integration_tests/tests/file_transfers/multi_server.rs @@ -7,7 +7,7 @@ use crate::test_utils::{ mock::files::{create_file_secret, update_file_secret}, simulate_device, spawn, teardown, wait_for_num_transfers, }; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests uploading an external file to multiple servers. #[tokio::test] @@ -295,7 +295,7 @@ async fn file_transfers_multi_download() -> Result<()> { { // Sync pulls down the file event logs and // creates the pending download transfer operation - assert!(downloader.owner.sync().await.is_none()); + assert!(downloader.owner.sync().await.first_error().is_none()); wait_for_num_transfers(&downloader.owner, 1).await?; let server1_path = downloader.server_path; diff --git a/crates/integration_tests/tests/file_transfers/offline_multi.rs b/crates/integration_tests/tests/file_transfers/offline_multi.rs index a32407c388..27bfbbf67e 100644 --- a/crates/integration_tests/tests/file_transfers/offline_multi.rs +++ b/crates/integration_tests/tests/file_transfers/offline_multi.rs @@ -8,7 +8,7 @@ use crate::test_utils::{ mock::files::{create_file_secret, update_file_secret}, simulate_device, spawn, teardown, wait_for_file, wait_for_file_not_exist, }; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests uploading an external file to multiple servers /// when the first server is offline. @@ -388,8 +388,8 @@ async fn file_transfers_offline_multi_download() -> Result<()> { // // We have an error here as the first server will fail // to connect for the sync. - let sync_error = downloader.owner.sync().await; - assert!(sync_error.is_none()); + let sync_result = downloader.owner.sync().await; + assert!(sync_result.first_error().is_none()); // Wait for the file to exist let paths = downloader.owner.paths(); diff --git a/crates/integration_tests/tests/file_transfers/single_server.rs b/crates/integration_tests/tests/file_transfers/single_server.rs index 8a9a2cbadd..c2f6be75dc 100644 --- a/crates/integration_tests/tests/file_transfers/single_server.rs +++ b/crates/integration_tests/tests/file_transfers/single_server.rs @@ -7,7 +7,7 @@ use crate::test_utils::{ mock::files::{create_file_secret, update_file_secret}, simulate_device, spawn, teardown, wait_for_num_transfers, }; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests uploading an external file. #[tokio::test] @@ -266,7 +266,7 @@ async fn file_transfers_single_download() -> Result<()> { { // Sync pulls down the file event logs and // creates the pending download transfer operation - assert!(downloader.owner.sync().await.is_none()); + assert!(downloader.owner.sync().await.first_error().is_none()); wait_for_num_transfers(&downloader.owner, 1).await?; diff --git a/crates/integration_tests/tests/network_account/archive_unarchive.rs b/crates/integration_tests/tests/network_account/archive_unarchive.rs index 15ee64982a..ee75e077e7 100644 --- a/crates/integration_tests/tests/network_account/archive_unarchive.rs +++ b/crates/integration_tests/tests/network_account/archive_unarchive.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ simulate_device_with_builder, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests moving to and from an archive folder. #[tokio::test] @@ -35,8 +35,8 @@ async fn network_sync_archive_unarchive() -> Result<()> { .await?; // Sync so they both have the secret - assert!(device1.owner.sync().await.is_none()); - assert!(device2.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Move to the archive let SecretMove { id: secret_id, .. } = device1 @@ -52,7 +52,7 @@ async fn network_sync_archive_unarchive() -> Result<()> { assert_eq!(1, num_events(&mut device2.owner, archive_folder.id()).await); // Second client syncs - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Events should be in sync now assert_eq!(3, num_events(&mut device1.owner, default_folder.id()).await); @@ -81,7 +81,7 @@ async fn network_sync_archive_unarchive() -> Result<()> { assert_eq!(2, num_events(&mut device2.owner, archive_folder.id()).await); // Second client syncs to get up to date - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); let mut bridge = device1.owner.remove_server(&origin).await?.unwrap(); assert_local_remote_events_eq( diff --git a/crates/integration_tests/tests/network_account/authenticator_sync.rs b/crates/integration_tests/tests/network_account/authenticator_sync.rs index aadb3c8b93..063171b9aa 100644 --- a/crates/integration_tests/tests/network_account/authenticator_sync.rs +++ b/crates/integration_tests/tests/network_account/authenticator_sync.rs @@ -1,6 +1,6 @@ use crate::test_utils::{mock, simulate_device, spawn, teardown}; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests syncing an authenticator folder after /// disabling the NO_SYNC flag. @@ -36,8 +36,8 @@ async fn network_authenticator_sync() -> Result<()> { .await?; // Desktop syncs before NO_SYNC flag has been removed - let sync_error = desktop.owner.sync().await; - assert!(sync_error.is_none()); + let sync_result = desktop.owner.sync().await; + assert!(sync_result.first_error().is_none()); // Try to read the secret but can't as the server // will not send events when NO_SYNC is set @@ -57,8 +57,8 @@ async fn network_authenticator_sync() -> Result<()> { .await?; // Sync the account on the desktop device - let sync_error = desktop.owner.sync().await; - assert!(sync_error.is_none()); + let sync_result = desktop.owner.sync().await; + assert!(sync_result.first_error().is_none()); // Should be able to read the TOTP on the synced desktop device let (data, _) = diff --git a/crates/integration_tests/tests/network_account/change_account_password.rs b/crates/integration_tests/tests/network_account/change_account_password.rs index f5a7229aad..31ea32b75d 100644 --- a/crates/integration_tests/tests/network_account/change_account_password.rs +++ b/crates/integration_tests/tests/network_account/change_account_password.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, mock, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests changing the account password and force syncing /// the updated and diverged account data. @@ -32,7 +32,7 @@ async fn network_sync_change_account_password() -> Result<()> { .await?; // Sync on the second device to fetch initial account state - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); let (new_password, _) = generate_passphrase()?; device1 @@ -58,8 +58,8 @@ async fn network_sync_change_account_password() -> Result<()> { // account data. // // Account will be signed out due to the forced pull. - let sync_error = device2.owner.sync().await; - assert!(sync_error.is_none()); + let sync_result = device2.owner.sync().await; + assert!(sync_result.first_error().is_none()); // Check we can sign in again // on the device that just synced using the @@ -74,7 +74,7 @@ async fn network_sync_change_account_password() -> Result<()> { .await?; // Sync on the original device and check it can read the secret - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); let (secret_data, _) = device1 .owner .read_secret(&id, Some(default_folder.clone())) diff --git a/crates/integration_tests/tests/network_account/change_cipher.rs b/crates/integration_tests/tests/network_account/change_cipher.rs index ef0852fa4d..25c89dbfee 100644 --- a/crates/integration_tests/tests/network_account/change_cipher.rs +++ b/crates/integration_tests/tests/network_account/change_cipher.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, mock, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests changing the account cipher and force syncing /// the updated and diverged account data. @@ -36,7 +36,7 @@ async fn network_sync_change_cipher() -> Result<()> { .await?; // Sync on the second device to fetch initial account state - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); let target_cipher = Cipher::XChaCha20Poly1305; let target_kdf = KeyDerivation::BalloonHash; @@ -80,7 +80,7 @@ async fn network_sync_change_cipher() -> Result<()> { // Try to sync on other device after force update // which should perform a force pull to update the // account data - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Check we can sign in again device2.owner.sign_in(&key).await?; @@ -92,14 +92,16 @@ async fn network_sync_change_cipher() -> Result<()> { // Create a secret on the synced device let (meta, secret) = mock::note(TEST_ID, TEST_ID); - let SecretChange { id, sync_error, .. } = device2 + let SecretChange { + id, sync_result, .. + } = device2 .owner .create_secret(meta.clone(), secret.clone(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Sync on the original device and check it can read the secret - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); let device1_commit = device1.owner.root_commit(&default_folder).await?; let device2_commit = device2.owner.root_commit(&default_folder).await?; diff --git a/crates/integration_tests/tests/network_account/change_folder_password.rs b/crates/integration_tests/tests/network_account/change_folder_password.rs index 02311906b3..67ef15ab93 100644 --- a/crates/integration_tests/tests/network_account/change_folder_password.rs +++ b/crates/integration_tests/tests/network_account/change_folder_password.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, mock, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests changing a folder password and force syncing /// the updated folder events log. @@ -34,7 +34,7 @@ async fn network_sync_change_folder_password() -> Result<()> { .await?; // Sync on the second device to fetch initial account state - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); let (new_password, _) = generate_passphrase()?; let key: AccessKey = new_password.into(); @@ -58,7 +58,7 @@ async fn network_sync_change_folder_password() -> Result<()> { // Try to sync on other device after force update // which should perform a force pull to update the // account data - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Check we can sign out and sign in again // on the device that just synced using the @@ -73,7 +73,7 @@ async fn network_sync_change_folder_password() -> Result<()> { .await?; // Sync on the original device and check it can read the secret - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); let (secret_data, _) = device1 .owner .read_secret(&id, Some(default_folder.clone())) diff --git a/crates/integration_tests/tests/network_account/compact_account.rs b/crates/integration_tests/tests/network_account/compact_account.rs index 51757c2f1b..c0b5aa50c7 100644 --- a/crates/integration_tests/tests/network_account/compact_account.rs +++ b/crates/integration_tests/tests/network_account/compact_account.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, mock, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests compacting all the folders in an account and /// syncing the changes to another device. @@ -39,7 +39,7 @@ async fn network_sync_compact_account() -> Result<()> { .await?; // Sync on the second device to fetch initial account state - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Compact the account folders (rewrites all event logs) device1.owner.compact_account().await?; @@ -55,7 +55,7 @@ async fn network_sync_compact_account() -> Result<()> { // Try to sync on other device after force update // which should perform a force pull to update the // account data - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Check we can read in the secret data on synced device let (secret_data, _) = device2 @@ -80,7 +80,7 @@ async fn network_sync_compact_account() -> Result<()> { .await?; // Sync on the original device and check it can read the secret - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); let (secret_data, _) = device1 .owner .read_secret(&id, Some(default_folder.clone())) diff --git a/crates/integration_tests/tests/network_account/compact_folder.rs b/crates/integration_tests/tests/network_account/compact_folder.rs index ccfef782e3..c925272532 100644 --- a/crates/integration_tests/tests/network_account/compact_folder.rs +++ b/crates/integration_tests/tests/network_account/compact_folder.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, mock, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests compacting a single folders and /// syncing the changes to another device. @@ -39,7 +39,7 @@ async fn network_sync_compact_folder() -> Result<()> { .await?; // Sync on the second device to fetch initial account state - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Compact the folder device1.owner.compact_folder(&default_folder).await?; @@ -55,7 +55,7 @@ async fn network_sync_compact_folder() -> Result<()> { // Try to sync on other device after force update // which should perform a force pull to update the // account data - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Check we can read in the secret data on synced device let (secret_data, _) = device2 @@ -90,7 +90,7 @@ async fn network_sync_compact_folder() -> Result<()> { .await?; // Sync on the original device and check it can read the secret - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); let (secret_data, _) = device1 .owner .read_secret(&id, Some(default_folder.clone())) diff --git a/crates/integration_tests/tests/network_account/folder_description.rs b/crates/integration_tests/tests/network_account/folder_description.rs index 378be1acab..4357ab2bad 100644 --- a/crates/integration_tests/tests/network_account/folder_description.rs +++ b/crates/integration_tests/tests/network_account/folder_description.rs @@ -19,14 +19,14 @@ async fn network_sync_folder_description() -> Result<()> { let default_folder = device.default_folder.clone(); let folders = device.folders.clone(); - let FolderChange { sync_error, .. } = device + let FolderChange { sync_result, .. } = device .owner .set_folder_description( &default_folder, "new_description".to_string(), ) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Get the remote out of the owner so we can // assert on equality between local and remote diff --git a/crates/integration_tests/tests/network_account/listen_folder_create.rs b/crates/integration_tests/tests/network_account/listen_folder_create.rs index 505847426d..d31b6ad72e 100644 --- a/crates/integration_tests/tests/network_account/listen_folder_create.rs +++ b/crates/integration_tests/tests/network_account/listen_folder_create.rs @@ -36,13 +36,13 @@ async fn network_sync_listen_folder_create() -> Result<()> { let FolderCreate { folder: new_folder, - sync_error, + sync_result, .. } = device1 .owner .create_folder("sync_folder".to_string(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Our new local folder should have the single create vault event assert_eq!(1, num_events(&mut device1.owner, new_folder.id()).await); @@ -62,7 +62,7 @@ async fn network_sync_listen_folder_create() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Pause a while to allow the first owner to sync // with the new change diff --git a/crates/integration_tests/tests/network_account/listen_folder_delete.rs b/crates/integration_tests/tests/network_account/listen_folder_delete.rs index 825b1aa282..a1355765b1 100644 --- a/crates/integration_tests/tests/network_account/listen_folder_delete.rs +++ b/crates/integration_tests/tests/network_account/listen_folder_delete.rs @@ -27,17 +27,17 @@ async fn network_sync_listen_folder_delete() -> Result<()> { let FolderCreate { folder: new_folder, - sync_error, + sync_result, .. } = device1 .owner .create_folder("sync_folder".to_string(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); - let FolderDelete { sync_error, .. } = + let FolderDelete { sync_result, .. } = device1.owner.delete_folder(&new_folder).await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); let mut server_files = vec![ server_path.join(&address).join(format!( diff --git a/crates/integration_tests/tests/network_account/listen_folder_import.rs b/crates/integration_tests/tests/network_account/listen_folder_import.rs index a438cb0659..caae8bc194 100644 --- a/crates/integration_tests/tests/network_account/listen_folder_import.rs +++ b/crates/integration_tests/tests/network_account/listen_folder_import.rs @@ -27,13 +27,13 @@ async fn network_sync_listen_folder_import() -> Result<()> { let FolderCreate { folder: new_folder, - sync_error, + sync_result, .. } = device1 .owner .create_folder("sync_folder".to_string(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Open the new folder so we can get a copy of the data, // using the same vault data ensures the same identifier @@ -76,7 +76,7 @@ async fn network_sync_listen_folder_import() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Pause a while to allow the first owner to sync // with the new change diff --git a/crates/integration_tests/tests/network_account/listen_folder_rename.rs b/crates/integration_tests/tests/network_account/listen_folder_rename.rs index 26492c31c6..94c53ef460 100644 --- a/crates/integration_tests/tests/network_account/listen_folder_rename.rs +++ b/crates/integration_tests/tests/network_account/listen_folder_rename.rs @@ -28,11 +28,11 @@ async fn network_sync_listen_folder_rename() -> Result<()> { device1.listen().await?; device2.listen().await?; - let FolderChange { sync_error, .. } = device1 + let FolderChange { sync_result, .. } = device1 .owner .rename_folder(&default_folder, "new_name".to_string()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Pause a while to give the listener some time to process // the change notification diff --git a/crates/integration_tests/tests/network_account/listen_multiple.rs b/crates/integration_tests/tests/network_account/listen_multiple.rs index dbadfc6cb0..b15f301fe8 100644 --- a/crates/integration_tests/tests/network_account/listen_multiple.rs +++ b/crates/integration_tests/tests/network_account/listen_multiple.rs @@ -39,7 +39,7 @@ async fn network_sync_listen_multiple() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); /* // First client is now ahead diff --git a/crates/integration_tests/tests/network_account/listen_secret_create.rs b/crates/integration_tests/tests/network_account/listen_secret_create.rs index 220c4c482f..255134d9ed 100644 --- a/crates/integration_tests/tests/network_account/listen_secret_create.rs +++ b/crates/integration_tests/tests/network_account/listen_secret_create.rs @@ -36,7 +36,7 @@ async fn network_sync_listen_secret_create() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); /* // First client is now ahead diff --git a/crates/integration_tests/tests/network_account/listen_secret_delete.rs b/crates/integration_tests/tests/network_account/listen_secret_delete.rs index dce0c902aa..3aff60b780 100644 --- a/crates/integration_tests/tests/network_account/listen_secret_delete.rs +++ b/crates/integration_tests/tests/network_account/listen_secret_delete.rs @@ -36,14 +36,14 @@ async fn network_sync_listen_secret_delete() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Delete the secret - let SecretDelete { sync_error, .. } = device1 + let SecretDelete { sync_result, .. } = device1 .owner .delete_secret(&result.id, Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Pause a while to give the listener some time to process // the change notification diff --git a/crates/integration_tests/tests/network_account/listen_secret_update.rs b/crates/integration_tests/tests/network_account/listen_secret_update.rs index 7827f4558c..abc099273c 100644 --- a/crates/integration_tests/tests/network_account/listen_secret_update.rs +++ b/crates/integration_tests/tests/network_account/listen_secret_update.rs @@ -36,12 +36,12 @@ async fn network_sync_listen_secret_update() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Update the secret let (meta, secret) = mock::note("note_first_owner", "send_events_secret_updated"); - let SecretChange { sync_error, .. } = device1 + let SecretChange { sync_result, .. } = device1 .owner .update_secret( &result.id, @@ -52,11 +52,7 @@ async fn network_sync_listen_secret_update() -> Result<()> { ) .await?; - if sync_error.is_some() { - println!("{:#?}", sync_error); - } - - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Pause a while to give the listener some time to process // the change notification diff --git a/crates/integration_tests/tests/network_account/multiple_remotes.rs b/crates/integration_tests/tests/network_account/multiple_remotes.rs index 7f2e340c3b..d9c0c3d5ff 100644 --- a/crates/integration_tests/tests/network_account/multiple_remotes.rs +++ b/crates/integration_tests/tests/network_account/multiple_remotes.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, mock, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests syncing a single client with multiple /// remote servers. @@ -24,7 +24,7 @@ async fn network_sync_multiple_remotes() -> Result<()> { device.owner.add_server(origin.clone()).await?; // Sync again with the additional remote - assert!(device.owner.sync().await.is_none()); + assert!(device.owner.sync().await.first_error().is_none()); // Create a secret that should be synced to multiple remotes let (meta, secret) = mock::note("note", TEST_ID); diff --git a/crates/integration_tests/tests/network_account/multiple_remotes_fallback.rs b/crates/integration_tests/tests/network_account/multiple_remotes_fallback.rs index 58f8bd843c..a4b457653d 100644 --- a/crates/integration_tests/tests/network_account/multiple_remotes_fallback.rs +++ b/crates/integration_tests/tests/network_account/multiple_remotes_fallback.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync, SyncError}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests syncing a single client with multiple /// remote servers when one of the servers is offline. @@ -27,7 +27,7 @@ async fn network_sync_multiple_remotes_fallback() -> Result<()> { device.owner.add_server(origin.clone()).await?; // Sync again with the additional remote - assert!(device.owner.sync().await.is_none()); + assert!(device.owner.sync().await.first_error().is_none()); // Shutdown the first server drop(server1); @@ -42,15 +42,15 @@ async fn network_sync_multiple_remotes_fallback() -> Result<()> { // Explicit sync afterwards, triggers the code path // where we try to connect to a remote which is down - let sync_error = device.owner.sync().await; - assert!(matches!(sync_error, Some(SyncError { .. }))); + let sync_result = device.owner.sync().await; + assert!(sync_result.first_error().is_some()); // Bring the server back online let server1 = spawn(TEST_ID, Some(addr), Some("server1")).await?; sync_pause(None).await; // Now we should be able to sync to both remotes - assert!(device.owner.sync().await.is_none()); + assert!(device.owner.sync().await.first_error().is_none()); // Assert on first server let mut bridge = diff --git a/crates/integration_tests/tests/network_account/no_sync.rs b/crates/integration_tests/tests/network_account/no_sync.rs index 5867985847..5f145cb55d 100644 --- a/crates/integration_tests/tests/network_account/no_sync.rs +++ b/crates/integration_tests/tests/network_account/no_sync.rs @@ -1,7 +1,7 @@ use crate::test_utils::{setup, simulate_device, spawn, teardown}; use anyhow::Result; use sos_net::{ - protocol::SyncStorage, sdk::prelude::*, NetworkAccount, RemoteSync, + protocol::SyncStorage, sdk::prelude::*, AccountSync, NetworkAccount, SyncClient, }; @@ -46,7 +46,7 @@ async fn network_no_sync_create_account() -> Result<()> { // Configure the server on the client account account.add_server(server.origin.clone()).await?; - assert!(account.sync().await.is_none()); + assert!(account.sync().await.first_error().is_none()); // Server should not contain the folder files // as the NO_SYNC flag was set before the first sync @@ -88,7 +88,7 @@ async fn network_no_sync_update_account() -> Result<()> { .await?; // Sync the account to push the new folder - assert!(device.owner.sync().await.is_none()); + assert!(device.owner.sync().await.first_error().is_none()); // Update the folder with new flags. device @@ -101,7 +101,7 @@ async fn network_no_sync_update_account() -> Result<()> { // Sync the account again which should ignore the updates // to the new folder that has now been marked with NO_SYNC - assert!(device.owner.sync().await.is_none()); + assert!(device.owner.sync().await.first_error().is_none()); // Local should be equal with remote now. // diff --git a/crates/integration_tests/tests/network_account/offline_manual.rs b/crates/integration_tests/tests/network_account/offline_manual.rs index f9f9e25e5c..8a14956282 100644 --- a/crates/integration_tests/tests/network_account/offline_manual.rs +++ b/crates/integration_tests/tests/network_account/offline_manual.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ sync_pause, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests syncing events between two clients after /// a server goes offline and a client commits changes @@ -38,13 +38,13 @@ async fn network_sync_offline_manual() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_some()); + assert!(result.sync_result.first_error().is_some()); let (_, _) = device1 .owner .read_secret(&result.id, Default::default()) .await?; let (meta, secret) = mock::note("note_edited", "offline_secret_edit"); - let SecretChange { sync_error, .. } = device1 + let SecretChange { sync_result, .. } = device1 .owner .update_secret( &result.id, @@ -54,12 +54,12 @@ async fn network_sync_offline_manual() -> Result<()> { None, ) .await?; - assert!(sync_error.is_some()); - let SecretDelete { sync_error, .. } = device1 + assert!(sync_result.first_error().is_some()); + let SecretDelete { sync_result, .. } = device1 .owner .delete_secret(&result.id, Default::default()) .await?; - assert!(sync_error.is_some()); + assert!(sync_result.first_error().is_some()); // The first client is now very much ahead of the second client assert_eq!(4, num_events(&mut device1.owner, &default_folder_id).await); @@ -75,10 +75,10 @@ async fn network_sync_offline_manual() -> Result<()> { // they signed in again (which is a natural time to sync). // // This should push the local changes to the remote. - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); // The client explicitly sync from the other device too. - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Now both devices should be up to date assert_eq!(4, num_events(&mut device1.owner, &default_folder_id).await); diff --git a/crates/integration_tests/tests/network_account/recover_remote_folder.rs b/crates/integration_tests/tests/network_account/recover_remote_folder.rs index 91ddc10660..a00e859b9e 100644 --- a/crates/integration_tests/tests/network_account/recover_remote_folder.rs +++ b/crates/integration_tests/tests/network_account/recover_remote_folder.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, mock, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests recovering a folder from a remote origin after /// it has been removed from the local account. @@ -21,21 +21,23 @@ async fn network_sync_recover_remote_folder() -> Result<()> { // Create a folder let FolderCreate { folder: new_folder, - sync_error, + sync_result, .. } = device .owner .create_folder(TEST_ID.to_string(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Create a secret in the new folder let (meta, secret) = mock::note(TEST_ID, TEST_ID); - let SecretChange { id, sync_error, .. } = device + let SecretChange { + id, sync_result, .. + } = device .owner .create_secret(meta.clone(), secret, new_folder.clone().into()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Remove the folder files from the local account let paths = device.owner.paths(); @@ -54,8 +56,8 @@ async fn network_sync_recover_remote_folder() -> Result<()> { // Now sync is broken as the local account is // in an inconsistent state - let sync_error = device.owner.sync().await; - assert!(sync_error.is_some()); + let sync_result = device.owner.sync().await; + assert!(sync_result.first_error().is_some()); // Recover the folder from the remote origin device diff --git a/crates/integration_tests/tests/network_account/rename_account.rs b/crates/integration_tests/tests/network_account/rename_account.rs index bb6b5d17b5..5cb3dcc207 100644 --- a/crates/integration_tests/tests/network_account/rename_account.rs +++ b/crates/integration_tests/tests/network_account/rename_account.rs @@ -2,7 +2,7 @@ use crate::test_utils::{ assert_local_remote_events_eq, simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests changing the account name is synced. #[tokio::test] @@ -50,7 +50,7 @@ async fn network_sync_rename_account() -> Result<()> { assert_ne!(device1_name, device2_name); // Sync on the other device - assert!(device2.owner.sync().await.is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Now the names are back in sync let device1_name = { diff --git a/crates/integration_tests/tests/network_account/send_folder_create.rs b/crates/integration_tests/tests/network_account/send_folder_create.rs index 6027b5a10e..99096bedf3 100644 --- a/crates/integration_tests/tests/network_account/send_folder_create.rs +++ b/crates/integration_tests/tests/network_account/send_folder_create.rs @@ -23,14 +23,14 @@ async fn network_sync_folder_create() -> Result<()> { let FolderCreate { folder: new_folder, - sync_error, + sync_result, .. } = device .owner .create_folder("sync_folder".to_string(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Our new local folder should have the single create vault event assert_eq!(1, num_events(&mut device.owner, new_folder.id()).await); diff --git a/crates/integration_tests/tests/network_account/send_folder_delete.rs b/crates/integration_tests/tests/network_account/send_folder_delete.rs index 89795facee..faf369c159 100644 --- a/crates/integration_tests/tests/network_account/send_folder_delete.rs +++ b/crates/integration_tests/tests/network_account/send_folder_delete.rs @@ -27,13 +27,13 @@ async fn network_sync_folder_delete() -> Result<()> { let FolderCreate { folder: new_folder, - sync_error, + sync_result, .. } = device .owner .create_folder("sync_delete_folder".to_string(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); let (secret_id, _, _, file_name) = create_file_secret(&mut device.owner, &new_folder, None).await?; @@ -42,9 +42,9 @@ async fn network_sync_folder_delete() -> Result<()> { assert_eq!(3, num_events(&mut device.owner, new_folder.id()).await); - let FolderDelete { sync_error, .. } = + let FolderDelete { sync_result, .. } = device.owner.delete_folder(&new_folder).await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); let updated_summaries: Vec = { let storage = device.owner.storage().await?; diff --git a/crates/integration_tests/tests/network_account/send_folder_import.rs b/crates/integration_tests/tests/network_account/send_folder_import.rs index 6565c9f4a7..3b60bc167c 100644 --- a/crates/integration_tests/tests/network_account/send_folder_import.rs +++ b/crates/integration_tests/tests/network_account/send_folder_import.rs @@ -23,13 +23,13 @@ async fn network_sync_folder_import() -> Result<()> { // with the default folder let FolderCreate { folder: new_folder, - sync_error, + sync_result, .. } = device .owner .create_folder("sync_folder".to_string(), Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Open the new folder so we can get a copy of the data, // using the same vault data ensures the same identifier diff --git a/crates/integration_tests/tests/network_account/send_folder_rename.rs b/crates/integration_tests/tests/network_account/send_folder_rename.rs index 5ee924a3f4..533638df9a 100644 --- a/crates/integration_tests/tests/network_account/send_folder_rename.rs +++ b/crates/integration_tests/tests/network_account/send_folder_rename.rs @@ -23,11 +23,11 @@ async fn network_sync_folder_rename() -> Result<()> { // Path that we expect the remote server to write to let server_path = server.account_path(device.owner.address()); - let FolderChange { sync_error, .. } = device + let FolderChange { sync_result, .. } = device .owner .rename_folder(&default_folder, "new_name".to_string()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Get the remote out of the owner so we can // assert on equality between local and remote diff --git a/crates/integration_tests/tests/network_account/send_secret_create.rs b/crates/integration_tests/tests/network_account/send_secret_create.rs index 26070ff251..ff2ae664ef 100644 --- a/crates/integration_tests/tests/network_account/send_secret_create.rs +++ b/crates/integration_tests/tests/network_account/send_secret_create.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests syncing create secret events between two /// clients. @@ -51,7 +51,7 @@ async fn network_sync_secret_create() -> Result<()> { assert_eq!(3, num_events(&mut device2.owner, &default_folder_id).await); // First client runs sync to pull down the additional secret - assert!(device1.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); // Everyone is equal assert_eq!(3, num_events(&mut device1.owner, &default_folder_id).await); diff --git a/crates/integration_tests/tests/network_account/send_secret_delete.rs b/crates/integration_tests/tests/network_account/send_secret_delete.rs index 75b2147aee..6f50f2fbcd 100644 --- a/crates/integration_tests/tests/network_account/send_secret_delete.rs +++ b/crates/integration_tests/tests/network_account/send_secret_delete.rs @@ -26,16 +26,16 @@ async fn network_sync_secret_delete() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Should have two events assert_eq!(2, num_events(&mut device.owner, &default_folder_id).await); - let SecretDelete { sync_error, .. } = device + let SecretDelete { sync_result, .. } = device .owner .delete_secret(&result.id, Default::default()) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Should have three events assert_eq!(3, num_events(&mut device.owner, &default_folder_id).await); diff --git a/crates/integration_tests/tests/network_account/send_secret_move.rs b/crates/integration_tests/tests/network_account/send_secret_move.rs index 503cfa98ae..92808bae00 100644 --- a/crates/integration_tests/tests/network_account/send_secret_move.rs +++ b/crates/integration_tests/tests/network_account/send_secret_move.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests syncing move secret events between two /// clients. @@ -40,8 +40,8 @@ async fn network_sync_secret_move() -> Result<()> { .await?; // Sync up both clients - assert!(device1.owner.sync().await.is_none()); - assert!(device2.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Move the secret device1 @@ -59,8 +59,8 @@ async fn network_sync_secret_move() -> Result<()> { assert_eq!(1, num_events(&mut device2.owner, destination.id()).await); // Sync up both clients - assert!(device1.owner.sync().await.is_none()); - assert!(device2.owner.sync().await.is_none()); + assert!(device1.owner.sync().await.first_error().is_none()); + assert!(device2.owner.sync().await.first_error().is_none()); // Folder is now up to date assert_eq!(2, num_events(&mut device1.owner, destination.id()).await); diff --git a/crates/integration_tests/tests/network_account/send_secret_update.rs b/crates/integration_tests/tests/network_account/send_secret_update.rs index 8b13a8531d..c3778b3e55 100644 --- a/crates/integration_tests/tests/network_account/send_secret_update.rs +++ b/crates/integration_tests/tests/network_account/send_secret_update.rs @@ -26,13 +26,13 @@ async fn network_sync_secret_update() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Should have two events assert_eq!(2, num_events(&mut device.owner, &default_folder_id).await); let (meta, secret) = mock::note("note", "secret1"); - let SecretChange { sync_error, .. } = device + let SecretChange { sync_result, .. } = device .owner .update_secret( &result.id, @@ -42,7 +42,7 @@ async fn network_sync_secret_update() -> Result<()> { None, ) .await?; - assert!(sync_error.is_none()); + assert!(sync_result.first_error().is_none()); // Should have three events assert_eq!(3, num_events(&mut device.owner, &default_folder_id).await); diff --git a/crates/integration_tests/tests/pairing/device_revoke.rs b/crates/integration_tests/tests/pairing/device_revoke.rs index 6247f8a00d..43044bef1e 100644 --- a/crates/integration_tests/tests/pairing/device_revoke.rs +++ b/crates/integration_tests/tests/pairing/device_revoke.rs @@ -4,7 +4,7 @@ use crate::test_utils::{ }; use anyhow::Result; use http::StatusCode; -use sos_net::{sdk::prelude::*, Error as ClientError, RemoteSync, SyncError}; +use sos_net::{sdk::prelude::*, AccountSync, Error as ClientError}; /// Tests pairing a new device and revoking trust in the device. #[tokio::test] @@ -40,7 +40,7 @@ async fn pairing_device_revoke() -> Result<()> { assert!(matches!(result, Err(ClientError::RevokeDeviceSelf))); // Sync on the original device to fetch the updated device logs - assert!(primary_device.owner.sync().await.is_none()); + assert!(primary_device.owner.sync().await.first_error().is_none()); // Primary device revokes access to the newly enrolled device // as if it were lost or stolen @@ -56,10 +56,9 @@ async fn pairing_device_revoke() -> Result<()> { // println!("{:#?}", revoke_error); - if let Err(ClientError::RevokeDeviceSync(mut e)) = revoke_error { - let (_, err) = e.errors.remove(0); + if let Err(ClientError::RevokeDeviceSync(err)) = revoke_error { assert!(matches!( - err, + &*err, ClientError::ResponseJson(StatusCode::FORBIDDEN, _) )); } else { @@ -68,9 +67,8 @@ async fn pairing_device_revoke() -> Result<()> { // Attempting to sync after the device was revoked // yields a forbidden response - let sync_error = enrolled_account.sync().await; - if let Some(SyncError { mut errors }) = sync_error { - let (_, err) = errors.remove(0); + let sync_result = enrolled_account.sync().await; + if let Some(err) = sync_result.first_error() { assert!(matches!( err, ClientError::ResponseJson(StatusCode::FORBIDDEN, _) diff --git a/crates/integration_tests/tests/pairing/pairing_account_name.rs b/crates/integration_tests/tests/pairing/pairing_account_name.rs index c656e69a17..89c609028c 100644 --- a/crates/integration_tests/tests/pairing/pairing_account_name.rs +++ b/crates/integration_tests/tests/pairing/pairing_account_name.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests the protocol for pairing devices respects /// an account name that has been changed. @@ -28,7 +28,7 @@ async fn pairing_account_name() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); let new_name = "pairing_account_name_updated"; primary_device @@ -43,7 +43,7 @@ async fn pairing_account_name() -> Result<()> { .unwrap(); // Sync on the original device to fetch the updated device logs - assert!(primary_device.owner.sync().await.is_none()); + assert!(primary_device.owner.sync().await.first_error().is_none()); // Read the secret on the newly enrolled account let (secret_data, _) = diff --git a/crates/integration_tests/tests/pairing/pairing_inverted.rs b/crates/integration_tests/tests/pairing/pairing_inverted.rs index ffd83a8141..05608c8250 100644 --- a/crates/integration_tests/tests/pairing/pairing_inverted.rs +++ b/crates/integration_tests/tests/pairing/pairing_inverted.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests the protocol for pairing devices using the inverted flow. #[tokio::test] @@ -27,14 +27,14 @@ async fn pairing_inverted() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Run the pairing protocol to completion. let mut enrolled_account = run_inverted_pairing_protocol(&mut primary_device, TEST_ID).await?; // Sync on the original device to fetch the updated device logs - assert!(primary_device.owner.sync().await.is_none()); + assert!(primary_device.owner.sync().await.first_error().is_none()); // Read the secret on the newly enrolled account let (secret_data, _) = diff --git a/crates/integration_tests/tests/pairing/pairing_protocol.rs b/crates/integration_tests/tests/pairing/pairing_protocol.rs index 1aaa81b2d8..00d3bed773 100644 --- a/crates/integration_tests/tests/pairing/pairing_protocol.rs +++ b/crates/integration_tests/tests/pairing/pairing_protocol.rs @@ -3,7 +3,7 @@ use crate::test_utils::{ simulate_device, spawn, teardown, }; use anyhow::Result; -use sos_net::{sdk::prelude::*, RemoteSync}; +use sos_net::{sdk::prelude::*, AccountSync}; /// Tests the protocol for pairing devices. #[tokio::test] @@ -27,7 +27,7 @@ async fn pairing_protocol() -> Result<()> { .owner .create_secret(meta, secret, Default::default()) .await?; - assert!(result.sync_error.is_none()); + assert!(result.sync_result.first_error().is_none()); // Run the pairing protocol to completion. let mut enrolled_account = @@ -36,7 +36,7 @@ async fn pairing_protocol() -> Result<()> { .unwrap(); // Sync on the original device to fetch the updated device logs - assert!(primary_device.owner.sync().await.is_none()); + assert!(primary_device.owner.sync().await.first_error().is_none()); // Read the secret on the newly enrolled account let (secret_data, _) = diff --git a/crates/net/Cargo.toml b/crates/net/Cargo.toml index 8777bb354e..7d581c793f 100644 --- a/crates/net/Cargo.toml +++ b/crates/net/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sos-net" -version = "0.14.11" +version = "0.15.0" edition = "2021" description = "Networking library for the Save Our Secrets SDK." homepage = "https://saveoursecrets.com" @@ -79,16 +79,16 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync"] } tokio-tungstenite = { version = "0.23", features = ["rustls-tls-webpki-roots"] , optional = true} [dependencies.sos-sdk] -version = "0.14" +version = "0.15" path = "../sdk" [dependencies.sos-protocol] -version = "0.14" +version = "0.15" path = "../protocol" features = ["account"] [dependencies.sos-account-extras] -version = "0.14" +version = "0.15" path = "../account_extras/" optional = true diff --git a/crates/net/src/account/auto_merge.rs b/crates/net/src/account/auto_merge.rs index 50ee83eda5..89d44be38a 100644 --- a/crates/net/src/account/auto_merge.rs +++ b/crates/net/src/account/auto_merge.rs @@ -122,7 +122,7 @@ impl RemoteBridge { conflict: MaybeConflict, local: SyncStatus, _remote: SyncStatus, - ) -> Result<()> { + ) -> Result { let mut force_merge_outcome = MergeOutcome::default(); let mut has_hard_conflict = false; @@ -175,7 +175,7 @@ impl RemoteBridge { account.sign_out().await?; } - Ok(()) + Ok(force_merge_outcome) } auto_merge_impl!( diff --git a/crates/net/src/account/listen.rs b/crates/net/src/account/listen.rs index e935328ea0..c72b8f2c16 100644 --- a/crates/net/src/account/listen.rs +++ b/crates/net/src/account/listen.rs @@ -1,9 +1,9 @@ //! Adds functions for listening to change notifications using //! a websocket connection. use crate::{ - protocol::{ChangeNotification, Origin, SyncError, SyncStorage}, + protocol::{ChangeNotification, Origin, SyncStorage}, sync::RemoteSync, - Error, ListenOptions, NetworkAccount, Result, + Error, ListenOptions, NetworkAccount, RemoteResult, Result, }; use std::sync::Arc; use tokio::sync::mpsc; @@ -37,9 +37,7 @@ impl NetworkAccount { &self, origin: &Origin, options: ListenOptions, - listener: Option< - mpsc::Sender<(ChangeNotification, Option>)>, - >, + listener: Option>, ) -> Result<()> { let remotes = self.remotes.read().await; if let Some(remote) = remotes.get(origin) { @@ -79,8 +77,8 @@ impl NetworkAccount { let _ = sync_lock.lock().await; // Sync with the remote that notified us - let sync_error = sync_remote.sync().await; - if let Some(e) = &sync_error { + let sync_result = sync_remote.sync().await; + if let Err(e) = &sync_result.result { tracing::error!( error = ?e, "listen_sync", @@ -91,7 +89,7 @@ impl NetworkAccount { // change notification and a possible sync error let tx = listener.clone(); if let Some(tx) = tx { - let _ = tx.send((message, sync_error)).await; + let _ = tx.send((message, sync_result)).await; } } else { tracing::debug!( diff --git a/crates/net/src/account/network_account.rs b/crates/net/src/account/network_account.rs index 6178431725..ff627c0bd8 100644 --- a/crates/net/src/account/network_account.rs +++ b/crates/net/src/account/network_account.rs @@ -1,6 +1,6 @@ //! Network aware account. use crate::{ - protocol::{Origin, SyncError, SyncOptions, UpdateSet}, + protocol::{Origin, SyncOptions, UpdateSet}, sdk::{ account::{ Account, AccountBuilder, AccountChange, AccountData, @@ -26,7 +26,7 @@ use crate::{ }, vfs, Paths, }, - SyncClient, + SyncClient, SyncResult, }; use async_trait::async_trait; use secrecy::SecretString; @@ -70,7 +70,7 @@ use crate::sdk::account::security_report::{ use crate::sdk::migrate::import::ImportTarget; use super::remote::Remotes; -use crate::{Error, RemoteBridge, RemoteSync, Result}; +use crate::{AccountSync, Error, RemoteBridge, RemoteSync, Result}; #[cfg(feature = "files")] use crate::{ @@ -203,9 +203,9 @@ impl NetworkAccount { } // Send the device event logs to the remote servers - if let Some(e) = self.sync().await { + if let Some(e) = self.sync().await.first_error() { tracing::error!(error = ?e); - return Err(Error::RevokeDeviceSync(e)); + return Err(Error::RevokeDeviceSync(Box::new(e))); } Ok(()) @@ -263,7 +263,7 @@ impl NetworkAccount { pub async fn add_server( &mut self, origin: Origin, - ) -> Result>> { + ) -> Result> { let remote = self.remote_bridge(&origin).await?; #[cfg(feature = "files")] @@ -289,7 +289,11 @@ impl NetworkAccount { origins: vec![origin.clone()], ..Default::default() }; - sync_error = remote.sync_with_options(&options).await; + if let Err(err) = + remote.sync_with_options(&options).await.result + { + sync_error = Some(err); + } } } @@ -636,7 +640,7 @@ impl NetworkAccount { impl Account for NetworkAccount { type Account = NetworkAccount; type Error = Error; - type NetworkError = SyncError; + type NetworkResult = SyncResult; fn address(&self) -> &Address { &self.address @@ -705,7 +709,7 @@ impl Account for NetworkAccount { &mut self, folder: &Summary, description: impl AsRef + Send + Sync, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { let mut account = self.account.lock().await; @@ -715,7 +719,7 @@ impl Account for NetworkAccount { let result = FolderChange { event: result.event, commit_state: result.commit_state, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) @@ -787,16 +791,17 @@ impl Account for NetworkAccount { ..Default::default() }; - let sync_error = self.force_update(updates, &sync_options).await; - if let Some(sync_error) = sync_error { - return Err(Error::ForceUpdate(sync_error)); + let sync_result = self.force_update(updates, &sync_options).await; + if let Some(sync_error) = sync_result.first_error() { + return Err(Error::ForceUpdate(Box::new(sync_error))); } // In case we have pending updates to the account, device // or file event logs - if let Some(sync_error) = self.sync_with_options(&sync_options).await + if let Some(sync_error) = + self.sync_with_options(&sync_options).await.first_error() { - return Err(Error::ForceUpdate(sync_error)); + return Err(Error::ForceUpdate(Box::new(sync_error))); } Ok(conversion) @@ -822,16 +827,17 @@ impl Account for NetworkAccount { ..Default::default() }; - let sync_error = self.force_update(updates, &sync_options).await; - if let Some(sync_error) = sync_error { - return Err(Error::ForceUpdate(sync_error)); + let sync_result = self.force_update(updates, &sync_options).await; + if let Some(sync_error) = sync_result.first_error() { + return Err(Error::ForceUpdate(Box::new(sync_error))); } // In case we have pending updates to the account, device // or file event logs - if let Some(sync_error) = self.sync_with_options(&sync_options).await + if let Some(sync_error) = + self.sync_with_options(&sync_options).await.first_error() { - return Err(Error::ForceUpdate(sync_error)); + return Err(Error::ForceUpdate(Box::new(sync_error))); } Ok(()) @@ -882,7 +888,7 @@ impl Account for NetworkAccount { async fn rename_account( &mut self, account_name: String, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { let mut account = self.account.lock().await; @@ -892,7 +898,7 @@ impl Account for NetworkAccount { let result = AccountChange { event: result.event, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) @@ -1001,16 +1007,17 @@ impl Account for NetworkAccount { ..Default::default() }; - let sync_error = self.force_update(updates, &sync_options).await; - if let Some(sync_error) = sync_error { - return Err(Error::ForceUpdate(sync_error)); + let sync_result = self.force_update(updates, &sync_options).await; + if let Some(sync_error) = sync_result.first_error() { + return Err(Error::ForceUpdate(Box::new(sync_error))); } // In case we have pending updates to the account, device // or file event logs - if let Some(sync_error) = self.sync_with_options(&sync_options).await + if let Some(sync_error) = + self.sync_with_options(&sync_options).await.first_error() { - return Err(Error::ForceUpdate(sync_error)); + return Err(Error::ForceUpdate(Box::new(sync_error))); } Ok(result) @@ -1044,17 +1051,17 @@ impl Account for NetworkAccount { ..Default::default() }; - let sync_error = self.force_update(updates, &sync_options).await; - if let Some(sync_error) = sync_error { - return Err(Error::ForceUpdate(sync_error)); + let sync_result = self.force_update(updates, &sync_options).await; + if let Some(sync_error) = sync_result.first_error() { + return Err(Error::ForceUpdate(Box::new(sync_error))); } // In case we have pending updates to the account, device // or file event logs if let Some(sync_error) = - self.sync_with_options(&sync_options).await + self.sync_with_options(&sync_options).await.first_error() { - return Err(Error::ForceUpdate(sync_error)); + return Err(Error::ForceUpdate(Box::new(sync_error))); } } @@ -1105,17 +1112,17 @@ impl Account for NetworkAccount { ..Default::default() }; - let sync_error = self.force_update(updates, &sync_options).await; - if let Some(sync_error) = sync_error { - return Err(Error::ForceUpdate(sync_error)); + let sync_result = self.force_update(updates, &sync_options).await; + if let Some(sync_error) = sync_result.first_error() { + return Err(Error::ForceUpdate(Box::new(sync_error))); } // In case we have pending updates to the account, device // or file event logs if let Some(sync_error) = - self.sync_with_options(&sync_options).await + self.sync_with_options(&sync_options).await.first_error() { - return Err(Error::ForceUpdate(sync_error)); + return Err(Error::ForceUpdate(Box::new(sync_error))); } } @@ -1207,7 +1214,7 @@ impl Account for NetworkAccount { meta: SecretMeta, secret: Secret, options: AccessOptions, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1221,7 +1228,7 @@ impl Account for NetworkAccount { event: result.event, commit_state: result.commit_state, folder: result.folder, - sync_error: self.sync().await, + sync_result: self.sync().await, #[cfg(feature = "files")] file_events: result.file_events, }; @@ -1235,7 +1242,7 @@ impl Account for NetworkAccount { async fn insert_secrets( &mut self, secrets: Vec<(SecretMeta, Secret)>, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1258,13 +1265,13 @@ impl Account for NetworkAccount { event: result.event, commit_state: result.commit_state, folder: result.folder, - sync_error: None, + sync_result: Default::default(), #[cfg(feature = "files")] file_events: result.file_events, } }) .collect(), - sync_error: self.sync().await, + sync_result: self.sync().await, }; #[cfg(feature = "files")] @@ -1280,7 +1287,7 @@ impl Account for NetworkAccount { secret: Option, options: AccessOptions, destination: Option<&Summary>, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1296,7 +1303,7 @@ impl Account for NetworkAccount { event: result.event, commit_state: result.commit_state, folder: result.folder, - sync_error: self.sync().await, + sync_result: self.sync().await, #[cfg(feature = "files")] file_events: result.file_events, }; @@ -1313,7 +1320,7 @@ impl Account for NetworkAccount { from: &Summary, to: &Summary, options: AccessOptions, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1324,7 +1331,7 @@ impl Account for NetworkAccount { let result = SecretMove { id: result.id, event: result.event, - sync_error: self.sync().await, + sync_result: self.sync().await, #[cfg(feature = "files")] file_events: result.file_events, }; @@ -1348,7 +1355,7 @@ impl Account for NetworkAccount { &mut self, secret_id: &SecretId, options: AccessOptions, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1360,7 +1367,7 @@ impl Account for NetworkAccount { event: result.event, commit_state: result.commit_state, folder: result.folder, - sync_error: self.sync().await, + sync_result: self.sync().await, #[cfg(feature = "files")] file_events: result.file_events, }; @@ -1376,7 +1383,7 @@ impl Account for NetworkAccount { from: &Summary, secret_id: &SecretId, options: AccessOptions, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { let mut account = self.account.lock().await; @@ -1386,7 +1393,7 @@ impl Account for NetworkAccount { let result = SecretMove { id: result.id, event: result.event, - sync_error: self.sync().await, + sync_result: self.sync().await, #[cfg(feature = "files")] file_events: result.file_events, }; @@ -1402,7 +1409,7 @@ impl Account for NetworkAccount { secret_id: &SecretId, secret_meta: &SecretMeta, options: AccessOptions, - ) -> Result<(SecretMove, Summary)> { + ) -> Result<(SecretMove, Summary)> { let _ = self.sync_lock.lock().await; let (result, to) = { @@ -1413,7 +1420,7 @@ impl Account for NetworkAccount { let result = SecretMove { id: result.id, event: result.event, - sync_error: self.sync().await, + sync_result: self.sync().await, #[cfg(feature = "files")] file_events: result.file_events, }; @@ -1432,7 +1439,7 @@ impl Account for NetworkAccount { path: impl AsRef + Send + Sync, options: AccessOptions, destination: Option<&Summary>, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1448,7 +1455,7 @@ impl Account for NetworkAccount { event: result.event, commit_state: result.commit_state, folder: result.folder, - sync_error: self.sync().await, + sync_result: self.sync().await, #[cfg(feature = "files")] file_events: result.file_events, }; @@ -1463,7 +1470,7 @@ impl Account for NetworkAccount { &mut self, name: String, options: NewFolderOptions, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { let mut account = self.account.lock().await; @@ -1474,7 +1481,7 @@ impl Account for NetworkAccount { folder: result.folder, event: result.event, commit_state: result.commit_state, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) @@ -1484,7 +1491,7 @@ impl Account for NetworkAccount { &mut self, summary: &Summary, name: String, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { let mut account = self.account.lock().await; @@ -1494,7 +1501,7 @@ impl Account for NetworkAccount { let result = FolderChange { event: result.event, commit_state: result.commit_state, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) @@ -1504,7 +1511,7 @@ impl Account for NetworkAccount { &mut self, summary: &Summary, flags: VaultFlags, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { let mut account = self.account.lock().await; @@ -1514,7 +1521,7 @@ impl Account for NetworkAccount { let result = FolderChange { event: result.event, commit_state: result.commit_state, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) @@ -1525,7 +1532,7 @@ impl Account for NetworkAccount { path: impl AsRef + Send + Sync, key: AccessKey, overwrite: bool, - ) -> Result> { + ) -> Result> { let buffer = vfs::read(path.as_ref()).await?; self.import_folder_buffer(&buffer, key, overwrite).await } @@ -1543,7 +1550,7 @@ impl Account for NetworkAccount { buffer: impl AsRef<[u8]> + Send + Sync, key: AccessKey, overwrite: bool, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1555,7 +1562,7 @@ impl Account for NetworkAccount { folder: result.folder, event: result.event, commit_state: result.commit_state, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) @@ -1589,7 +1596,7 @@ impl Account for NetworkAccount { async fn delete_folder( &mut self, summary: &Summary, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { let mut account = self.account.lock().await; @@ -1599,7 +1606,7 @@ impl Account for NetworkAccount { let result = FolderDelete { events: result.events, commit_state: result.commit_state, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) @@ -1673,7 +1680,7 @@ impl Account for NetworkAccount { async fn import_file( &mut self, target: ImportTarget, - ) -> Result> { + ) -> Result> { let _ = self.sync_lock.lock().await; let result = { @@ -1685,7 +1692,7 @@ impl Account for NetworkAccount { folder: result.folder, event: result.event, commit_state: result.commit_state, - sync_error: self.sync().await, + sync_result: self.sync().await, }; Ok(result) diff --git a/crates/net/src/account/remote.rs b/crates/net/src/account/remote.rs index ee57e7e085..8cd8cbee69 100644 --- a/crates/net/src/account/remote.rs +++ b/crates/net/src/account/remote.rs @@ -11,7 +11,7 @@ use crate::{ storage::StorageEventLogs, vfs, }, - Error, RemoteSync, Result, SyncClient, SyncError, + Error, RemoteResult, RemoteSync, Result, SyncClient, }; use async_trait::async_trait; use std::{collections::HashMap, sync::Arc}; @@ -86,7 +86,10 @@ impl RemoteBridge { Ok(()) } - async fn sync_account(&self, remote_status: SyncStatus) -> Result<()> { + async fn sync_account( + &self, + remote_status: SyncStatus, + ) -> Result { let mut account = self.account.lock().await; tracing::debug!("merge_client"); @@ -96,6 +99,8 @@ impl RemoteBridge { tracing::debug!(needs_sync = %needs_sync, "merge_client"); + let mut outcome = MergeOutcome::default(); + if needs_sync { let packet = SyncPacket { status: local_status, @@ -111,8 +116,6 @@ impl RemoteBridge { .unwrap_or_default(); let has_conflicts = maybe_conflict.has_conflicts(); - let mut outcome = MergeOutcome::default(); - if !has_conflicts { account.merge(remote_changes.diff, &mut outcome).await?; @@ -201,29 +204,35 @@ impl RemoteBridge { } } - Ok(()) + Ok(outcome) } - async fn execute_sync(&self, options: &SyncOptions) -> Result<()> { + async fn execute_sync( + &self, + options: &SyncOptions, + ) -> Result> { let exists = self.client.account_exists().await?; if exists { let sync_status = self.client.sync_status().await?; match self.sync_account(sync_status).await { - Ok(_) => Ok(()), + Ok(outcome) => Ok(Some(outcome)), Err(e) => match e { Error::SoftConflict { conflict, local, remote, } => { - self.auto_merge(options, conflict, local, remote) - .await + let outcome = self + .auto_merge(options, conflict, local, remote) + .await?; + Ok(Some(outcome)) } _ => Err(e), }, } } else { - self.create_remote_account().await + self.create_remote_account().await?; + Ok(None) } } @@ -256,73 +265,47 @@ impl RemoteBridge { #[async_trait] impl RemoteSync for RemoteBridge { - async fn sync(&self) -> Option { + async fn sync(&self) -> RemoteResult { self.sync_with_options(&Default::default()).await } - async fn sync_with_options( - &self, - options: &SyncOptions, - ) -> Option { - let should_sync = options.origins.is_empty() - || options - .origins - .iter() - .find(|&o| o == &self.origin) - .is_some(); - - if !should_sync { - tracing::warn!(origin = %self.origin, "skip sync"); - return None; - } - - tracing::debug!(origin = %self.origin.url()); - + async fn sync_with_options(&self, options: &SyncOptions) -> RemoteResult { match self.execute_sync(options).await { - Ok(_) => None, - Err(e) => Some(SyncError { - errors: vec![(self.origin.clone(), e)], - }), + Ok(outcome) => RemoteResult { + origin: self.origin.clone(), + result: Ok(outcome), + }, + Err(e) => RemoteResult { + origin: self.origin.clone(), + result: Err(e), + }, } } #[cfg(feature = "files")] - async fn sync_file_transfers( - &self, - options: &SyncOptions, - ) -> Option { - let should_sync = options.origins.is_empty() - || options - .origins - .iter() - .find(|&o| o == &self.origin) - .is_some(); - - if !should_sync { - tracing::warn!(origin = %self.origin, "skip sync"); - return None; - } - - tracing::debug!(origin = %self.origin.url()); - + async fn sync_file_transfers(&self) -> RemoteResult { match self.execute_sync_file_transfers().await { - Ok(_) => None, - Err(e) => Some(SyncError { - errors: vec![(self.origin.clone(), e)], - }), + Ok(_) => RemoteResult { + origin: self.origin.clone(), + result: Ok(None), + }, + Err(e) => RemoteResult { + origin: self.origin.clone(), + result: Err(e), + }, } } - async fn force_update( - &self, - account_data: UpdateSet, - _options: &SyncOptions, - ) -> Option { + async fn force_update(&self, account_data: UpdateSet) -> RemoteResult { match self.client.update_account(account_data).await { - Ok(_) => None, - Err(e) => Some(SyncError { - errors: vec![(self.origin.clone(), e)], - }), + Ok(_) => RemoteResult { + origin: self.origin.clone(), + result: Ok(None), + }, + Err(e) => RemoteResult { + origin: self.origin.clone(), + result: Err(e), + }, } } } diff --git a/crates/net/src/account/sync.rs b/crates/net/src/account/sync.rs index a7bf5cbcfe..4fcce4daf4 100644 --- a/crates/net/src/account/sync.rs +++ b/crates/net/src/account/sync.rs @@ -7,7 +7,7 @@ use crate::{ vault::{Summary, VaultId}, Result, }, - NetworkAccount, RemoteSync, SyncClient, SyncError, + AccountSync, NetworkAccount, RemoteSync, SyncClient, SyncResult, }; use async_trait::async_trait; use indexmap::IndexSet; @@ -88,94 +88,92 @@ impl NetworkAccount { } #[async_trait] -impl RemoteSync for NetworkAccount { - async fn sync(&self) -> Option { +impl AccountSync for NetworkAccount { + async fn sync(&self) -> SyncResult { self.sync_with_options(&Default::default()).await } - async fn sync_with_options( - &self, - options: &SyncOptions, - ) -> Option { + async fn sync_with_options(&self, options: &SyncOptions) -> SyncResult { + let mut result = SyncResult::default(); if self.offline { tracing::warn!("offline mode active, ignoring sync"); - return None; + return result; } let _ = self.sync_lock.lock().await; - let mut maybe_error: SyncError = Default::default(); let remotes = self.remotes.read().await; for (origin, remote) in &*remotes { let sync_remote = options.origins.is_empty() || options.origins.contains(origin); - if sync_remote { - if let Some(mut e) = remote.sync_with_options(options).await { - maybe_error.errors.append(&mut e.errors); - } + if !sync_remote { + tracing::warn!(origin = %origin, "skip_sync::sync_with_options"); + continue; } + + let remote_result = remote.sync_with_options(options).await; + result.remotes.push(remote_result); } - maybe_error.into_option() + result } #[cfg(feature = "files")] - async fn sync_file_transfers( - &self, - options: &SyncOptions, - ) -> Option { + async fn sync_file_transfers(&self, options: &SyncOptions) -> SyncResult { + let mut result = SyncResult::default(); if self.offline { tracing::warn!( "offline mode active, ignoring sync file transfers" ); - return None; + return result; } let _ = self.sync_lock.lock().await; - let mut maybe_error: SyncError = Default::default(); let remotes = self.remotes.read().await; for (origin, remote) in &*remotes { let sync_remote = options.origins.is_empty() || options.origins.contains(origin); - if sync_remote { - if let Some(mut e) = remote.sync_file_transfers(options).await - { - maybe_error.errors.append(&mut e.errors); - } + if !sync_remote { + tracing::warn!(origin = %origin, "skip_sync::sync_file_transfers"); + continue; } + + let remote_result = remote.sync_file_transfers().await; + result.remotes.push(remote_result); } - maybe_error.into_option() + result } async fn force_update( &self, account_data: UpdateSet, options: &SyncOptions, - ) -> Option { + ) -> SyncResult { + let mut result = SyncResult::default(); if self.offline { tracing::warn!("offline mode active, ignoring force update"); - return None; + return result; } let _ = self.sync_lock.lock().await; - let mut maybe_error: SyncError = Default::default(); let remotes = self.remotes.read().await; for (origin, remote) in &*remotes { let sync_remote = options.origins.is_empty() || options.origins.contains(origin); - if sync_remote { - if let Some(mut e) = - remote.force_update(account_data.clone(), options).await - { - maybe_error.errors.append(&mut e.errors); - } + if !sync_remote { + tracing::warn!(origin = %origin, "skip_sync::force_update"); + continue; } + + let remote_result = + remote.force_update(account_data.clone()).await; + result.remotes.push(remote_result); } - maybe_error.into_option() + result } } diff --git a/crates/net/src/error.rs b/crates/net/src/error.rs index 367124afe8..7d1ef3675b 100644 --- a/crates/net/src/error.rs +++ b/crates/net/src/error.rs @@ -2,7 +2,7 @@ use crate::CancelReason; use http::StatusCode; use serde_json::Value; -use sos_protocol::{MaybeConflict, Origin, SyncError, SyncStatus}; +use sos_protocol::{MaybeConflict, Origin, SyncStatus}; use sos_sdk::vault::VaultId; use std::error::Error as StdError; use std::path::PathBuf; @@ -58,11 +58,11 @@ pub enum Error { /// Error generated when failing to sync after revoking a device. #[error("failed to sync after revoking device, {0}")] - RevokeDeviceSync(SyncError), + RevokeDeviceSync(Box), /// Error generated force update of an account failed. #[error("failed to force update, {0}")] - ForceUpdate(SyncError), + ForceUpdate(Box), /// Error generated trying to parse a device enrollment sharing URL. #[deprecated] diff --git a/crates/net/src/lib.rs b/crates/net/src/lib.rs index 02a947821d..ac21fe93c3 100644 --- a/crates/net/src/lib.rs +++ b/crates/net/src/lib.rs @@ -28,7 +28,9 @@ pub use error::Error; #[cfg(feature = "listen")] pub use net::{changes, connect, ListenOptions, WebSocketHandle}; pub use net::{HttpClient, NetworkRetry}; -pub use sync::{RemoteSync, SyncClient, SyncError}; +pub use sync::{ + AccountSync, RemoteResult, RemoteSync, SyncClient, SyncResult, +}; #[cfg(any( feature = "preferences", diff --git a/crates/net/src/pairing/error.rs b/crates/net/src/pairing/error.rs index e0d857874f..e1efc7d8e1 100644 --- a/crates/net/src/pairing/error.rs +++ b/crates/net/src/pairing/error.rs @@ -1,5 +1,4 @@ //! Error type for the pairing module. -use crate::protocol::SyncError; use thiserror::Error; /// Errors generated by the pairing library. @@ -16,7 +15,7 @@ pub enum Error { /// Error generated failing to sync devices patch. #[error("failed to sync devices: {0}")] - DevicePatchSync(SyncError), + DevicePatchSync(Box), /// Error generated trying to access device enrollment /// before pairing protocol completion. @@ -36,7 +35,7 @@ pub enum Error { /// Error generated when failing to sync after completing /// device enrollment. #[error("could not sync after device enrollment authentication: '{0}'")] - EnrollSync(SyncError), + EnrollSync(Box), /// Error generated when the protocol is in the wrong state /// or a packet payload is not of the expected type. diff --git a/crates/net/src/pairing/websocket.rs b/crates/net/src/pairing/websocket.rs index 90ecb91dfc..82d7d1aca7 100644 --- a/crates/net/src/pairing/websocket.rs +++ b/crates/net/src/pairing/websocket.rs @@ -13,7 +13,7 @@ use crate::{ signer::ecdsa::SingleParty, url::Url, }, - sync::RemoteSync, + sync::AccountSync, NetworkAccount, WebSocketRequest, }; use futures::{ @@ -459,9 +459,9 @@ impl<'a> OfferPairing<'a> { ..Default::default() }; if let Some(sync_error) = - self.account.sync_with_options(&options).await + self.account.sync_with_options(&options).await.first_error() { - return Err(Error::DevicePatchSync(sync_error)); + return Err(Error::DevicePatchSync(Box::new(sync_error))); } // Creating a new device vault saves the folder password @@ -470,9 +470,9 @@ impl<'a> OfferPairing<'a> { // fetch data that includes the password for the device // vault we will send if let Some(sync_error) = - self.account.sync_with_options(&options).await + self.account.sync_with_options(&options).await.first_error() { - return Err(Error::EnrollSync(sync_error)); + return Err(Error::EnrollSync(Box::new(sync_error))); } Ok(()) diff --git a/crates/net/src/sync.rs b/crates/net/src/sync.rs index bcb4b6d5ca..3bc1267c68 100644 --- a/crates/net/src/sync.rs +++ b/crates/net/src/sync.rs @@ -1,9 +1,8 @@ -use super::Error; use crate::{ protocol::{ - CreateSet, DiffRequest, DiffResponse, Origin, PatchRequest, - PatchResponse, ScanRequest, ScanResponse, SyncOptions, SyncPacket, - SyncStatus, UpdateSet, + CreateSet, DiffRequest, DiffResponse, MergeOutcome, Origin, + PatchRequest, PatchResponse, ScanRequest, ScanResponse, SyncOptions, + SyncPacket, SyncStatus, UpdateSet, }, CancelReason, Result, }; @@ -11,10 +10,52 @@ use async_trait::async_trait; use sos_sdk::storage; use std::path::Path; -/// Error type that can be returned from a sync operation. -pub type SyncError = crate::protocol::SyncError; +/// Result of a sync operation with a single remote. +#[derive(Debug)] +pub struct RemoteResult { + /// Origin of the remote. + pub origin: Origin, + /// Result of the sync operation. + pub result: Result>, +} + +/// Result of a sync operation. +#[derive(Debug, Default)] +pub struct SyncResult { + /// Result of syncing with remote servers. + pub remotes: Vec, +} + +impl SyncResult { + /// Find the first sync error. + pub fn first_error(self) -> Option { + self.remotes.into_iter().find_map(|res| { + if res.result.is_err() { + res.result.err() + } else { + None + } + }) + } + + /// Find the first sync error by reference. + pub fn first_error_ref(&self) -> Option<&crate::Error> { + self.remotes.iter().find_map(|res| { + if let Err(e) = &res.result { + Some(e) + } else { + None + } + }) + } + + /// Determine if the sync has one or more errors. + pub fn has_error(&self) -> bool { + self.remotes.iter().any(|r| r.result.is_err()) + } +} -/// Trait for types that can sync accounts with a remote. +/// Trait for types that can sync with a single remote. #[async_trait] pub trait RemoteSync { /// Perform a full sync of the account using @@ -24,16 +65,13 @@ pub trait RemoteSync { /// server the account will be created and /// [RemoteSync::sync_file_transfers] will be called /// to ensure the transfers queue is synced. - async fn sync(&self) -> Option; + async fn sync(&self) -> RemoteResult; /// Perform a full sync of the account /// using the given options. /// /// See the documentation for [RemoteSync::sync] for more details. - async fn sync_with_options( - &self, - options: &SyncOptions, - ) -> Option; + async fn sync_with_options(&self, options: &SyncOptions) -> RemoteResult; /// Sync file transfers. /// @@ -41,10 +79,42 @@ pub trait RemoteSync { /// uploads or downloads by comparing the local file /// state with the file state on remote server(s). #[cfg(feature = "files")] - async fn sync_file_transfers( - &self, - options: &SyncOptions, - ) -> Option; + async fn sync_file_transfers(&self) -> RemoteResult; + + /// Force update an account on remote servers. + /// + /// Should be called after making destructive + /// changes to an account's folders. For example, if + /// the encryption cipher has been changed, a folder + /// password was changed or folder(s) were compacted. + async fn force_update(&self, account_data: UpdateSet) -> RemoteResult; +} + +/// Trait for types that can sync with multiple remotes. +#[async_trait] +pub trait AccountSync { + /// Perform a full sync of the account using + /// the default options. + /// + /// If the account does not exist on the remote + /// server the account will be created and + /// [RemoteSync::sync_file_transfers] will be called + /// to ensure the transfers queue is synced. + async fn sync(&self) -> SyncResult; + + /// Perform a full sync of the account + /// using the given options. + /// + /// See the documentation for [RemoteSync::sync] for more details. + async fn sync_with_options(&self, options: &SyncOptions) -> SyncResult; + + /// Sync file transfers. + /// + /// Updates the file transfers queue with any pending + /// uploads or downloads by comparing the local file + /// state with the file state on remote server(s). + #[cfg(feature = "files")] + async fn sync_file_transfers(&self, options: &SyncOptions) -> SyncResult; /// Force update an account on remote servers. /// @@ -56,7 +126,7 @@ pub trait RemoteSync { &self, account_data: UpdateSet, options: &SyncOptions, - ) -> Option; + ) -> SyncResult; } /// Client that can synchronize with a remote server. diff --git a/crates/protocol/Cargo.toml b/crates/protocol/Cargo.toml index ac33571c0e..df277bbf8c 100644 --- a/crates/protocol/Cargo.toml +++ b/crates/protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sos-protocol" -version = "0.14.11" +version = "0.15.0" edition = "2021" description = "Networking and sync protocol types for the Save Our Secrets SDK." homepage = "https://saveoursecrets.com" @@ -33,7 +33,7 @@ prost.workspace = true tokio = { version = "1", features = ["rt", "macros"] } [dependencies.sos-sdk] -version = "0.14" +version = "0.15" path = "../sdk" [dev-dependencies] diff --git a/crates/protocol/src/sync/primitives.rs b/crates/protocol/src/sync/primitives.rs index f31baf2583..c3eaefa92c 100644 --- a/crates/protocol/src/sync/primitives.rs +++ b/crates/protocol/src/sync/primitives.rs @@ -14,10 +14,7 @@ use crate::{ }; use async_trait::async_trait; use indexmap::IndexMap; -use std::{ - collections::{HashMap, HashSet}, - fmt, -}; +use std::collections::{HashMap, HashSet}; use crate::sdk::events::DeviceDiff; @@ -41,41 +38,6 @@ pub struct SyncOptions { pub hard_conflict_resolver: HardConflictResolver, } -/// Error type that can be returned from a sync operation. -#[derive(Debug)] -pub struct SyncError { - /// Errors generated during a sync operation. - pub errors: Vec<(Origin, T)>, -} - -impl std::error::Error for SyncError {} - -impl fmt::Display for SyncError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for (_, e) in self.errors.iter() { - write!(f, "{}", e)?; - } - Ok(()) - } -} - -impl SyncError { - /// Convert to an option. - pub fn into_option(self) -> Option { - if self.errors.is_empty() { - None - } else { - Some(self) - } - } -} - -impl Default for SyncError { - fn default() -> Self { - Self { errors: Vec::new() } - } -} - /// Options for folder merge. pub(crate) enum FolderMergeOptions<'a> { /// Update a URN lookup when merging. diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index 12dfd2c59b..e591031b32 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sos-sdk" -version = "0.14.11" +version = "0.15.0" edition = "2021" description = "Distributed, encrypted database for private secrets." homepage = "https://saveoursecrets.com" diff --git a/crates/sdk/src/account/account.rs b/crates/sdk/src/account/account.rs index 9801522a22..efe5d2eeae 100644 --- a/crates/sdk/src/account/account.rs +++ b/crates/sdk/src/account/account.rs @@ -95,15 +95,15 @@ pub struct SigninOptions { } /// Result information for a change to an account. -pub struct AccountChange { +pub struct AccountChange { /// Event to be logged. pub event: Event, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, } /// Result information for a created or updated secret. -pub struct SecretChange { +pub struct SecretChange { /// Secret identifier. pub id: SecretId, /// Event to be logged. @@ -113,36 +113,36 @@ pub struct SecretChange { pub commit_state: CommitState, /// Folder containing the secret. pub folder: Summary, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, /// File mutation events. #[cfg(feature = "files")] pub file_events: Vec, } /// Result information for a bulk insert. -pub struct SecretInsert { +pub struct SecretInsert { /// Created secrets. pub results: Vec>, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, } /// Result information for a secret move event. -pub struct SecretMove { +pub struct SecretMove { /// Secret identifier. pub id: SecretId, /// Event to be logged. pub event: Event, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, /// File mutation events. #[cfg(feature = "files")] pub file_events: Vec, } /// Result information for a deleted secret. -pub struct SecretDelete { +pub struct SecretDelete { /// Event to be logged. pub event: Event, /// Commit state of the folder event log before @@ -150,43 +150,43 @@ pub struct SecretDelete { pub commit_state: CommitState, /// Folder the secret was deleted from. pub folder: Summary, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, /// File mutation events. #[cfg(feature = "files")] pub file_events: Vec, } /// Result information for folder creation. -pub struct FolderCreate { +pub struct FolderCreate { /// Created folder. pub folder: Summary, /// Event to be logged. pub event: Event, /// Commit state of the new folder. pub commit_state: CommitState, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, } /// Result information for changes to a folder's attributes. -pub struct FolderChange { +pub struct FolderChange { /// Event to be logged. pub event: Event, /// Commit state before the change. pub commit_state: CommitState, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, } /// Result information for folder deletion. -pub struct FolderDelete { +pub struct FolderDelete { /// Events to be logged. pub events: Vec, /// Commit state of the folder. pub commit_state: CommitState, - /// Error generated during a sync. - pub sync_error: Option, + /// Result generated during a sync. + pub sync_result: T, } /// Progress event when importing contacts. @@ -215,8 +215,8 @@ pub trait Account { /// Errors for this account. type Error: std::error::Error + std::fmt::Debug; - /// Error type for network-aware implementations. - type NetworkError: std::error::Error + std::fmt::Debug; + /// Result type for network-aware implementations. + type NetworkResult: std::fmt::Debug; /// Account address. fn address(&self) -> &Address; @@ -283,7 +283,7 @@ pub trait Account { &mut self, folder: &Summary, description: impl AsRef + Send + Sync, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Find the password for a folder. async fn find_folder_password( @@ -385,7 +385,7 @@ pub trait Account { async fn rename_account( &mut self, account_name: String, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Delete the account for this user and sign out. async fn delete_account( @@ -555,13 +555,13 @@ pub trait Account { meta: SecretMeta, secret: Secret, options: AccessOptions, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Bulk insert secrets into the currently open folder. async fn insert_secrets( &mut self, secrets: Vec<(SecretMeta, Secret)>, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Update a secret in the current open folder or a specific folder. /// @@ -574,7 +574,7 @@ pub trait Account { secret: Option, options: AccessOptions, destination: Option<&Summary>, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Move a secret between folders. async fn move_secret( @@ -583,7 +583,7 @@ pub trait Account { from: &Summary, to: &Summary, options: AccessOptions, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Read a secret in the current open folder. async fn read_secret( @@ -597,7 +597,7 @@ pub trait Account { &mut self, secret_id: &SecretId, options: AccessOptions, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Move a secret to the archive. /// @@ -607,7 +607,7 @@ pub trait Account { from: &Summary, secret_id: &SecretId, options: AccessOptions, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Move a secret out of the archive. /// @@ -626,7 +626,7 @@ pub trait Account { secret_meta: &SecretMeta, options: AccessOptions, ) -> std::result::Result< - (SecretMove, Summary), + (SecretMove, Summary), Self::Error, >; @@ -643,28 +643,28 @@ pub trait Account { path: impl AsRef + Send + Sync, options: AccessOptions, destination: Option<&Summary>, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Create a folder. async fn create_folder( &mut self, name: String, options: NewFolderOptions, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Rename a folder. async fn rename_folder( &mut self, summary: &Summary, name: String, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Update folder flags. async fn update_folder_flags( &mut self, summary: &Summary, flags: VaultFlags, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Import a folder from a vault file. async fn import_folder( @@ -672,7 +672,7 @@ pub trait Account { path: impl AsRef + Send + Sync, key: AccessKey, overwrite: bool, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Import a folder from a vault buffer. async fn import_folder_buffer( @@ -680,7 +680,7 @@ pub trait Account { buffer: impl AsRef<[u8]> + Send + Sync, key: AccessKey, overwrite: bool, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Import and overwrite the identity folder from a vault. /// @@ -712,7 +712,7 @@ pub trait Account { async fn delete_folder( &mut self, summary: &Summary, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Try to load an avatar JPEG image for a contact. /// @@ -766,7 +766,7 @@ pub trait Account { async fn import_file( &mut self, target: ImportTarget, - ) -> std::result::Result, Self::Error>; + ) -> std::result::Result, Self::Error>; /// Create a backup archive containing the /// encrypted data for the account. @@ -1236,7 +1236,7 @@ impl LocalAccount { from: &Summary, to: &Summary, mut options: AccessOptions, - ) -> Result> { + ) -> Result::NetworkResult>> { self.open_vault(from, false).await?; let (secret_data, read_event) = self.get_secret(secret_id, None, false).await?; @@ -1308,7 +1308,7 @@ impl LocalAccount { Ok(SecretMove { id: new_id, event, - sync_error: None, + sync_result: (), #[cfg(feature = "files")] file_events, }) @@ -1352,7 +1352,7 @@ impl LocalAccount { path: P, folder_name: String, converter: impl Convert, - ) -> Result> { + ) -> Result> { let paths = self.paths(); #[cfg(feature = "audit")] @@ -1539,7 +1539,7 @@ impl LocalAccount { impl Account for LocalAccount { type Account = LocalAccount; type Error = Error; - type NetworkError = Error; + type NetworkResult = (); fn address(&self) -> &Address { &self.address @@ -1617,7 +1617,7 @@ impl Account for LocalAccount { &mut self, folder: &Summary, description: impl AsRef + Send + Sync, - ) -> Result> { + ) -> Result> { self.authenticated.as_ref().ok_or(Error::NotAuthenticated)?; self.open_folder(folder).await?; @@ -1639,7 +1639,7 @@ impl Account for LocalAccount { Ok(FolderChange { event, commit_state, - sync_error: None, + sync_result: (), }) } @@ -1795,7 +1795,7 @@ impl Account for LocalAccount { async fn rename_account( &mut self, account_name: String, - ) -> Result> { + ) -> Result> { // Rename the local identity folder self.user_mut()? .rename_account(account_name.clone()) @@ -1812,7 +1812,7 @@ impl Account for LocalAccount { Ok(AccountChange { event: Event::Account(event), - sync_error: None, + sync_result: (), }) } @@ -2186,7 +2186,7 @@ impl Account for LocalAccount { meta: SecretMeta, secret: Secret, options: AccessOptions, - ) -> Result> { + ) -> Result> { let (folder, commit_state) = self.compute_folder_state(&options).await?; @@ -2209,7 +2209,7 @@ impl Account for LocalAccount { event, commit_state, folder, - sync_error: None, + sync_result: (), #[cfg(feature = "files")] file_events, }) @@ -2218,7 +2218,7 @@ impl Account for LocalAccount { async fn insert_secrets( &mut self, secrets: Vec<(SecretMeta, Secret)>, - ) -> Result> { + ) -> Result> { let mut results = Vec::new(); for (meta, secret) in secrets { results.push( @@ -2227,7 +2227,7 @@ impl Account for LocalAccount { } Ok(SecretInsert { results, - sync_error: None, + sync_result: (), }) } @@ -2238,7 +2238,7 @@ impl Account for LocalAccount { secret: Option, options: AccessOptions, destination: Option<&Summary>, - ) -> Result> { + ) -> Result> { let (folder, commit_state) = self.compute_folder_state(&options).await?; @@ -2286,7 +2286,7 @@ impl Account for LocalAccount { event, commit_state, folder, - sync_error: None, + sync_result: (), #[cfg(feature = "files")] file_events, }) @@ -2298,7 +2298,7 @@ impl Account for LocalAccount { from: &Summary, to: &Summary, options: AccessOptions, - ) -> Result> { + ) -> Result> { self.mv_secret(secret_id, from, to, options).await } @@ -2314,7 +2314,7 @@ impl Account for LocalAccount { &mut self, secret_id: &SecretId, options: AccessOptions, - ) -> Result> { + ) -> Result> { let (folder, commit_state) = self.compute_folder_state(&options).await?; @@ -2335,7 +2335,7 @@ impl Account for LocalAccount { event, commit_state, folder, - sync_error: None, + sync_result: (), #[cfg(feature = "files")] file_events: result.file_events, }) @@ -2346,7 +2346,7 @@ impl Account for LocalAccount { from: &Summary, secret_id: &SecretId, options: AccessOptions, - ) -> Result> { + ) -> Result> { if from.flags().is_archive() { return Err(Error::AlreadyArchived); } @@ -2363,7 +2363,7 @@ impl Account for LocalAccount { secret_id: &SecretId, secret_meta: &SecretMeta, options: AccessOptions, - ) -> Result<(SecretMove, Summary)> { + ) -> Result<(SecretMove, Summary)> { let from = self .archive_folder() .await @@ -2398,7 +2398,7 @@ impl Account for LocalAccount { path: impl AsRef + Send + Sync, options: AccessOptions, destination: Option<&Summary>, - ) -> Result> { + ) -> Result> { let path = path.as_ref().to_path_buf(); let secret: Secret = path.try_into()?; self.update_secret( @@ -2415,7 +2415,7 @@ impl Account for LocalAccount { &mut self, name: String, mut options: NewFolderOptions, - ) -> Result> { + ) -> Result> { self.authenticated.as_ref().ok_or(Error::NotAuthenticated)?; let key: AccessKey = if let Some(key) = options.key.take() { @@ -2464,7 +2464,7 @@ impl Account for LocalAccount { folder, event, commit_state, - sync_error: None, + sync_result: (), }) } @@ -2472,7 +2472,7 @@ impl Account for LocalAccount { &mut self, summary: &Summary, name: String, - ) -> Result> { + ) -> Result> { let options = AccessOptions { folder: Some(summary.clone()), ..Default::default() @@ -2490,7 +2490,7 @@ impl Account for LocalAccount { Ok(FolderChange { event, commit_state, - sync_error: None, + sync_result: (), }) } @@ -2498,7 +2498,7 @@ impl Account for LocalAccount { &mut self, summary: &Summary, flags: VaultFlags, - ) -> Result> { + ) -> Result> { let options = AccessOptions { folder: Some(summary.clone()), ..Default::default() @@ -2516,7 +2516,7 @@ impl Account for LocalAccount { Ok(FolderChange { event, commit_state, - sync_error: None, + sync_result: (), }) } @@ -2525,7 +2525,7 @@ impl Account for LocalAccount { path: impl AsRef + Send + Sync, key: AccessKey, overwrite: bool, - ) -> Result> { + ) -> Result> { self.authenticated.as_ref().ok_or(Error::NotAuthenticated)?; let buffer = vfs::read(path.as_ref()).await?; self.import_folder_buffer(&buffer, key, overwrite).await @@ -2536,7 +2536,7 @@ impl Account for LocalAccount { buffer: impl AsRef<[u8]> + Send + Sync, key: AccessKey, overwrite: bool, - ) -> Result> { + ) -> Result> { self.authenticated.as_ref().ok_or(Error::NotAuthenticated)?; let mut vault: Vault = decode(buffer.as_ref()).await?; @@ -2651,7 +2651,7 @@ impl Account for LocalAccount { folder: summary, event, commit_state, - sync_error: None, + sync_result: (), }) } @@ -2745,7 +2745,7 @@ impl Account for LocalAccount { async fn delete_folder( &mut self, summary: &Summary, - ) -> Result> { + ) -> Result> { let options = AccessOptions { folder: Some(summary.clone()), ..Default::default() @@ -2765,7 +2765,7 @@ impl Account for LocalAccount { Ok(FolderDelete { events, commit_state, - sync_error: None, + sync_result: (), }) } @@ -2996,7 +2996,7 @@ impl Account for LocalAccount { async fn import_file( &mut self, target: ImportTarget, - ) -> Result> { + ) -> Result> { let result = match target.format { ImportFormat::OnePasswordCsv => { self.import_csv( diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index b632f327f1..6fafb2282c 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sos-server" -version = "0.14.12" +version = "0.15.0" edition = "2021" description = "Server for the Save Our Secrets sync protocol." homepage = "https://saveoursecrets.com" @@ -54,7 +54,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] tokio-rustls-acme = { version = "0.4", features = ["axum"], optional = true } [dependencies.sos-protocol] -version = "0.14" +version = "0.15" path = "../protocol" features = ["files"] diff --git a/crates/sos/Cargo.toml b/crates/sos/Cargo.toml index b7ba00e406..740cc2cd27 100644 --- a/crates/sos/Cargo.toml +++ b/crates/sos/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sos" -version = "0.14.12" +version = "0.15.0" edition = "2021" description = "Distributed, encrypted database for private secrets." homepage = "https://saveoursecrets.com" @@ -51,7 +51,7 @@ rustyline = "14" rustyline-derive = "0.10" [dependencies.sos-net] -version = "0.14" +version = "0.15" features = ["full"] path = "../net" diff --git a/crates/sos/src/commands/server.rs b/crates/sos/src/commands/server.rs index 7b8cddecbd..d980e9f871 100644 --- a/crates/sos/src/commands/server.rs +++ b/crates/sos/src/commands/server.rs @@ -9,7 +9,7 @@ use clap::Subcommand; use sos_net::{ protocol::{Origin, SyncOptions}, sdk::{identity::AccountRef, url::Url}, - RemoteSync, + AccountSync, }; #[derive(Subcommand, Debug)] @@ -53,8 +53,8 @@ pub async fn run(cmd: Command) -> Result<()> { ..Default::default() }; - let sync_error = owner.sync_with_options(&options).await; - if let Some(err) = sync_error { + let sync_result = owner.sync_with_options(&options).await; + if let Some(err) = sync_result.first_error() { owner.remove_server(&origin).await?; return Err(Error::InitialSync(err)); } else { diff --git a/crates/sos/src/commands/sync.rs b/crates/sos/src/commands/sync.rs index e7782fbde2..09d490b8d7 100644 --- a/crates/sos/src/commands/sync.rs +++ b/crates/sos/src/commands/sync.rs @@ -13,7 +13,7 @@ use sos_net::{ storage::StorageEventLogs, url::Url, }, - NetworkAccount, RemoteSync, + AccountSync, NetworkAccount, }; #[derive(Subcommand, Debug)] @@ -58,8 +58,8 @@ pub async fn run(cmd: Command) -> Result<()> { } let options = if url.is_empty() { - let sync_error = owner.sync().await; - if sync_error.is_some() { + let sync_result = owner.sync().await; + if sync_result.first_error().is_some() { return Err(Error::SyncFail); } Default::default() @@ -78,16 +78,16 @@ pub async fn run(cmd: Command) -> Result<()> { origins, ..Default::default() }; - let sync_error = owner.sync_with_options(&options).await; - if sync_error.is_some() { + let sync_result = owner.sync_with_options(&options).await; + if sync_result.first_error().is_some() { return Err(Error::SyncFail); } options }; if files { - let sync_error = owner.sync_file_transfers(&options).await; - if sync_error.is_some() { + let sync_result = owner.sync_file_transfers(&options).await; + if sync_result.first_error().is_some() { return Err(Error::SyncFail); } } diff --git a/crates/sos/src/error.rs b/crates/sos/src/error.rs index d2bc2502ef..58ab1f5682 100644 --- a/crates/sos/src/error.rs +++ b/crates/sos/src/error.rs @@ -1,7 +1,4 @@ -use sos_net::{ - protocol::SyncError, - sdk::{vault::secret::SecretRef, vcard4}, -}; +use sos_net::sdk::{vault::secret::SecretRef, vcard4}; use std::path::PathBuf; use thiserror::Error; @@ -34,7 +31,7 @@ pub enum Error { /// Error performing an initial sync with a server. #[error(r#"initial sync has errors: {0}"#)] - InitialSync(SyncError), + InitialSync(sos_net::Error), /// Could not find an authenticator folder. #[error("could not find an authenticator folder")] diff --git a/crates/test_utils/src/network.rs b/crates/test_utils/src/network.rs index 3e1bc9f186..2f9afa87a5 100644 --- a/crates/test_utils/src/network.rs +++ b/crates/test_utils/src/network.rs @@ -16,8 +16,8 @@ use sos_net::{ vault::{Summary, VaultId}, vfs, Paths, }, - InflightNotification, InflightTransfers, ListenOptions, NetworkAccount, - RemoteBridge, RemoteSync, SyncClient, + AccountSync, InflightNotification, InflightTransfers, ListenOptions, + NetworkAccount, RemoteBridge, SyncClient, }; use std::{ path::PathBuf, @@ -147,8 +147,8 @@ pub async fn simulate_device_with_builder( owner.add_server(origin.clone()).await?; // Sync the local account to create the account on remote - let sync_error = owner.sync().await; - assert!(sync_error.is_none()); + let sync_result = owner.sync().await; + assert!(sync_result.first_error().is_none()); (origin, server.account_path(owner.address())) } else {