IndexedDB: upgrade indexed_db_futures dependency (#5722)
**NOTE:** _this should not be merged until matrix-org/rust-indexed-db#1 is merged! The `[patch]` in this branch should point to the official `matrix-org` fork of `rust-indexed_db`, but is currently pointed at my personal fork._ ## Background This pull request makes updates [`indexed_db_futures`](https://docs.rs/indexed_db_futures/latest/indexed_db_futures/index.html) in the `matrix-sdk-indexeddb` crate. The reason we'd like to update this dependency is because the version currently used does not fully support the Chrome browser (see #5420). The latest version of `indexed_db_futures` has significant changes. Many of these changes can be integrated without issue. There is, however, a single change which is incompatible with the `matrix-sdk-indexeddb` crate. Namely, one cannot access the active transaction in the callback to update the database (for details, see Alorel/rust-indexed-db#66). ### An Updated Proposal Originally, new migrations were implemented in order to work around this issue (see #5467). However, the proposal was ultimately rejected (see @andybalaam's [comment](https://github.com/matrix-org/matrix-rust-sdk/pull/5467#issuecomment-3149550617)). For this reason, the dependency has instead been `[patch]`ed in the top-level `Cargo.toml` with a modified version of `indexed_db_futures` (see matrix-org/rust-indexed-db#1). Furthermore, these changes have been proposed to the maintainer and are awaiting feedback (see Alorel/rust-indexed-db#72). ### Why do we need the active transaction in our migrations? The `crypto_store` module provides access to the active transaction to its migrations (see [here](https://github.com/matrix-org/matrix-rust-sdk/blob/ca89700dfe9f29dcd823bb10861807f9d75e0634/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/mod.rs#L211)). Furthermore, there is a single migration (`v11_to_v12`) in the `crypto_store` module which actually makes use of the active transaction (see [here](https://github.com/matrix-org/matrix-rust-sdk/blob/ca89700dfe9f29dcd823bb10861807f9d75e0634/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/v11_to_v12.rs#L23)). For clarity, the reason `v11_to_v12` is problematic in the latest versions of `indexed_db_futures` is because it is simply adding an index to an object store which was created in a different migration and this requires access to the active transaction. All the other migrations create object stores and indices in the same migration, which does not suffer from the same issue. ## Changes - Move `indexed_db_futures` to the workspace `Cargo.toml` and add a `[patch]` so that it points to a modified version. - Add `GenericError` type and conversions in order to more easily map `indexed_db_futures` errors into `matrix-sdk-*` errors. - Update all IndexedDB interactions so that they use the upgraded interface provided by `indexed_db_futures` - Add functionality for running `wasm-pack` tests against Chrome --- Closes #5420. --- - [ ] Public API changes documented in changelogs (optional) Signed-off-by: Michael Goldenberg <m@mgoldenberg.net> --------- Signed-off-by: Michael Goldenberg <m@mgoldenberg.net>
This commit is contained in:
@@ -11,6 +11,7 @@ version = 2
|
||||
ignore = [
|
||||
{ id = "RUSTSEC-2024-0436", reason = "Unmaintained paste crate, not critical." },
|
||||
{ id = "RUSTSEC-2025-0056", reason = "Unmaintained adler crate, not a direct dependency" },
|
||||
{ id = "RUSTSEC-2024-0388", reason = "Unmaintained derivative crate, not a direct dependency" },
|
||||
]
|
||||
|
||||
[licenses]
|
||||
@@ -54,4 +55,6 @@ allow-git = [
|
||||
# We can release vodozemac whenever we need but let's not block development
|
||||
# on releases.
|
||||
"https://github.com/matrix-org/vodozemac",
|
||||
# A patch override for the bindings: https://github.com/Alorel/rust-indexed-db/pull/72
|
||||
"https://github.com/matrix-org/rust-indexed-db",
|
||||
]
|
||||
|
||||
Generated
+210
-75
@@ -4,14 +4,14 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "accessory"
|
||||
version = "1.3.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87537f9ae7cfa78d5b8ebd1a1db25959f5e737126be4d8eb44a5452fc4b63cde"
|
||||
checksum = "28e416a3ab45838bac2ab2d81b1088d738d7b2d2c5272a54d39366565a29bd80"
|
||||
dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -179,7 +179,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -238,7 +238,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -350,7 +350,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -361,7 +361,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -594,7 +594,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -852,7 +852,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1153,7 +1153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1190,7 +1190,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1224,7 +1224,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1238,7 +1238,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1249,7 +1249,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core 0.20.10",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1260,7 +1260,7 @@ checksum = "2b5be8a7a562d315a5b92a630c30cec6bcf663e6673f00fbb69cca66a6f521b9"
|
||||
dependencies = [
|
||||
"darling_core 0.21.1",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1331,14 +1331,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "delegate-display"
|
||||
version = "2.1.1"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66"
|
||||
checksum = "9926686c832494164c33a36bf65118f4bd6e704000b58c94681bf62e9ad67a74"
|
||||
dependencies = [
|
||||
"impartial-ord",
|
||||
"itoa",
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1362,6 +1364,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
@@ -1380,7 +1393,7 @@ dependencies = [
|
||||
"darling 0.20.10",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1390,7 +1403,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
|
||||
dependencies = [
|
||||
"derive_more-impl 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||
dependencies = [
|
||||
"derive_more-impl 2.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1439,7 +1493,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1722,7 +1776,7 @@ checksum = "dd65f1b59dd22d680c7a626cc4a000c1e03d241c51c3e034d2bc9f1e90734f9b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1789,14 +1843,14 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fancy_constructor"
|
||||
version = "1.3.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07b19d0e43eae2bfbafe4931b5e79c73fb1a849ca15cd41a761a7b8587f9a1a2"
|
||||
checksum = "28a27643a5d05f3a22f5afd6e0d0e6e354f92d37907006f97b84b9cb79082198"
|
||||
dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1966,7 +2020,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2262,7 +2316,7 @@ dependencies = [
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2554,7 +2608,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2620,6 +2674,17 @@ dependencies = [
|
||||
"bitmaps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impartial-ord"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab604ee7085efba6efc65e4ebca0e9533e3aff6cb501d7d77b211e3a781c6d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.4"
|
||||
@@ -2647,19 +2712,39 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexed_db_futures"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c"
|
||||
version = "0.6.4"
|
||||
source = "git+https://github.com/matrix-org/rust-indexed-db?rev=201caac274e38756c6e523ba7cf84ffb625b7c48#201caac274e38756c6e523ba7cf84ffb625b7c48"
|
||||
dependencies = [
|
||||
"accessory",
|
||||
"cfg-if",
|
||||
"delegate-display",
|
||||
"derive_more 2.0.1",
|
||||
"fancy_constructor",
|
||||
"futures-core",
|
||||
"indexed_db_futures_macros_internal",
|
||||
"js-sys",
|
||||
"uuid",
|
||||
"sealed",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"smallvec",
|
||||
"thiserror 2.0.16",
|
||||
"tokio",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm_evt_listener",
|
||||
"web-sys",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexed_db_futures_macros_internal"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/matrix-org/rust-indexed-db?rev=201caac274e38756c6e523ba7cf84ffb625b7c48#201caac274e38756c6e523ba7cf84ffb625b7c48"
|
||||
dependencies = [
|
||||
"macroific",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2713,7 +2798,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2998,9 +3083,9 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "macroific"
|
||||
version = "1.3.1"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd"
|
||||
checksum = "89f276537b4b8f981bf1c13d79470980f71134b7bdcc5e6e911e910e556b0285"
|
||||
dependencies = [
|
||||
"macroific_attr_parse",
|
||||
"macroific_core",
|
||||
@@ -3009,38 +3094,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "macroific_attr_parse"
|
||||
version = "1.3.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67"
|
||||
checksum = "ad4023761b45fcd36abed8fb7ae6a80456b0a38102d55e89a57d9a594a236be9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"sealed",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macroific_core"
|
||||
version = "1.0.2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55"
|
||||
checksum = "d0a7594d3c14916fa55bef7e9d18c5daa9ed410dd37504251e4b75bbdeec33e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"sealed",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macroific_macro"
|
||||
version = "1.1.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae"
|
||||
checksum = "4da6f2ed796261b0a74e2b52b42c693bb6dee1effba3a482c49592659f824b3b"
|
||||
dependencies = [
|
||||
"macroific_attr_parse",
|
||||
"macroific_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3098,7 +3184,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3380,14 +3466,13 @@ version = "0.7.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-sdk-indexeddb"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_matches",
|
||||
"assert_matches2",
|
||||
"async-trait",
|
||||
@@ -3569,7 +3654,7 @@ name = "matrix-sdk-test-macros"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3933,7 +4018,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4091,7 +4176,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4278,7 +4363,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4356,7 +4441,7 @@ dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4896,7 +4981,7 @@ dependencies = [
|
||||
"quote",
|
||||
"ruma-identifiers-validation",
|
||||
"serde",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"toml 0.8.15",
|
||||
]
|
||||
|
||||
@@ -5096,7 +5181,18 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sealed"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5276,7 +5372,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5466,6 +5562,9 @@ name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
@@ -5594,7 +5693,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5607,7 +5706,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5616,6 +5715,17 @@ version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
@@ -5644,7 +5754,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5854,7 +5964,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5865,7 +5975,7 @@ checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5981,7 +6091,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6161,7 +6271,7 @@ source = "git+https://github.com/tokio-rs/tracing.git?rev=20f5b3d8ba057ca9c4ae00
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6362,6 +6472,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "uniffi"
|
||||
version = "0.28.0"
|
||||
@@ -6426,7 +6542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "802d2051a700e3ec894c79f80d2705b69d85844dafbbe5d1a92776f8f48b563a"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6458,7 +6574,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"toml 0.5.11",
|
||||
"uniffi_meta",
|
||||
]
|
||||
@@ -6751,7 +6867,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -6786,7 +6902,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -6821,7 +6937,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6837,6 +6953,24 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm_evt_listener"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc92d6378b411ed94839112a36d9dbc77143451d85b05dfb0cce93a78dab1963"
|
||||
dependencies = [
|
||||
"accessory",
|
||||
"derivative",
|
||||
"derive_more 1.0.0",
|
||||
"fancy_constructor",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
@@ -6854,6 +6988,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -6993,7 +7128,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7004,7 +7139,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7317,7 +7452,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -7338,7 +7473,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7358,7 +7493,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -7379,7 +7514,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7401,7 +7536,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -51,6 +51,7 @@ hkdf = "0.12.4"
|
||||
hmac = "0.12.1"
|
||||
http = "1.3.1"
|
||||
imbl = "6.1.0"
|
||||
indexed_db_futures = "0.6.4"
|
||||
indexmap = "2.11.0"
|
||||
insta = { version = "1.43.1", features = ["json", "redactions"] }
|
||||
itertools = "0.14.0"
|
||||
@@ -200,6 +201,7 @@ lto = false
|
||||
[patch.crates-io]
|
||||
async-compat = { git = "https://github.com/element-hq/async-compat", rev = "5a27c8b290f1f1dcfc0c4ec22c464e38528aa591" }
|
||||
const_panic = { git = "https://github.com/jplatte/const_panic", rev = "9024a4cb3eac45c1d2d980f17aaee287b17be498" }
|
||||
indexed_db_futures = { git = "https://github.com/matrix-org/rust-indexed-db", rev = "201caac274e38756c6e523ba7cf84ffb625b7c48" }
|
||||
# Needed to fix rotation log issue on Android (https://github.com/tokio-rs/tracing/issues/2937)
|
||||
tracing = { git = "https://github.com/tokio-rs/tracing.git", rev = "20f5b3d8ba057ca9c4ae00ad30dda3dce8a71c05" }
|
||||
tracing-core = { git = "https://github.com/tokio-rs/tracing.git", rev = "20f5b3d8ba057ca9c4ae00ad30dda3dce8a71c05" }
|
||||
|
||||
@@ -25,13 +25,17 @@ experimental-encrypted-state-events = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
base64.workspace = true
|
||||
gloo-utils = { version = "0.2.0", features = ["serde"] }
|
||||
growable-bloom-filter = { workspace = true, optional = true }
|
||||
hkdf.workspace = true
|
||||
indexed_db_futures = "0.5.0"
|
||||
indexed_db_futures = { workspace = true, features = [
|
||||
"serde",
|
||||
"cursors",
|
||||
"indices",
|
||||
"streams",
|
||||
]}
|
||||
js-sys.workspace = true
|
||||
matrix-sdk-base = { workspace = true, features = ["js"], optional = true }
|
||||
matrix-sdk-crypto = { workspace = true, features = ["js"], optional = true }
|
||||
|
||||
@@ -14,9 +14,16 @@
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use indexed_db_futures::{prelude::*, web_sys::DomException};
|
||||
use indexed_db_futures::{
|
||||
database::Database,
|
||||
error::{Error, OpenDbError},
|
||||
index::Index,
|
||||
internals::SystemRepr,
|
||||
object_store::ObjectStore,
|
||||
prelude::*,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use tracing::info;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use crate::{crypto_store::Result, serializer::SafeEncodeSerializer, IndexeddbCryptoStoreError};
|
||||
|
||||
@@ -32,7 +39,7 @@ mod v7_to_v8;
|
||||
mod v8_to_v10;
|
||||
|
||||
struct MigrationDb {
|
||||
db: IdbDatabase,
|
||||
db: Database,
|
||||
next_version: u32,
|
||||
}
|
||||
|
||||
@@ -42,12 +49,12 @@ impl MigrationDb {
|
||||
/// closing the DB when this object is dropped.
|
||||
async fn new(name: &str, next_version: u32) -> Result<Self> {
|
||||
info!("IndexeddbCryptoStore migrate data before v{next_version} starting");
|
||||
Ok(Self { db: IdbDatabase::open(name)?.await?, next_version })
|
||||
Ok(Self { db: Database::open(name).await?, next_version })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MigrationDb {
|
||||
type Target = IdbDatabase;
|
||||
type Target = Database;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.db
|
||||
@@ -58,7 +65,7 @@ impl Drop for MigrationDb {
|
||||
fn drop(&mut self) {
|
||||
let version = self.next_version;
|
||||
info!("IndexeddbCryptoStore migrate data before v{version} finished");
|
||||
self.db.close();
|
||||
self.db.as_sys().close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +108,7 @@ const MAX_SUPPORTED_SCHEMA_VERSION: u32 = 99;
|
||||
pub async fn open_and_upgrade_db(
|
||||
name: &str,
|
||||
serializer: &SafeEncodeSerializer,
|
||||
) -> Result<IdbDatabase, IndexeddbCryptoStoreError> {
|
||||
) -> Result<Database, IndexeddbCryptoStoreError> {
|
||||
// Move the DB version up from where it is to the latest version.
|
||||
//
|
||||
// Schema changes need to be separate from data migrations, so we often
|
||||
@@ -177,11 +184,11 @@ pub async fn open_and_upgrade_db(
|
||||
// `MAX_SUPPORTED_SCHEMA_VERSION` itself to the next multiple of 10).
|
||||
|
||||
// Open and return the DB (we know it's at the latest version)
|
||||
Ok(IdbDatabase::open(name)?.await?)
|
||||
Ok(Database::open(name).await?)
|
||||
}
|
||||
|
||||
async fn db_version(name: &str) -> Result<u32, IndexeddbCryptoStoreError> {
|
||||
let db = IdbDatabase::open(name)?.await?;
|
||||
let db = Database::open(name).await?;
|
||||
let old_version = db.version() as u32;
|
||||
db.close();
|
||||
Ok(old_version)
|
||||
@@ -197,50 +204,45 @@ type OldVersion = u32;
|
||||
/// * `version` - version we are upgrading to.
|
||||
/// * `f` - closure which will be called if the database is below the version
|
||||
/// given. It will be called with three arguments `(db, txn, oldver)`, where:
|
||||
/// * `db` - the [`IdbDatabase`]
|
||||
/// * `txn` - the database transaction: a [`IdbTransaction`]
|
||||
/// * `db` - the [`Database`]
|
||||
/// * `txn` - the database transaction: a [`Transaction`]
|
||||
/// * `oldver` - the version number before the upgrade.
|
||||
async fn do_schema_upgrade<F>(name: &str, version: u32, f: F) -> Result<(), DomException>
|
||||
async fn do_schema_upgrade<F>(name: &str, version: u32, f: F) -> Result<(), OpenDbError>
|
||||
where
|
||||
F: Fn(&IdbDatabase, IdbTransaction<'_>, OldVersion) -> Result<(), JsValue> + 'static,
|
||||
F: Fn(&Transaction<'_>, OldVersion) -> Result<(), Error> + 'static,
|
||||
{
|
||||
info!("IndexeddbCryptoStore upgrade schema -> v{version} starting");
|
||||
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(name, version)?;
|
||||
let db = Database::open(name)
|
||||
.with_version(version)
|
||||
.with_on_upgrade_needed(move |evt, tx| {
|
||||
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
|
||||
// works with an unsigned integer.
|
||||
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
|
||||
let old_version = evt.old_version() as u32;
|
||||
|
||||
db_req.set_on_upgrade_needed(Some(move |evt: &IdbVersionChangeEvent| {
|
||||
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
|
||||
// works with an unsigned integer.
|
||||
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
|
||||
let old_version = evt.old_version() as u32;
|
||||
|
||||
// Run the upgrade code we were supplied
|
||||
f(evt.db(), evt.transaction(), old_version)
|
||||
}));
|
||||
|
||||
let db = db_req.await?;
|
||||
// Run the upgrade code we were supplied
|
||||
f(tx, old_version)
|
||||
})
|
||||
.await?;
|
||||
db.close();
|
||||
info!("IndexeddbCryptoStore upgrade schema -> v{version} complete");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_nonunique_index<'a>(
|
||||
object_store: &'a IdbObjectStore<'a>,
|
||||
object_store: &'a ObjectStore<'a>,
|
||||
name: &str,
|
||||
key_path: &str,
|
||||
) -> Result<IdbIndex<'a>, DomException> {
|
||||
let params = IdbIndexParameters::new();
|
||||
params.set_unique(false);
|
||||
object_store.create_index_with_params(name, &IdbKeyPath::str(key_path), ¶ms)
|
||||
) -> Result<Index<'a>, Error> {
|
||||
object_store.create_index(name, key_path.into()).with_unique(false).build()
|
||||
}
|
||||
|
||||
fn add_unique_index<'a>(
|
||||
object_store: &'a IdbObjectStore<'a>,
|
||||
object_store: &'a ObjectStore<'a>,
|
||||
name: &str,
|
||||
key_path: &str,
|
||||
) -> Result<IdbIndex<'a>, DomException> {
|
||||
let params = IdbIndexParameters::new();
|
||||
params.set_unique(true);
|
||||
object_store.create_index_with_params(name, &IdbKeyPath::str(key_path), ¶ms)
|
||||
) -> Result<Index<'a>, Error> {
|
||||
object_store.create_index(name, key_path.into()).with_unique(true).build()
|
||||
}
|
||||
|
||||
#[cfg(all(test, target_family = "wasm"))]
|
||||
@@ -249,7 +251,9 @@ mod tests {
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use indexed_db_futures::prelude::*;
|
||||
use indexed_db_futures::{
|
||||
database::VersionChangeEvent, prelude::*, transaction::TransactionMode,
|
||||
};
|
||||
use matrix_sdk_common::js_tracing::make_tracing_subscriber;
|
||||
use matrix_sdk_crypto::{
|
||||
olm::{InboundGroupSession, SenderData, SessionKey},
|
||||
@@ -262,6 +266,7 @@ mod tests {
|
||||
use ruma::{room_id, OwnedRoomId, RoomId};
|
||||
use serde::Serialize;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::console;
|
||||
|
||||
use super::{v0_to_v5, v7::InboundGroupSessionIndexedDbObject2};
|
||||
@@ -304,14 +309,21 @@ mod tests {
|
||||
// Check how long it takes to insert these records
|
||||
measure_performance("Inserting", "v8", NUM_RECORDS_FOR_PERF, || async {
|
||||
for (key, session_js) in objects.iter() {
|
||||
store.add_key_val(key, session_js).unwrap().await.unwrap();
|
||||
store
|
||||
.add(session_js)
|
||||
.with_key(key)
|
||||
.without_key_type()
|
||||
.build()
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Check how long it takes to count these records
|
||||
measure_performance("Counting", "v8", NUM_RECORDS_FOR_PERF, || async {
|
||||
store.count().unwrap().await.unwrap();
|
||||
store.count().await.unwrap();
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -340,40 +352,51 @@ mod tests {
|
||||
// Check how long it takes to insert these records
|
||||
measure_performance("Inserting", "v10", NUM_RECORDS_FOR_PERF, || async {
|
||||
for (key, session_js) in objects.iter() {
|
||||
store.add_key_val(key, session_js).unwrap().await.unwrap();
|
||||
store
|
||||
.add(session_js)
|
||||
.with_key(key)
|
||||
.without_key_type()
|
||||
.build()
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Check how long it takes to count these records
|
||||
measure_performance("Counting", "v10", NUM_RECORDS_FOR_PERF, || async {
|
||||
store.count().unwrap().await.unwrap();
|
||||
store.count().await.unwrap();
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn create_db(db_prefix: &str) -> IdbDatabase {
|
||||
async fn create_db(db_prefix: &str) -> Database {
|
||||
let db_name = format!("{db_prefix}::matrix-sdk-crypto");
|
||||
let store_name = format!("{db_prefix}_store");
|
||||
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&db_name, 1).unwrap();
|
||||
db_req.set_on_upgrade_needed(Some(
|
||||
move |evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
|
||||
evt.db().create_object_store(&store_name)?;
|
||||
Ok(())
|
||||
},
|
||||
));
|
||||
db_req.await.unwrap()
|
||||
Database::open(&db_name)
|
||||
.with_version(1u32)
|
||||
.with_on_upgrade_needed(
|
||||
move |_: VersionChangeEvent, tx: &Transaction<'_>| -> Result<(), Error> {
|
||||
tx.db().create_object_store(&store_name).build()?;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.build()
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn create_transaction<'a>(db: &'a IdbDatabase, db_prefix: &str) -> IdbTransaction<'a> {
|
||||
async fn create_transaction<'a>(db: &'a Database, db_prefix: &str) -> Transaction<'a> {
|
||||
let store_name = format!("{db_prefix}_store");
|
||||
db.transaction_on_one_with_mode(&store_name, IdbTransactionMode::Readwrite).unwrap()
|
||||
db.transaction(&store_name).with_mode(TransactionMode::Readwrite).build().unwrap()
|
||||
}
|
||||
|
||||
async fn create_store<'a>(
|
||||
transaction: &'a IdbTransaction<'a>,
|
||||
transaction: &'a Transaction<'a>,
|
||||
db_prefix: &str,
|
||||
) -> IdbObjectStore<'a> {
|
||||
) -> ObjectStore<'a> {
|
||||
let store_name = format!("{db_prefix}_store");
|
||||
transaction.object_store(&store_name).unwrap()
|
||||
}
|
||||
@@ -520,7 +543,7 @@ mod tests {
|
||||
let db_name = format!("{db_prefix:0}::matrix-sdk-crypto");
|
||||
|
||||
// delete the db in case it was used in a previous run
|
||||
let _ = IdbDatabase::delete_by_name(&db_name);
|
||||
let _ = Database::delete_by_name(&db_name);
|
||||
|
||||
// Given a DB with data in it as it was at v5
|
||||
let room_id = room_id!("!test:localhost");
|
||||
@@ -568,21 +591,21 @@ mod tests {
|
||||
store: &IndexeddbCryptoStore,
|
||||
fetched_backed_up_session: &InboundGroupSession,
|
||||
) {
|
||||
let db = IdbDatabase::open(&db_name).unwrap().await.unwrap();
|
||||
let db = Database::open(&db_name).build().unwrap().await.unwrap();
|
||||
assert!(db.version() >= 10.0);
|
||||
let transaction = db.transaction_on_one("inbound_group_sessions3").unwrap();
|
||||
let transaction = db.transaction("inbound_group_sessions3").build().unwrap();
|
||||
let raw_store = transaction.object_store("inbound_group_sessions3").unwrap();
|
||||
let key = store.serializer.encode_key(
|
||||
keys::INBOUND_GROUP_SESSIONS_V3,
|
||||
(fetched_backed_up_session.room_id(), fetched_backed_up_session.session_id()),
|
||||
);
|
||||
let idb_object: InboundGroupSessionIndexedDbObject =
|
||||
serde_wasm_bindgen::from_value(raw_store.get(&key).unwrap().await.unwrap().unwrap())
|
||||
.unwrap();
|
||||
serde_wasm_bindgen::from_value(raw_store.get(&key).await.unwrap().unwrap()).unwrap();
|
||||
|
||||
assert_eq!(idb_object.backed_up_to, -1);
|
||||
assert!(raw_store.index_names().find(|idx| idx == "backed_up_to").is_some());
|
||||
|
||||
transaction.commit().await.unwrap();
|
||||
db.close();
|
||||
}
|
||||
|
||||
@@ -591,16 +614,15 @@ mod tests {
|
||||
store: &IndexeddbCryptoStore,
|
||||
session: &InboundGroupSession,
|
||||
) {
|
||||
let db = IdbDatabase::open(&db_name).unwrap().await.unwrap();
|
||||
let db = Database::open(&db_name).build().unwrap().await.unwrap();
|
||||
assert!(db.version() >= 12.0);
|
||||
let transaction = db.transaction_on_one("inbound_group_sessions3").unwrap();
|
||||
let transaction = db.transaction("inbound_group_sessions3").build().unwrap();
|
||||
let raw_store = transaction.object_store("inbound_group_sessions3").unwrap();
|
||||
let key = store
|
||||
.serializer
|
||||
.encode_key(keys::INBOUND_GROUP_SESSIONS_V3, (session.room_id(), session.session_id()));
|
||||
let idb_object: InboundGroupSessionIndexedDbObject =
|
||||
serde_wasm_bindgen::from_value(raw_store.get(&key).unwrap().await.unwrap().unwrap())
|
||||
.unwrap();
|
||||
serde_wasm_bindgen::from_value(raw_store.get(&key).await.unwrap().unwrap()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
idb_object.session_id,
|
||||
@@ -623,6 +645,7 @@ mod tests {
|
||||
.find(|idx| idx == "inbound_group_session_sender_key_sender_data_type_idx")
|
||||
.is_some());
|
||||
|
||||
transaction.commit().await.unwrap();
|
||||
db.close();
|
||||
}
|
||||
|
||||
@@ -685,10 +708,9 @@ mod tests {
|
||||
let serializer = SafeEncodeSerializer::new(store_cipher.clone());
|
||||
|
||||
let txn = db
|
||||
.transaction_on_one_with_mode(
|
||||
old_keys::INBOUND_GROUP_SESSIONS_V1,
|
||||
IdbTransactionMode::Readwrite,
|
||||
)
|
||||
.transaction(old_keys::INBOUND_GROUP_SESSIONS_V1)
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()
|
||||
.unwrap();
|
||||
let sessions = txn.object_store(old_keys::INBOUND_GROUP_SESSIONS_V1).unwrap();
|
||||
for session in session_entries {
|
||||
@@ -701,9 +723,9 @@ mod tests {
|
||||
// Serialize the session with the old style of serialization, since that's what
|
||||
// we used at the time.
|
||||
let serialized_session = serialize_value_as_legacy(&store_cipher, &pickle);
|
||||
sessions.put_key_val(&key, &serialized_session).unwrap();
|
||||
sessions.put(&serialized_session).with_key(key).build().unwrap();
|
||||
}
|
||||
txn.await.into_result().unwrap();
|
||||
txn.commit().await.unwrap();
|
||||
|
||||
// now close our DB, reopen it properly, and check that we can still read our
|
||||
// data.
|
||||
@@ -736,21 +758,23 @@ mod tests {
|
||||
let db_name = format!("{db_prefix:0}::matrix-sdk-crypto");
|
||||
|
||||
// delete the db in case it was used in a previous run
|
||||
let _ = IdbDatabase::delete_by_name(&db_name);
|
||||
let _ = Database::delete_by_name(&db_name).unwrap().await.unwrap();
|
||||
|
||||
// Given a DB with data in it as it was at v5
|
||||
let db = create_v5_db(&db_name).await.unwrap();
|
||||
|
||||
let txn = db
|
||||
.transaction_on_one_with_mode(keys::BACKUP_KEYS, IdbTransactionMode::Readwrite)
|
||||
.transaction(keys::BACKUP_KEYS)
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()
|
||||
.unwrap();
|
||||
let store = txn.object_store(keys::BACKUP_KEYS).unwrap();
|
||||
store
|
||||
.put_key_val(
|
||||
&JsValue::from_str(old_keys::BACKUP_KEY_V1),
|
||||
&serialize_value_as_legacy(&store_cipher, &"1".to_owned()),
|
||||
)
|
||||
.put(&serialize_value_as_legacy(&store_cipher, &"1".to_owned()))
|
||||
.with_key(JsValue::from_str(old_keys::BACKUP_KEY_V1))
|
||||
.build()
|
||||
.unwrap();
|
||||
txn.commit().await.unwrap();
|
||||
db.close();
|
||||
|
||||
// When I open a store based on that DB, triggering an upgrade
|
||||
@@ -762,9 +786,9 @@ mod tests {
|
||||
assert_eq!(backup_data.backup_version, Some("1".to_owned()));
|
||||
}
|
||||
|
||||
async fn create_v5_db(name: &str) -> std::result::Result<IdbDatabase, DomException> {
|
||||
async fn create_v5_db(name: &str) -> std::result::Result<Database, OpenDbError> {
|
||||
v0_to_v5::schema_add(name).await?;
|
||||
IdbDatabase::open_u32(name, 5)?.await
|
||||
Database::open(name).with_version(5u32).build()?.await
|
||||
}
|
||||
|
||||
/// Opening a db that has been upgraded to MAX_SUPPORTED_SCHEMA_VERSION
|
||||
@@ -811,23 +835,27 @@ mod tests {
|
||||
let db_name = format!("{db_prefix}::matrix-sdk-crypto");
|
||||
|
||||
// delete the db in case it was used in a previous run
|
||||
let _ = IdbDatabase::delete_by_name(&db_name);
|
||||
let _ = Database::delete_by_name(&db_name);
|
||||
|
||||
// Open, and close, the store at the regular version.
|
||||
IndexeddbCryptoStore::open_with_store_cipher(&db_prefix, None).await.unwrap();
|
||||
|
||||
// Now upgrade to the given version, keeping a record of the previous version so
|
||||
// that we can double-check it.
|
||||
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&db_name, version).unwrap();
|
||||
|
||||
let old_version: Rc<Cell<Option<u32>>> = Rc::new(Cell::new(None));
|
||||
let old_version2 = old_version.clone();
|
||||
db_req.set_on_upgrade_needed(Some(move |evt: &IdbVersionChangeEvent| {
|
||||
old_version2.set(Some(evt.old_version() as u32));
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
let db = db_req.await.unwrap();
|
||||
let db = Database::open(&db_name)
|
||||
.with_version(version)
|
||||
.with_on_upgrade_needed(move |evt: VersionChangeEvent, _: &Transaction<'_>| {
|
||||
old_version2.set(Some(evt.old_version() as u32));
|
||||
Ok(())
|
||||
})
|
||||
.build()
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
old_version.get(),
|
||||
Some(EXPECTED_SCHEMA_VERSION),
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
//! Schema-only migrations adding various stores and indices, notably
|
||||
//! the first version of `inbound_group_sessions`.
|
||||
|
||||
use indexed_db_futures::IdbDatabase;
|
||||
use web_sys::DomException;
|
||||
use indexed_db_futures::{
|
||||
database::Database,
|
||||
error::{Error, OpenDbError},
|
||||
Build,
|
||||
};
|
||||
|
||||
use crate::crypto_store::{
|
||||
keys,
|
||||
@@ -25,11 +28,12 @@ use crate::crypto_store::{
|
||||
};
|
||||
|
||||
/// Perform schema migrations as needed, up to schema version 5.
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 5, |db, _, old_version| {
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 5, |tx, old_version| {
|
||||
let db = tx.db();
|
||||
// An old_version of 1 could either mean actually the first version of the
|
||||
// schema, or a completely empty schema that has been created with a
|
||||
// call to `IdbDatabase::open` with no explicit "version". So, to determine
|
||||
// call to `Database::open` with no explicit "version". So, to determine
|
||||
// if we need to create the V1 stores, we actually check if the schema is empty.
|
||||
if db.object_store_names().next().is_none() {
|
||||
schema_add_v1(db)?;
|
||||
@@ -56,57 +60,57 @@ pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
|
||||
.await
|
||||
}
|
||||
|
||||
fn schema_add_v1(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
db.create_object_store(keys::CORE)?;
|
||||
db.create_object_store(keys::SESSION)?;
|
||||
fn schema_add_v1(db: &Database) -> Result<(), Error> {
|
||||
db.create_object_store(keys::CORE).build()?;
|
||||
db.create_object_store(keys::SESSION).build()?;
|
||||
|
||||
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
|
||||
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
|
||||
db.create_object_store(keys::TRACKED_USERS)?;
|
||||
db.create_object_store(keys::OLM_HASHES)?;
|
||||
db.create_object_store(keys::DEVICES)?;
|
||||
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1).build()?;
|
||||
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS).build()?;
|
||||
db.create_object_store(keys::TRACKED_USERS).build()?;
|
||||
db.create_object_store(keys::OLM_HASHES).build()?;
|
||||
db.create_object_store(keys::DEVICES).build()?;
|
||||
|
||||
db.create_object_store(keys::IDENTITIES)?;
|
||||
db.create_object_store(keys::BACKUP_KEYS)?;
|
||||
db.create_object_store(keys::IDENTITIES).build()?;
|
||||
db.create_object_store(keys::BACKUP_KEYS).build()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schema_add_v2(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
fn schema_add_v2(db: &Database) -> Result<(), Error> {
|
||||
// We changed how we store inbound group sessions, the key used to
|
||||
// be a tuple of `(room_id, sender_key, session_id)` now it's a
|
||||
// tuple of `(room_id, session_id)`
|
||||
//
|
||||
// Let's just drop the whole object store.
|
||||
db.delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
|
||||
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
|
||||
db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1).build()?;
|
||||
|
||||
db.create_object_store(keys::ROOM_SETTINGS)?;
|
||||
db.create_object_store(keys::ROOM_SETTINGS).build()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schema_add_v3(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
fn schema_add_v3(db: &Database) -> Result<(), Error> {
|
||||
// We changed the way we store outbound session.
|
||||
// ShareInfo changed from a struct to an enum with struct variant.
|
||||
// Let's just discard the existing outbounds
|
||||
db.delete_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
|
||||
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
|
||||
db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS).build()?;
|
||||
|
||||
// Support for MSC2399 withheld codes
|
||||
db.create_object_store(keys::DIRECT_WITHHELD_INFO)?;
|
||||
db.create_object_store(keys::DIRECT_WITHHELD_INFO).build()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schema_add_v4(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
db.create_object_store(keys::SECRETS_INBOX)?;
|
||||
fn schema_add_v4(db: &Database) -> Result<(), Error> {
|
||||
db.create_object_store(keys::SECRETS_INBOX).build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schema_add_v5(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
fn schema_add_v5(db: &Database) -> Result<(), Error> {
|
||||
// Create a new store for outgoing secret requests
|
||||
let object_store = db.create_object_store(keys::GOSSIP_REQUESTS)?;
|
||||
let object_store = db.create_object_store(keys::GOSSIP_REQUESTS).build()?;
|
||||
|
||||
add_nonunique_index(&object_store, keys::GOSSIP_REQUESTS_UNSENT_INDEX, "unsent")?;
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
//! Migration code that moves from `backup_keys.backup_key_v1` to
|
||||
//! `backup_keys.backup_version_v1`, switching to a new serialization format.
|
||||
|
||||
use indexed_db_futures::IdbQuerySource;
|
||||
use indexed_db_futures::{
|
||||
error::OpenDbError, query_source::QuerySource, transaction::TransactionMode, Build,
|
||||
};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::{DomException, IdbTransactionMode};
|
||||
|
||||
use crate::{
|
||||
crypto_store::{
|
||||
@@ -34,10 +35,10 @@ pub(crate) async fn data_migrate(
|
||||
serializer: &SafeEncodeSerializer,
|
||||
) -> crate::crypto_store::Result<()> {
|
||||
let db = MigrationDb::new(name, 11).await?;
|
||||
let txn = db.transaction_on_one_with_mode(keys::BACKUP_KEYS, IdbTransactionMode::Readwrite)?;
|
||||
let txn = db.transaction(keys::BACKUP_KEYS).with_mode(TransactionMode::Readwrite).build()?;
|
||||
let store = txn.object_store(keys::BACKUP_KEYS)?;
|
||||
|
||||
let bv = store.get(&JsValue::from_str(old_keys::BACKUP_KEY_V1))?.await?;
|
||||
let bv = store.get(&JsValue::from_str(old_keys::BACKUP_KEY_V1)).await?;
|
||||
|
||||
let Some(bv) = bv else {
|
||||
return Ok(());
|
||||
@@ -50,14 +51,15 @@ pub(crate) async fn data_migrate(
|
||||
|
||||
// Re-serialize as new format, then store in the new field.
|
||||
let serialized = serializer.serialize_value(&bv)?;
|
||||
store.put_key_val(&JsValue::from_str(keys::BACKUP_VERSION_V1), &serialized)?.await?;
|
||||
store.delete(&JsValue::from_str(old_keys::BACKUP_KEY_V1))?.await?;
|
||||
store.put(&serialized).with_key(JsValue::from_str(keys::BACKUP_VERSION_V1)).await?;
|
||||
store.delete(&JsValue::from_str(old_keys::BACKUP_KEY_V1)).await?;
|
||||
txn.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform the schema upgrade v10 to v11, just bumping the schema version.
|
||||
pub(crate) async fn schema_bump(name: &str) -> crate::crypto_store::Result<(), DomException> {
|
||||
pub(crate) async fn schema_bump(name: &str) -> crate::crypto_store::Result<(), OpenDbError> {
|
||||
// Just bump the version number to 11 to demonstrate that we have run the data
|
||||
// changes from data_migrate.
|
||||
do_schema_upgrade(name, 11, |_, _, _| Ok(())).await
|
||||
do_schema_upgrade(name, 11, |_, _| Ok(())).await
|
||||
}
|
||||
|
||||
@@ -12,21 +12,22 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use indexed_db_futures::IdbKeyPath;
|
||||
use web_sys::DomException;
|
||||
use indexed_db_futures::{error::OpenDbError, Build};
|
||||
|
||||
use crate::crypto_store::{keys, migrations::do_schema_upgrade, Result};
|
||||
|
||||
/// Perform the schema upgrade v11 to v12, adding an index on
|
||||
/// `(curve_key, sender_data_type, session_id)` to `inbound_group_sessions3`.
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 12, |_, transaction, _| {
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 12, |transaction, _| {
|
||||
let object_store = transaction.object_store(keys::INBOUND_GROUP_SESSIONS_V3)?;
|
||||
|
||||
object_store.create_index(
|
||||
keys::INBOUND_GROUP_SESSIONS_SENDER_KEY_INDEX,
|
||||
&IdbKeyPath::str_sequence(&["sender_key", "sender_data_type", "session_id"]),
|
||||
)?;
|
||||
object_store
|
||||
.create_index(
|
||||
keys::INBOUND_GROUP_SESSIONS_SENDER_KEY_INDEX,
|
||||
["sender_key", "sender_data_type", "session_id"].into(),
|
||||
)
|
||||
.build()?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
||||
@@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
use web_sys::DomException;
|
||||
use indexed_db_futures::{error::OpenDbError, Build};
|
||||
|
||||
use crate::crypto_store::{keys, migrations::do_schema_upgrade, Result};
|
||||
|
||||
/// Perform the schema upgrade v12 to v13, adding the
|
||||
/// `received_room_key_bundles` store.
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 13, |db, _, _| {
|
||||
db.create_object_store(keys::RECEIVED_ROOM_KEY_BUNDLES)?;
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 13, |tx, _| {
|
||||
tx.db().create_object_store(keys::RECEIVED_ROOM_KEY_BUNDLES).build()?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
use web_sys::{DomException, IdbTransactionMode};
|
||||
use indexed_db_futures::{error::OpenDbError, transaction::TransactionMode, Build};
|
||||
|
||||
use super::MigrationDb;
|
||||
use crate::{
|
||||
@@ -24,25 +24,25 @@ use crate::{
|
||||
|
||||
pub(crate) async fn data_migrate(name: &str, _: &SafeEncodeSerializer) -> Result<()> {
|
||||
let db = MigrationDb::new(name, 14).await?;
|
||||
let transaction = db.transaction_on_one_with_mode(
|
||||
keys::RECEIVED_ROOM_KEY_BUNDLES,
|
||||
IdbTransactionMode::Readwrite,
|
||||
)?;
|
||||
let transaction = db
|
||||
.transaction(keys::RECEIVED_ROOM_KEY_BUNDLES)
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()?;
|
||||
let store = transaction.object_store(keys::RECEIVED_ROOM_KEY_BUNDLES)?;
|
||||
|
||||
// The schema didn't actually change, we just changed the objects that are
|
||||
// stored. So let us remove them.
|
||||
store.clear()?;
|
||||
|
||||
transaction.await.into_result()?;
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform the schema upgrade v13 to v14, just bumping the schema version since
|
||||
/// the schema didn't actually change.
|
||||
pub(crate) async fn schema_bump(name: &str) -> Result<(), DomException> {
|
||||
pub(crate) async fn schema_bump(name: &str) -> Result<(), OpenDbError> {
|
||||
// Just bump the version number to 14 to demonstrate that we have run the data
|
||||
// changes from data_migrate.
|
||||
do_schema_upgrade(name, 14, |_, _, _| Ok(())).await
|
||||
do_schema_upgrade(name, 14, |_, _| Ok(())).await
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
//! Then we move the data into the new store.
|
||||
//! The migration 6->7 deletes the old store inbound_group_sessions.
|
||||
|
||||
use indexed_db_futures::IdbQuerySource;
|
||||
use indexed_db_futures::{
|
||||
error::OpenDbError, query_source::QuerySource, transaction::TransactionMode, Build,
|
||||
};
|
||||
use matrix_sdk_crypto::olm::InboundGroupSession;
|
||||
use tracing::{debug, info};
|
||||
use web_sys::{DomException, IdbTransactionMode};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use crate::{
|
||||
crypto_store::{
|
||||
@@ -35,9 +37,10 @@ use crate::{
|
||||
};
|
||||
|
||||
/// Perform the schema upgrade v5 to v6, creating `inbound_group_sessions2`.
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 6, |db, _, _| {
|
||||
let object_store = db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V2)?;
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 6, |tx, _| {
|
||||
let db = tx.db();
|
||||
let object_store = db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V2).build()?;
|
||||
|
||||
add_nonunique_index(
|
||||
&object_store,
|
||||
@@ -55,25 +58,25 @@ pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer)
|
||||
let db = MigrationDb::new(name, 7).await?;
|
||||
|
||||
// The new store has been made for inbound group sessions; time to populate it.
|
||||
let txn = db.transaction_on_multi_with_mode(
|
||||
&[old_keys::INBOUND_GROUP_SESSIONS_V1, old_keys::INBOUND_GROUP_SESSIONS_V2],
|
||||
IdbTransactionMode::Readwrite,
|
||||
)?;
|
||||
let txn = db
|
||||
.transaction([old_keys::INBOUND_GROUP_SESSIONS_V1, old_keys::INBOUND_GROUP_SESSIONS_V2])
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()?;
|
||||
|
||||
let old_store = txn.object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
|
||||
let new_store = txn.object_store(old_keys::INBOUND_GROUP_SESSIONS_V2)?;
|
||||
|
||||
let row_count = old_store.count()?.await?;
|
||||
let row_count = old_store.count().await?;
|
||||
info!(row_count, "Migrating inbound group session data from v1 to v2");
|
||||
|
||||
if let Some(cursor) = old_store.open_cursor()?.await? {
|
||||
if let Some(mut cursor) = old_store.open_cursor().await? {
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
while let Some(value) = cursor.next_record::<JsValue>().await? {
|
||||
idx += 1;
|
||||
let key = cursor.key().ok_or(matrix_sdk_crypto::CryptoStoreError::Backend(
|
||||
"inbound_group_sessions v1 cursor has no key".into(),
|
||||
))?;
|
||||
let value = cursor.value();
|
||||
let key =
|
||||
cursor.key::<JsValue>()?.ok_or(matrix_sdk_crypto::CryptoStoreError::Backend(
|
||||
"inbound_group_sessions v1 cursor has no key".into(),
|
||||
))?;
|
||||
|
||||
if idx % 100 == 0 {
|
||||
debug!("Migrating session {idx} of {row_count}");
|
||||
@@ -88,14 +91,10 @@ pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer)
|
||||
needs_backup: !igs.backed_up(),
|
||||
})?;
|
||||
|
||||
new_store.add_key_val(&key, &new_data)?;
|
||||
new_store.add(&new_data).with_key(key).build()?;
|
||||
|
||||
// We are done with the original data, so delete it now.
|
||||
cursor.delete()?;
|
||||
|
||||
if !cursor.continue_cursor()?.await? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,13 +103,13 @@ pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer)
|
||||
// for more details.
|
||||
old_store.clear()?.await?;
|
||||
|
||||
Ok(txn.await.into_result()?)
|
||||
Ok(txn.commit().await?)
|
||||
}
|
||||
|
||||
/// Perform the schema upgrade v6 to v7, deleting `inbound_group_sessions`.
|
||||
pub(crate) async fn schema_delete(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 7, |db, _, _| {
|
||||
db.delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
|
||||
pub(crate) async fn schema_delete(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 7, |tx, _| {
|
||||
tx.db().delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
//! Migration code that modifies the data inside inbound_group_sessions2,
|
||||
//! ensuring that the keys are correctly encoded for this new store name.
|
||||
|
||||
use indexed_db_futures::IdbQuerySource;
|
||||
use indexed_db_futures::{
|
||||
error::OpenDbError, query_source::QuerySource, transaction::TransactionMode, Build,
|
||||
};
|
||||
use matrix_sdk_crypto::olm::InboundGroupSession;
|
||||
use tracing::{debug, info};
|
||||
use web_sys::{DomException, IdbTransactionMode};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use crate::{
|
||||
crypto_store::{
|
||||
@@ -36,32 +38,33 @@ use crate::{
|
||||
pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer) -> Result<()> {
|
||||
let db = MigrationDb::new(name, 8).await?;
|
||||
|
||||
let txn = db.transaction_on_one_with_mode(
|
||||
old_keys::INBOUND_GROUP_SESSIONS_V2,
|
||||
IdbTransactionMode::Readwrite,
|
||||
)?;
|
||||
let txn = db
|
||||
.transaction(old_keys::INBOUND_GROUP_SESSIONS_V2)
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()?;
|
||||
|
||||
let store = txn.object_store(old_keys::INBOUND_GROUP_SESSIONS_V2)?;
|
||||
|
||||
let row_count = store.count()?.await?;
|
||||
let row_count = store.count().await?;
|
||||
info!(row_count, "Fixing inbound group session data keys");
|
||||
|
||||
// Iterate through all rows
|
||||
if let Some(cursor) = store.open_cursor()?.await? {
|
||||
if let Some(mut cursor) = store.open_cursor().await? {
|
||||
let mut idx = 0;
|
||||
let mut updated = 0;
|
||||
let mut deleted = 0;
|
||||
loop {
|
||||
while let Some(value) = cursor.next_record::<JsValue>().await? {
|
||||
idx += 1;
|
||||
|
||||
// Get the old key and session
|
||||
|
||||
let old_key = cursor.key().ok_or(matrix_sdk_crypto::CryptoStoreError::Backend(
|
||||
"inbound_group_sessions2 cursor has no key".into(),
|
||||
))?;
|
||||
let old_key =
|
||||
cursor.key::<JsValue>()?.ok_or(matrix_sdk_crypto::CryptoStoreError::Backend(
|
||||
"inbound_group_sessions2 cursor has no key".into(),
|
||||
))?;
|
||||
|
||||
let idb_object: v7::InboundGroupSessionIndexedDbObject2 =
|
||||
serde_wasm_bindgen::from_value(cursor.value())?;
|
||||
serde_wasm_bindgen::from_value(value)?;
|
||||
let pickled_session =
|
||||
serializer.deserialize_value_from_bytes(&idb_object.pickled_session)?;
|
||||
let session = InboundGroupSession::from_pickle(pickled_session)
|
||||
@@ -86,37 +89,37 @@ pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer)
|
||||
cursor.delete()?;
|
||||
|
||||
// Check for an existing entry with the new key
|
||||
let new_value = store.get(&new_key)?.await?;
|
||||
let new_value = store.get::<JsValue, _, _>(&new_key).await?;
|
||||
|
||||
// If we found an existing entry, it is more up-to-date, so we don't need to do
|
||||
// anything more.
|
||||
|
||||
// If we didn't find an existing entry, we must create one with the correct key
|
||||
if new_value.is_none() {
|
||||
store.add_key_val(&new_key, &serde_wasm_bindgen::to_value(&idb_object)?)?;
|
||||
store
|
||||
.add(&serde_wasm_bindgen::to_value(&idb_object)?)
|
||||
.with_key(new_key)
|
||||
.build()?;
|
||||
updated += 1;
|
||||
} else {
|
||||
deleted += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !cursor.continue_cursor()?.await? {
|
||||
debug!(
|
||||
"Migrated {row_count} sessions: {updated} keys updated \
|
||||
and {deleted} obsolete entries deleted."
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Migrated {row_count} sessions: {updated} keys updated \
|
||||
and {deleted} obsolete entries deleted."
|
||||
);
|
||||
}
|
||||
|
||||
txn.await.into_result()?;
|
||||
txn.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform the schema upgrade v7 to v8, Just bumping the schema version.
|
||||
pub(crate) async fn schema_bump(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 8, |_, _, _| {
|
||||
pub(crate) async fn schema_bump(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 8, |_, _| {
|
||||
// Just bump the version number to 8 to demonstrate that we have run the data
|
||||
// changes from prepare_data_for_v8.
|
||||
Ok(())
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
//! Migration code that moves from inbound_group_sessions2 to
|
||||
//! inbound_group_sessions3, shrinking the values stored in each record.
|
||||
|
||||
use indexed_db_futures::IdbQuerySource;
|
||||
use indexed_db_futures::{
|
||||
error::OpenDbError, query_source::QuerySource, transaction::TransactionMode, Build,
|
||||
};
|
||||
use matrix_sdk_crypto::olm::InboundGroupSession;
|
||||
use tracing::{debug, info};
|
||||
use web_sys::{DomException, IdbTransactionMode};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use crate::{
|
||||
crypto_store::{
|
||||
@@ -34,9 +36,10 @@ use crate::{
|
||||
};
|
||||
|
||||
/// Perform the schema upgrade v8 to v9, creating `inbound_group_sessions3`.
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 9, |db, _, _| {
|
||||
let object_store = db.create_object_store(keys::INBOUND_GROUP_SESSIONS_V3)?;
|
||||
pub(crate) async fn schema_add(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 9, |tx, _| {
|
||||
let db = tx.db();
|
||||
let object_store = db.create_object_store(keys::INBOUND_GROUP_SESSIONS_V3).build()?;
|
||||
|
||||
add_nonunique_index(
|
||||
&object_store,
|
||||
@@ -62,21 +65,21 @@ pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
|
||||
pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer) -> Result<()> {
|
||||
let db = MigrationDb::new(name, 10).await?;
|
||||
|
||||
let txn = db.transaction_on_multi_with_mode(
|
||||
&[old_keys::INBOUND_GROUP_SESSIONS_V2, keys::INBOUND_GROUP_SESSIONS_V3],
|
||||
IdbTransactionMode::Readwrite,
|
||||
)?;
|
||||
let txn = db
|
||||
.transaction([old_keys::INBOUND_GROUP_SESSIONS_V2, keys::INBOUND_GROUP_SESSIONS_V3])
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()?;
|
||||
|
||||
let inbound_group_sessions2 = txn.object_store(old_keys::INBOUND_GROUP_SESSIONS_V2)?;
|
||||
let inbound_group_sessions3 = txn.object_store(keys::INBOUND_GROUP_SESSIONS_V3)?;
|
||||
|
||||
let row_count = inbound_group_sessions2.count()?.await?;
|
||||
let row_count = inbound_group_sessions2.count().await?;
|
||||
info!(row_count, "Shrinking inbound_group_session records");
|
||||
|
||||
// Iterate through all rows
|
||||
if let Some(cursor) = inbound_group_sessions2.open_cursor()?.await? {
|
||||
if let Some(mut cursor) = inbound_group_sessions2.open_cursor().await? {
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
while let Some(value) = cursor.next_record::<JsValue>().await? {
|
||||
idx += 1;
|
||||
|
||||
if idx % 100 == 0 {
|
||||
@@ -85,7 +88,7 @@ pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer)
|
||||
|
||||
// Deserialize the session from the old store
|
||||
let old_value: InboundGroupSessionIndexedDbObject2 =
|
||||
serde_wasm_bindgen::from_value(cursor.value())?;
|
||||
serde_wasm_bindgen::from_value(value)?;
|
||||
|
||||
let session = InboundGroupSession::from_pickle(
|
||||
serializer.deserialize_value_from_bytes(&old_value.pickled_session)?,
|
||||
@@ -104,17 +107,16 @@ pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer)
|
||||
|
||||
// Write it to the new store
|
||||
inbound_group_sessions3
|
||||
.add_key_val(&new_key, &serde_wasm_bindgen::to_value(&new_value)?)?;
|
||||
.add(&serde_wasm_bindgen::to_value(&new_value)?)
|
||||
.with_key(new_key)
|
||||
.build()?;
|
||||
|
||||
// We are done with the original data, so delete it now.
|
||||
cursor.delete()?;
|
||||
|
||||
// Continue to the next record, or stop if we're done
|
||||
if !cursor.continue_cursor()?.await? {
|
||||
debug!("Migrated {idx} sessions.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue to the next record, or stop if we're done
|
||||
debug!("Migrated {idx} sessions.");
|
||||
}
|
||||
|
||||
// We have finished with the old store. Clear it, since it is faster to
|
||||
@@ -122,14 +124,14 @@ pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer)
|
||||
// for more details.
|
||||
inbound_group_sessions2.clear()?.await?;
|
||||
|
||||
txn.await.into_result()?;
|
||||
txn.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform the schema upgrade v8 to v10, deleting `inbound_group_sessions2`.
|
||||
pub(crate) async fn schema_delete(name: &str) -> Result<(), DomException> {
|
||||
do_schema_upgrade(name, 10, |db, _, _| {
|
||||
db.delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V2)?;
|
||||
pub(crate) async fn schema_delete(name: &str) -> Result<(), OpenDbError> {
|
||||
do_schema_upgrade(name, 10, |tx, _| {
|
||||
tx.db().delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V2)?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,12 +12,66 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
#[cfg(feature = "event-cache-store")]
|
||||
use matrix_sdk_base::event_cache::store::EventCacheStoreError;
|
||||
#[cfg(feature = "media-store")]
|
||||
use matrix_sdk_base::media::store::MediaStoreError;
|
||||
#[cfg(feature = "state-store")]
|
||||
use matrix_sdk_base::StoreError;
|
||||
#[cfg(any(feature = "event-cache-store", feature = "media-store"))]
|
||||
use matrix_sdk_base::{SendOutsideWasm, SyncOutsideWasm};
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
use matrix_sdk_crypto::CryptoStoreError;
|
||||
use thiserror::Error;
|
||||
|
||||
/// A trait that combines the necessary traits needed for asynchronous runtimes,
|
||||
/// but excludes them when running in a web environment - i.e., when
|
||||
/// `#[cfg(target_family = "wasm")]`.
|
||||
#[cfg(any(feature = "event-cache-store", feature = "media-store"))]
|
||||
pub trait AsyncErrorDeps: std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static {}
|
||||
|
||||
#[cfg(any(feature = "event-cache-store", feature = "media-store"))]
|
||||
impl<T> AsyncErrorDeps for T where T: std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static
|
||||
{}
|
||||
|
||||
/// A wrapper around [`String`] that derives [`Error`](std::error::Error).
|
||||
/// This is useful when a particular error is not [`Send`] or [`Sync`] but
|
||||
/// must be mapped into a higher-level error that requires those constraints,
|
||||
/// e.g. [`StoreError::Backend`], [`CryptStoreError::Backend`], etc.
|
||||
#[derive(Debug, Error)]
|
||||
#[error("{0}")]
|
||||
pub struct GenericError(String);
|
||||
|
||||
impl<S: AsRef<str>> From<S> for GenericError {
|
||||
fn from(value: S) -> Self {
|
||||
Self(value.as_ref().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
impl From<GenericError> for CryptoStoreError {
|
||||
fn from(value: GenericError) -> Self {
|
||||
Self::backend(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "event-cache-store")]
|
||||
impl From<GenericError> for EventCacheStoreError {
|
||||
fn from(value: GenericError) -> Self {
|
||||
Self::backend(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "media-store")]
|
||||
impl From<GenericError> for MediaStoreError {
|
||||
fn from(value: GenericError) -> Self {
|
||||
Self::backend(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "state-store")]
|
||||
impl From<GenericError> for StoreError {
|
||||
fn from(value: GenericError) -> Self {
|
||||
Self::backend(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@ use matrix_sdk_base::event_cache::store::EventCacheStoreError;
|
||||
use serde::de::Error;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::transaction::TransactionError;
|
||||
use crate::{error::GenericError, transaction::TransactionError};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum IndexeddbEventCacheStoreError {
|
||||
#[error("unable to open database: {0}")]
|
||||
UnableToOpenDatabase(String),
|
||||
#[error("DomException {name} ({code}): {message}")]
|
||||
DomException { name: String, message: String, code: u16 },
|
||||
#[error("chunks contain disjoint lists")]
|
||||
@@ -44,11 +46,24 @@ impl From<web_sys::DomException> for IndexeddbEventCacheStoreError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<indexed_db_futures::error::OpenDbError> for IndexeddbEventCacheStoreError {
|
||||
fn from(value: indexed_db_futures::error::OpenDbError) -> Self {
|
||||
use indexed_db_futures::error::OpenDbError::*;
|
||||
match value {
|
||||
VersionZero | UnsupportedEnvironment | NullFactory => {
|
||||
Self::UnableToOpenDatabase(value.to_string())
|
||||
}
|
||||
Base(e) => TransactionError::from(e).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IndexeddbEventCacheStoreError> for EventCacheStoreError {
|
||||
fn from(value: IndexeddbEventCacheStoreError) -> Self {
|
||||
use IndexeddbEventCacheStoreError::*;
|
||||
|
||||
match value {
|
||||
UnableToOpenDatabase(e) => GenericError::from(e).into(),
|
||||
DomException { .. }
|
||||
| ChunksContainCycle
|
||||
| ChunksContainDisjointLists
|
||||
@@ -67,6 +82,7 @@ impl From<TransactionError> for EventCacheStoreError {
|
||||
DomException { .. } => Self::InvalidData { details: value.to_string() },
|
||||
Serialization(e) => Self::Serialization(serde_json::Error::custom(e.to_string())),
|
||||
ItemIsNotUnique | ItemNotFound => Self::InvalidData { details: value.to_string() },
|
||||
Backend(e) => GenericError::from(e.to_string()).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,10 @@
|
||||
// limitations under the License
|
||||
|
||||
use indexed_db_futures::{
|
||||
idb_object_store::IdbObjectStoreParameters, request::IdbOpenDbRequestLike, IdbDatabase,
|
||||
IdbVersionChangeEvent,
|
||||
database::Database,
|
||||
error::{DomException, Error, OpenDbError},
|
||||
};
|
||||
use thiserror::Error;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::{DomException, IdbIndexParameters};
|
||||
|
||||
/// The current version and keys used in the database.
|
||||
pub mod current {
|
||||
@@ -31,20 +29,20 @@ pub mod current {
|
||||
/// Opens a connection to the IndexedDB database and takes care of upgrading it
|
||||
/// if necessary.
|
||||
#[allow(unused)]
|
||||
pub async fn open_and_upgrade_db(name: &str) -> Result<IdbDatabase, DomException> {
|
||||
let mut request = IdbDatabase::open_u32(name, current::VERSION as u32)?;
|
||||
request.set_on_upgrade_needed(Some(|event: &IdbVersionChangeEvent| -> Result<(), JsValue> {
|
||||
let mut version =
|
||||
Version::try_from(event.old_version() as u32).map_err(DomException::from)?;
|
||||
while version < current::VERSION {
|
||||
version = match version.upgrade(event.db())? {
|
||||
Some(next) => next,
|
||||
None => current::VERSION, /* No more upgrades to apply, jump forward! */
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
request.await
|
||||
pub async fn open_and_upgrade_db(name: &str) -> Result<Database, OpenDbError> {
|
||||
Database::open(name)
|
||||
.with_version(current::VERSION as u32)
|
||||
.with_on_upgrade_needed(|event, transaction| {
|
||||
let mut version = Version::try_from(event.old_version() as u32)?;
|
||||
while version < current::VERSION {
|
||||
version = match version.upgrade(transaction.db())? {
|
||||
Some(next) => next,
|
||||
None => current::VERSION, /* No more upgrades to apply, jump forward! */
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Represents the version of the IndexedDB database.
|
||||
@@ -59,7 +57,7 @@ pub enum Version {
|
||||
|
||||
impl Version {
|
||||
/// Upgrade the database to the next version, if one exists.
|
||||
pub fn upgrade(self, db: &IdbDatabase) -> Result<Option<Self>, DomException> {
|
||||
pub fn upgrade(self, db: &Database) -> Result<Option<Self>, Error> {
|
||||
match self {
|
||||
Self::V0 => v0::upgrade(db).map(Some),
|
||||
Self::V1 => Ok(None),
|
||||
@@ -83,12 +81,12 @@ impl TryFrom<u32> for Version {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnknownVersionError> for DomException {
|
||||
impl From<UnknownVersionError> for Error {
|
||||
fn from(value: UnknownVersionError) -> Self {
|
||||
let message = format!("unknown version: {}", value.0);
|
||||
let name = "UnknownVersionError";
|
||||
match DomException::new_with_message_and_name(&message, name) {
|
||||
Ok(inner) => inner,
|
||||
match web_sys::DomException::new_with_message_and_name(&message, name) {
|
||||
Ok(inner) => Self::DomException(DomException::DataError(inner)),
|
||||
Err(err) => err.into(),
|
||||
}
|
||||
}
|
||||
@@ -98,13 +96,15 @@ pub mod v0 {
|
||||
use super::*;
|
||||
|
||||
/// Upgrade database from `v0` to `v1`
|
||||
pub fn upgrade(db: &IdbDatabase) -> Result<Version, DomException> {
|
||||
pub fn upgrade(db: &Database) -> Result<Version, Error> {
|
||||
v1::create_object_stores(db)?;
|
||||
Ok(Version::V1)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v1 {
|
||||
use indexed_db_futures::Build;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub mod keys {
|
||||
@@ -131,7 +131,7 @@ pub mod v1 {
|
||||
}
|
||||
|
||||
/// Create all object stores and indices for v1 database
|
||||
pub fn create_object_stores(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
pub fn create_object_stores(db: &Database) -> Result<(), Error> {
|
||||
create_lease_object_store(db)?;
|
||||
create_linked_chunks_object_store(db)?;
|
||||
create_events_object_store(db)?;
|
||||
@@ -140,10 +140,11 @@ pub mod v1 {
|
||||
}
|
||||
|
||||
/// Create an object store tracking leases on time-based locks
|
||||
fn create_lease_object_store(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
let mut object_store_params = IdbObjectStoreParameters::new();
|
||||
object_store_params.key_path(Some(&keys::LEASES_KEY_PATH.into()));
|
||||
let _ = db.create_object_store_with_params(keys::LEASES, &object_store_params)?;
|
||||
fn create_lease_object_store(db: &Database) -> Result<(), Error> {
|
||||
let _ = db
|
||||
.create_object_store(keys::LEASES)
|
||||
.with_key_path(keys::LEASES_KEY_PATH.into())
|
||||
.build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -151,13 +152,13 @@ pub mod v1 {
|
||||
///
|
||||
/// * Primary Key - `id`
|
||||
/// * Index - `is_last` - tracks the last chunk in linked chunks
|
||||
fn create_linked_chunks_object_store(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
let mut object_store_params = IdbObjectStoreParameters::new();
|
||||
object_store_params.key_path(Some(&keys::LINKED_CHUNKS_KEY_PATH.into()));
|
||||
let linked_chunks =
|
||||
db.create_object_store_with_params(keys::LINKED_CHUNKS, &object_store_params)?;
|
||||
linked_chunks
|
||||
.create_index(keys::LINKED_CHUNKS_NEXT, &keys::LINKED_CHUNKS_NEXT_KEY_PATH.into())?;
|
||||
fn create_linked_chunks_object_store(db: &Database) -> Result<(), Error> {
|
||||
let _ = db
|
||||
.create_object_store(keys::LINKED_CHUNKS)
|
||||
.with_key_path(keys::LINKED_CHUNKS_KEY_PATH.into())
|
||||
.build()?
|
||||
.create_index(keys::LINKED_CHUNKS_NEXT, keys::LINKED_CHUNKS_NEXT_KEY_PATH.into())
|
||||
.build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -169,39 +170,31 @@ pub mod v1 {
|
||||
/// chunks
|
||||
/// * Index - `relation` - tracks any event to which the given event is
|
||||
/// related
|
||||
fn create_events_object_store(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
let mut object_store_params = IdbObjectStoreParameters::new();
|
||||
object_store_params.key_path(Some(&keys::EVENTS_KEY_PATH.into()));
|
||||
let events = db.create_object_store_with_params(keys::EVENTS, &object_store_params)?;
|
||||
|
||||
let events_room_params = IdbIndexParameters::new();
|
||||
events_room_params.set_unique(true);
|
||||
events.create_index_with_params(
|
||||
keys::EVENTS_ROOM,
|
||||
&keys::EVENTS_ROOM_KEY_PATH.into(),
|
||||
&events_room_params,
|
||||
)?;
|
||||
|
||||
let events_position_params = IdbIndexParameters::new();
|
||||
events_position_params.set_unique(true);
|
||||
events.create_index_with_params(
|
||||
keys::EVENTS_POSITION,
|
||||
&keys::EVENTS_POSITION_KEY_PATH.into(),
|
||||
&events_position_params,
|
||||
)?;
|
||||
|
||||
events.create_index(keys::EVENTS_RELATION, &keys::EVENTS_RELATION_KEY_PATH.into())?;
|
||||
|
||||
fn create_events_object_store(db: &Database) -> Result<(), Error> {
|
||||
let events = db
|
||||
.create_object_store(keys::EVENTS)
|
||||
.with_key_path(keys::EVENTS_KEY_PATH.into())
|
||||
.build()?;
|
||||
let _ = events
|
||||
.create_index(keys::EVENTS_ROOM, keys::EVENTS_ROOM_KEY_PATH.into())
|
||||
.with_unique(true)
|
||||
.build()?;
|
||||
let _ = events
|
||||
.create_index(keys::EVENTS_POSITION, keys::EVENTS_POSITION_KEY_PATH.into())
|
||||
.with_unique(true)
|
||||
.build()?;
|
||||
let _ = events
|
||||
.create_index(keys::EVENTS_RELATION, keys::EVENTS_RELATION_KEY_PATH.into())
|
||||
.build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create an object store for tracking information about gaps.
|
||||
///
|
||||
/// * Primary Key - `id`
|
||||
fn create_gaps_object_store(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
let mut object_store_params = IdbObjectStoreParameters::new();
|
||||
object_store_params.key_path(Some(&keys::GAPS_KEY_PATH.into()));
|
||||
let _ = db.create_object_store_with_params(keys::GAPS, &object_store_params)?;
|
||||
fn create_gaps_object_store(db: &Database) -> Result<(), Error> {
|
||||
let _ =
|
||||
db.create_object_store(keys::GAPS).with_key_path(keys::GAPS_KEY_PATH.into()).build()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
use std::{rc::Rc, time::Duration};
|
||||
|
||||
use indexed_db_futures::IdbDatabase;
|
||||
use indexed_db_futures::{database::Database, Build};
|
||||
use matrix_sdk_base::{
|
||||
event_cache::{store::EventCacheStore, Event, Gap},
|
||||
linked_chunk::{
|
||||
@@ -61,7 +61,7 @@ pub use error::IndexeddbEventCacheStoreError;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IndexeddbEventCacheStore {
|
||||
// A handle to the IndexedDB database
|
||||
inner: Rc<IdbDatabase>,
|
||||
inner: Rc<Database>,
|
||||
// A serializer with functionality tailored to `IndexeddbEventCacheStore`
|
||||
serializer: IndexedTypeSerializer,
|
||||
}
|
||||
@@ -82,7 +82,11 @@ impl IndexeddbEventCacheStore {
|
||||
mode: IdbTransactionMode,
|
||||
) -> Result<IndexeddbEventCacheStoreTransaction<'a>, IndexeddbEventCacheStoreError> {
|
||||
Ok(IndexeddbEventCacheStoreTransaction::new(
|
||||
self.inner.transaction_on_multi_with_mode(stores, mode)?,
|
||||
self.inner
|
||||
.transaction(stores)
|
||||
.with_mode(mode)
|
||||
.build()
|
||||
.map_err(TransactionError::from)?,
|
||||
&self.serializer,
|
||||
))
|
||||
}
|
||||
@@ -120,7 +124,7 @@ impl EventCacheStore for IndexeddbEventCacheStore {
|
||||
expiration: now + Duration::from_millis(lease_duration_ms.into()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use indexed_db_futures::prelude::IdbTransaction;
|
||||
use indexed_db_futures::transaction as inner;
|
||||
use matrix_sdk_base::{
|
||||
event_cache::{Event as RawEvent, Gap as RawGap},
|
||||
linked_chunk::{ChunkContent, ChunkIdentifier, LinkedChunkId, RawChunk},
|
||||
@@ -54,7 +54,7 @@ impl<'a> Deref for IndexeddbEventCacheStoreTransaction<'a> {
|
||||
}
|
||||
|
||||
impl<'a> IndexeddbEventCacheStoreTransaction<'a> {
|
||||
pub fn new(transaction: IdbTransaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
|
||||
pub fn new(transaction: inner::Transaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
|
||||
Self { transaction: Transaction::new(transaction, serializer) }
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ use thiserror::Error;
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
mod crypto_store;
|
||||
#[cfg(any(feature = "event-cache-store", feature = "media-store"))]
|
||||
mod error;
|
||||
#[cfg(feature = "event-cache-store")]
|
||||
mod event_cache_store;
|
||||
|
||||
@@ -16,16 +16,16 @@ use matrix_sdk_base::media::store::{MediaStore, MediaStoreError, MemoryMediaStor
|
||||
use serde::de::Error;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::transaction::TransactionError;
|
||||
use crate::{error::GenericError, transaction::TransactionError};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum IndexeddbMediaStoreError {
|
||||
#[error("unable to open database: {0}")]
|
||||
UnableToOpenDatabase(String),
|
||||
#[error("media store: {0}")]
|
||||
MemoryStore(<MemoryMediaStore as MediaStore>::Error),
|
||||
|
||||
#[error("transaction: {0}")]
|
||||
Transaction(#[from] TransactionError),
|
||||
|
||||
#[error("DomException {name} ({code}): {message}")]
|
||||
DomException { name: String, message: String, code: u16 },
|
||||
}
|
||||
@@ -35,6 +35,7 @@ impl From<IndexeddbMediaStoreError> for MediaStoreError {
|
||||
use IndexeddbMediaStoreError::*;
|
||||
|
||||
match value {
|
||||
UnableToOpenDatabase(e) => GenericError::from(e).into(),
|
||||
DomException { .. } => Self::InvalidData { details: value.to_string() },
|
||||
Transaction(inner) => inner.into(),
|
||||
MemoryStore(error) => error,
|
||||
@@ -48,6 +49,18 @@ impl From<web_sys::DomException> for IndexeddbMediaStoreError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<indexed_db_futures::error::OpenDbError> for IndexeddbMediaStoreError {
|
||||
fn from(value: indexed_db_futures::error::OpenDbError) -> Self {
|
||||
use indexed_db_futures::error::OpenDbError::*;
|
||||
match value {
|
||||
VersionZero | UnsupportedEnvironment | NullFactory => {
|
||||
Self::UnableToOpenDatabase(value.to_string())
|
||||
}
|
||||
Base(e) => TransactionError::from(e).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionError> for MediaStoreError {
|
||||
fn from(value: TransactionError) -> Self {
|
||||
use TransactionError::*;
|
||||
@@ -56,6 +69,7 @@ impl From<TransactionError> for MediaStoreError {
|
||||
DomException { .. } => Self::InvalidData { details: value.to_string() },
|
||||
Serialization(e) => Self::Serialization(serde_json::Error::custom(e.to_string())),
|
||||
ItemIsNotUnique | ItemNotFound => Self::InvalidData { details: value.to_string() },
|
||||
Backend(e) => GenericError::from(e.to_string()).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,10 @@
|
||||
// limitations under the License
|
||||
|
||||
use indexed_db_futures::{
|
||||
idb_object_store::IdbObjectStoreParameters, request::IdbOpenDbRequestLike, IdbDatabase,
|
||||
IdbVersionChangeEvent,
|
||||
database::Database,
|
||||
error::{DomException, Error, OpenDbError},
|
||||
};
|
||||
use thiserror::Error;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::DomException;
|
||||
|
||||
/// The current version and keys used in the database.
|
||||
pub mod current {
|
||||
@@ -31,20 +29,20 @@ pub mod current {
|
||||
/// Opens a connection to the IndexedDB database and takes care of upgrading it
|
||||
/// if necessary.
|
||||
#[allow(unused)]
|
||||
pub async fn open_and_upgrade_db(name: &str) -> Result<IdbDatabase, DomException> {
|
||||
let mut request = IdbDatabase::open_u32(name, current::VERSION as u32)?;
|
||||
request.set_on_upgrade_needed(Some(|event: &IdbVersionChangeEvent| -> Result<(), JsValue> {
|
||||
let mut version =
|
||||
Version::try_from(event.old_version() as u32).map_err(DomException::from)?;
|
||||
while version < current::VERSION {
|
||||
version = match version.upgrade(event.db())? {
|
||||
Some(next) => next,
|
||||
None => current::VERSION, /* No more upgrades to apply, jump forward! */
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
request.await
|
||||
pub async fn open_and_upgrade_db(name: &str) -> Result<Database, OpenDbError> {
|
||||
Database::open(name)
|
||||
.with_version(current::VERSION as u32)
|
||||
.with_on_upgrade_needed(|event, transaction| {
|
||||
let mut version = Version::try_from(event.old_version() as u32)?;
|
||||
while version < current::VERSION {
|
||||
version = match version.upgrade(transaction.db())? {
|
||||
Some(next) => next,
|
||||
None => current::VERSION, /* No more upgrades to apply, jump forward! */
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Represents the version of the IndexedDB database.
|
||||
@@ -59,7 +57,7 @@ pub enum Version {
|
||||
|
||||
impl Version {
|
||||
/// Upgrade the database to the next version, if one exists.
|
||||
pub fn upgrade(self, db: &IdbDatabase) -> Result<Option<Self>, DomException> {
|
||||
pub fn upgrade(self, db: &Database) -> Result<Option<Self>, Error> {
|
||||
match self {
|
||||
Self::V0 => v0::upgrade(db).map(Some),
|
||||
Self::V1 => Ok(None),
|
||||
@@ -83,12 +81,12 @@ impl TryFrom<u32> for Version {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnknownVersionError> for DomException {
|
||||
impl From<UnknownVersionError> for Error {
|
||||
fn from(value: UnknownVersionError) -> Self {
|
||||
let message = format!("unknown version: {}", value.0);
|
||||
let name = "UnknownVersionError";
|
||||
match DomException::new_with_message_and_name(&message, name) {
|
||||
Ok(inner) => inner,
|
||||
match web_sys::DomException::new_with_message_and_name(&message, name) {
|
||||
Ok(inner) => Self::DomException(DomException::DataError(inner)),
|
||||
Err(err) => err.into(),
|
||||
}
|
||||
}
|
||||
@@ -98,13 +96,15 @@ pub mod v0 {
|
||||
use super::*;
|
||||
|
||||
/// Upgrade database from `v0` to `v1`
|
||||
pub fn upgrade(db: &IdbDatabase) -> Result<Version, DomException> {
|
||||
pub fn upgrade(db: &Database) -> Result<Version, Error> {
|
||||
v1::create_object_stores(db)?;
|
||||
Ok(Version::V1)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v1 {
|
||||
use indexed_db_futures::Build;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub mod keys {
|
||||
@@ -126,7 +126,7 @@ pub mod v1 {
|
||||
}
|
||||
|
||||
/// Create all object stores and indices for v1 database
|
||||
pub fn create_object_stores(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
pub fn create_object_stores(db: &Database) -> Result<(), Error> {
|
||||
create_core_object_store(db)?;
|
||||
create_lease_object_store(db)?;
|
||||
create_media_object_store(db)?;
|
||||
@@ -136,18 +136,18 @@ pub mod v1 {
|
||||
/// Create an object store for tracking miscellaneous information
|
||||
///
|
||||
/// * Primary Key - `id`
|
||||
fn create_core_object_store(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
let mut object_store_params = IdbObjectStoreParameters::new();
|
||||
object_store_params.key_path(Some(&keys::CORE_KEY_PATH.into()));
|
||||
let _ = db.create_object_store_with_params(keys::CORE, &object_store_params)?;
|
||||
fn create_core_object_store(db: &Database) -> Result<(), Error> {
|
||||
let _ =
|
||||
db.create_object_store(keys::CORE).with_key_path(keys::CORE_KEY_PATH.into()).build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create an object store tracking leases on time-based locks
|
||||
fn create_lease_object_store(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
let mut object_store_params = IdbObjectStoreParameters::new();
|
||||
object_store_params.key_path(Some(&keys::LEASES_KEY_PATH.into()));
|
||||
let _ = db.create_object_store_with_params(keys::LEASES, &object_store_params)?;
|
||||
fn create_lease_object_store(db: &Database) -> Result<(), Error> {
|
||||
let _ = db
|
||||
.create_object_store(keys::LEASES)
|
||||
.with_key_path(keys::LEASES_KEY_PATH.into())
|
||||
.build()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -164,17 +164,24 @@ pub mod v1 {
|
||||
///
|
||||
/// [1]: ruma::MxcUri
|
||||
/// [2]: matrix_sdk_base::media::store::MediaRetentionPolicy
|
||||
fn create_media_object_store(db: &IdbDatabase) -> Result<(), DomException> {
|
||||
let mut object_store_params = IdbObjectStoreParameters::new();
|
||||
object_store_params.key_path(Some(&keys::MEDIA_KEY_PATH.into()));
|
||||
let media = db.create_object_store_with_params(keys::MEDIA, &object_store_params)?;
|
||||
media.create_index(keys::MEDIA_URI, &keys::MEDIA_URI_KEY_PATH.into())?;
|
||||
media.create_index(keys::MEDIA_CONTENT_SIZE, &keys::MEDIA_CONTENT_SIZE_KEY_PATH.into())?;
|
||||
media.create_index(keys::MEDIA_LAST_ACCESS, &keys::MEDIA_LAST_ACCESS_KEY_PATH.into())?;
|
||||
media.create_index(
|
||||
keys::MEDIA_RETENTION_METADATA,
|
||||
&keys::MEDIA_RETENTION_METADATA_KEY_PATH.into(),
|
||||
)?;
|
||||
fn create_media_object_store(db: &Database) -> Result<(), Error> {
|
||||
let media = db
|
||||
.create_object_store(keys::MEDIA)
|
||||
.with_key_path(keys::MEDIA_KEY_PATH.into())
|
||||
.build()?;
|
||||
let _ = media.create_index(keys::MEDIA_URI, keys::MEDIA_URI_KEY_PATH.into()).build()?;
|
||||
let _ = media
|
||||
.create_index(keys::MEDIA_CONTENT_SIZE, keys::MEDIA_CONTENT_SIZE_KEY_PATH.into())
|
||||
.build()?;
|
||||
let _ = media
|
||||
.create_index(keys::MEDIA_LAST_ACCESS, keys::MEDIA_LAST_ACCESS_KEY_PATH.into())
|
||||
.build()?;
|
||||
let _ = media
|
||||
.create_index(
|
||||
keys::MEDIA_RETENTION_METADATA,
|
||||
keys::MEDIA_RETENTION_METADATA_KEY_PATH.into(),
|
||||
)
|
||||
.build()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ use std::{rc::Rc, time::Duration};
|
||||
|
||||
pub use builder::IndexeddbMediaStoreBuilder;
|
||||
pub use error::IndexeddbMediaStoreError;
|
||||
use indexed_db_futures::IdbDatabase;
|
||||
use indexed_db_futures::{database::Database, Build};
|
||||
use matrix_sdk_base::{
|
||||
media::{
|
||||
store::{
|
||||
@@ -49,6 +49,7 @@ use crate::{
|
||||
types::{Lease, Media, MediaMetadata},
|
||||
},
|
||||
serializer::{Indexed, IndexedTypeSerializer},
|
||||
transaction::TransactionError,
|
||||
};
|
||||
|
||||
/// A type for providing an IndexedDB implementation of [`MediaStore`][1].
|
||||
@@ -59,7 +60,7 @@ use crate::{
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IndexeddbMediaStore {
|
||||
// A handle to the IndexedDB database
|
||||
inner: Rc<IdbDatabase>,
|
||||
inner: Rc<Database>,
|
||||
// A serializer with functionality tailored to `IndexeddbMediaStore`
|
||||
serializer: IndexedTypeSerializer,
|
||||
// A service for conveniently delegating media-related queries to an `MediaStoreInner`
|
||||
@@ -89,7 +90,11 @@ impl IndexeddbMediaStore {
|
||||
mode: IdbTransactionMode,
|
||||
) -> Result<IndexeddbMediaStoreTransaction<'a>, IndexeddbMediaStoreError> {
|
||||
Ok(IndexeddbMediaStoreTransaction::new(
|
||||
self.inner.transaction_on_multi_with_mode(stores, mode)?,
|
||||
self.inner
|
||||
.transaction(stores)
|
||||
.with_mode(mode)
|
||||
.build()
|
||||
.map_err(TransactionError::from)?,
|
||||
&self.serializer,
|
||||
))
|
||||
}
|
||||
@@ -127,7 +132,7 @@ impl MediaStore for IndexeddbMediaStore {
|
||||
expiration: now + Duration::from_millis(lease_duration_ms.into()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use indexed_db_futures::prelude::IdbTransaction;
|
||||
use indexed_db_futures::transaction as inner;
|
||||
use matrix_sdk_base::media::{store::MediaRetentionPolicy, MediaRequestParameters};
|
||||
use ruma::MxcUri;
|
||||
|
||||
@@ -45,7 +45,7 @@ impl<'a> Deref for IndexeddbMediaStoreTransaction<'a> {
|
||||
}
|
||||
|
||||
impl<'a> IndexeddbMediaStoreTransaction<'a> {
|
||||
pub fn new(transaction: IdbTransaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
|
||||
pub fn new(transaction: inner::Transaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
|
||||
Self { transaction: Transaction::new(transaction, serializer) }
|
||||
}
|
||||
|
||||
|
||||
@@ -23,12 +23,12 @@ pub mod range;
|
||||
pub mod traits;
|
||||
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use indexed_db_futures::KeyRange;
|
||||
use range::IndexedKeyRange;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use thiserror::Error;
|
||||
use traits::{Indexed, IndexedKey};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::IdbKeyRange;
|
||||
|
||||
use crate::serializer::SafeEncodeSerializer;
|
||||
|
||||
@@ -99,21 +99,15 @@ impl IndexedTypeSerializer {
|
||||
///
|
||||
/// Note that the particular key which is encoded is defined by the type
|
||||
/// `K`.
|
||||
pub fn encode_key_range<T, K>(
|
||||
&self,
|
||||
range: impl Into<IndexedKeyRange<K>>,
|
||||
) -> Result<IdbKeyRange, serde_wasm_bindgen::Error>
|
||||
pub fn encode_key_range<T, K>(&self, range: impl Into<IndexedKeyRange<K>>) -> KeyRange<K>
|
||||
where
|
||||
T: Indexed,
|
||||
K: Serialize,
|
||||
{
|
||||
use serde_wasm_bindgen::to_value;
|
||||
Ok(match range.into() {
|
||||
IndexedKeyRange::Only(key) => IdbKeyRange::only(&to_value(&key)?)?,
|
||||
IndexedKeyRange::Bound(lower, upper) => {
|
||||
IdbKeyRange::bound(&to_value(&lower)?, &to_value(&upper)?)?
|
||||
}
|
||||
})
|
||||
match range.into() {
|
||||
IndexedKeyRange::Only(key) => KeyRange::Only(key),
|
||||
IndexedKeyRange::Bound(lower, upper) => KeyRange::Bound(lower, false, upper, false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes a key component range for an [`Indexed`] type.
|
||||
@@ -123,7 +117,7 @@ impl IndexedTypeSerializer {
|
||||
pub fn encode_key_component_range<'a, T, K>(
|
||||
&self,
|
||||
range: impl Into<IndexedKeyRange<K::KeyComponents<'a>>>,
|
||||
) -> Result<IdbKeyRange, serde_wasm_bindgen::Error>
|
||||
) -> KeyRange<K>
|
||||
where
|
||||
T: Indexed,
|
||||
K: IndexedKey<T> + Serialize,
|
||||
|
||||
@@ -5,6 +5,7 @@ use base64::{
|
||||
engine::{general_purpose, GeneralPurpose},
|
||||
Engine,
|
||||
};
|
||||
use indexed_db_futures::KeyRange;
|
||||
use matrix_sdk_store_encryption::StoreCipher;
|
||||
use ruma::{
|
||||
events::{
|
||||
@@ -14,7 +15,6 @@ use ruma::{
|
||||
UserId,
|
||||
};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::IdbKeyRange;
|
||||
|
||||
/// ASCII Group Separator, for elements in the keys
|
||||
pub const KEY_SEPARATOR: &str = "\u{001D}";
|
||||
@@ -53,26 +53,28 @@ pub trait SafeEncode {
|
||||
/// Encode self into a IdbKeyRange for searching all keys that are
|
||||
/// prefixed with this key, followed by `KEY_SEPARATOR`. Internally
|
||||
/// uses `as_encoded_string` to ensure the given key is escaped properly.
|
||||
fn encode_to_range(&self) -> Result<IdbKeyRange, String> {
|
||||
fn encode_to_range(&self) -> KeyRange<JsValue> {
|
||||
let key = self.as_encoded_string();
|
||||
IdbKeyRange::bound(
|
||||
&JsValue::from([&key, KEY_SEPARATOR].concat()),
|
||||
&JsValue::from([&key, RANGE_END].concat()),
|
||||
KeyRange::Bound(
|
||||
JsValue::from([&key, KEY_SEPARATOR].concat()),
|
||||
false,
|
||||
JsValue::from([&key, RANGE_END].concat()),
|
||||
false,
|
||||
)
|
||||
.map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned()))
|
||||
}
|
||||
|
||||
fn encode_to_range_secure(
|
||||
&self,
|
||||
table_name: &str,
|
||||
store_cipher: &StoreCipher,
|
||||
) -> Result<IdbKeyRange, String> {
|
||||
) -> KeyRange<JsValue> {
|
||||
let key = self.as_secure_string(table_name, store_cipher);
|
||||
IdbKeyRange::bound(
|
||||
&JsValue::from([&key, KEY_SEPARATOR].concat()),
|
||||
&JsValue::from([&key, RANGE_END].concat()),
|
||||
KeyRange::Bound(
|
||||
JsValue::from([&key, KEY_SEPARATOR].concat()),
|
||||
false,
|
||||
JsValue::from([&key, RANGE_END].concat()),
|
||||
false,
|
||||
)
|
||||
.map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ use base64::{
|
||||
Engine,
|
||||
};
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use indexed_db_futures::KeyRange;
|
||||
use matrix_sdk_crypto::CryptoStoreError;
|
||||
use matrix_sdk_store_encryption::{EncryptedValueBase64, StoreCipher};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::IdbKeyRange;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::serializer::safe_encode::traits::SafeEncode;
|
||||
@@ -146,11 +146,7 @@ impl SafeEncodeSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_to_range<T>(
|
||||
&self,
|
||||
table_name: &str,
|
||||
key: T,
|
||||
) -> Result<IdbKeyRange, SafeEncodeSerializerError>
|
||||
pub fn encode_to_range<T>(&self, table_name: &str, key: T) -> KeyRange<JsValue>
|
||||
where
|
||||
T: SafeEncode,
|
||||
{
|
||||
@@ -158,11 +154,6 @@ impl SafeEncodeSerializer {
|
||||
Some(cipher) => key.encode_to_range_secure(table_name, cipher),
|
||||
None => key.encode_to_range(),
|
||||
}
|
||||
.map_err(|e| SafeEncodeSerializerError::DomException {
|
||||
code: 0,
|
||||
name: "IdbKeyRangeMakeError".to_owned(),
|
||||
message: e,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encode the value for storage as a value in indexeddb.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -18,16 +18,19 @@
|
||||
// clean up any dead code.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use indexed_db_futures::{prelude::IdbTransaction, IdbQuerySource};
|
||||
use indexed_db_futures::{
|
||||
internals::SystemRepr, query_source::QuerySource, transaction as inner, BuildSerde,
|
||||
};
|
||||
use serde::{
|
||||
de::{DeserializeOwned, Error},
|
||||
Serialize,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::IdbCursorDirection;
|
||||
|
||||
use crate::{
|
||||
error::AsyncErrorDeps,
|
||||
error::{AsyncErrorDeps, GenericError},
|
||||
serializer::{Indexed, IndexedKey, IndexedKeyRange, IndexedTypeSerializer},
|
||||
};
|
||||
|
||||
@@ -41,6 +44,8 @@ pub enum TransactionError {
|
||||
ItemIsNotUnique,
|
||||
#[error("item not found")]
|
||||
ItemNotFound,
|
||||
#[error("backend: {0}")]
|
||||
Backend(Box<dyn AsyncErrorDeps>),
|
||||
}
|
||||
|
||||
impl From<web_sys::DomException> for TransactionError {
|
||||
@@ -55,16 +60,40 @@ impl From<serde_wasm_bindgen::Error> for TransactionError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<indexed_db_futures::error::SerialisationError> for TransactionError {
|
||||
fn from(e: indexed_db_futures::error::SerialisationError) -> Self {
|
||||
Self::Serialization(Box::new(serde_json::Error::custom(e.to_string())))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<indexed_db_futures::error::JSError> for TransactionError {
|
||||
fn from(value: indexed_db_futures::error::JSError) -> Self {
|
||||
Self::Backend(Box::new(GenericError::from(value.to_string())))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<indexed_db_futures::error::Error> for TransactionError {
|
||||
fn from(value: indexed_db_futures::error::Error) -> Self {
|
||||
use indexed_db_futures::error::Error;
|
||||
match value {
|
||||
Error::DomException(e) => e.into_sys().into(),
|
||||
Error::Serialisation(e) => e.into(),
|
||||
Error::MissingData(e) => Self::Backend(Box::new(e)),
|
||||
Error::Unknown(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an IndexedDB transaction, but provides a convenient interface for
|
||||
/// performing operations on types that implement [`Indexed`] and related
|
||||
/// traits.
|
||||
pub struct Transaction<'a> {
|
||||
transaction: IdbTransaction<'a>,
|
||||
transaction: inner::Transaction<'a>,
|
||||
serializer: &'a IndexedTypeSerializer,
|
||||
}
|
||||
|
||||
impl<'a> Transaction<'a> {
|
||||
pub fn new(transaction: IdbTransaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
|
||||
pub fn new(transaction: inner::Transaction<'a>, serializer: &'a IndexedTypeSerializer) -> Self {
|
||||
Self { transaction, serializer }
|
||||
}
|
||||
|
||||
@@ -75,13 +104,13 @@ impl<'a> Transaction<'a> {
|
||||
}
|
||||
|
||||
/// Returns the underlying IndexedDB transaction.
|
||||
pub fn into_inner(self) -> IdbTransaction<'a> {
|
||||
pub fn into_inner(self) -> inner::Transaction<'a> {
|
||||
self.transaction
|
||||
}
|
||||
|
||||
/// Commit all operations tracked in this transaction to IndexedDB.
|
||||
pub async fn commit(self) -> Result<(), TransactionError> {
|
||||
self.transaction.await.into_result().map_err(Into::into)
|
||||
self.transaction.commit().await.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Query IndexedDB for items that match the given key range
|
||||
@@ -95,18 +124,16 @@ impl<'a> Transaction<'a> {
|
||||
T::Error: AsyncErrorDeps,
|
||||
K: IndexedKey<T> + Serialize,
|
||||
{
|
||||
let range = self.serializer.encode_key_range::<T, K>(range)?;
|
||||
let range = self.serializer.encode_key_range::<T, K>(range);
|
||||
let object_store = self.transaction.object_store(T::OBJECT_STORE)?;
|
||||
let array = if let Some(index) = K::INDEX {
|
||||
object_store.index(index)?.get_all_with_key(&range)?.await?
|
||||
object_store.index(index)?.get_all().with_query(range).serde()?.await?
|
||||
} else {
|
||||
object_store.get_all_with_key(&range)?.await?
|
||||
object_store.get_all().with_query(range).serde()?.await?
|
||||
};
|
||||
let mut items = Vec::with_capacity(array.length() as usize);
|
||||
let mut items = Vec::with_capacity(array.len());
|
||||
for value in array {
|
||||
let item = self
|
||||
.serializer
|
||||
.deserialize(value)
|
||||
let item = T::from_indexed(value?, self.serializer.inner())
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)))?;
|
||||
items.push(item);
|
||||
}
|
||||
@@ -174,12 +201,12 @@ impl<'a> Transaction<'a> {
|
||||
T::Error: AsyncErrorDeps,
|
||||
K: IndexedKey<T> + Serialize,
|
||||
{
|
||||
let range = self.serializer.encode_key_range::<T, K>(range)?;
|
||||
let range = self.serializer.encode_key_range::<T, K>(range);
|
||||
let object_store = self.transaction.object_store(T::OBJECT_STORE)?;
|
||||
let count = if let Some(index) = K::INDEX {
|
||||
object_store.index(index)?.count_with_key(&range)?.await?
|
||||
object_store.index(index)?.count().with_query(range).serde()?.await?
|
||||
} else {
|
||||
object_store.count_with_key(&range)?.await?
|
||||
object_store.count().with_query(range).serde()?.await?
|
||||
};
|
||||
Ok(count as usize)
|
||||
}
|
||||
@@ -211,25 +238,30 @@ impl<'a> Transaction<'a> {
|
||||
T::Error: AsyncErrorDeps,
|
||||
K: IndexedKey<T> + Serialize,
|
||||
{
|
||||
let range = self.serializer.encode_key_range::<T, K>(range)?;
|
||||
let range = self.serializer.encode_key_range::<T, K>(range);
|
||||
let direction = IdbCursorDirection::Prev;
|
||||
let object_store = self.transaction.object_store(T::OBJECT_STORE)?;
|
||||
if let Some(index) = K::INDEX {
|
||||
object_store
|
||||
.index(index)?
|
||||
.open_cursor_with_range_and_direction(&range, direction)?
|
||||
.await?
|
||||
.map(|cursor| self.serializer.deserialize(cursor.value()))
|
||||
.transpose()
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)))
|
||||
} else {
|
||||
object_store
|
||||
.open_cursor_with_range_and_direction(&range, direction)?
|
||||
.await?
|
||||
.map(|cursor| self.serializer.deserialize(cursor.value()))
|
||||
.transpose()
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)))
|
||||
let index = object_store.index(index)?;
|
||||
if let Some(mut cursor) =
|
||||
index.open_cursor().with_query(range).with_direction(direction).serde()?.await?
|
||||
{
|
||||
if let Some(record) = cursor.next_record_ser().await? {
|
||||
return T::from_indexed(record, self.serializer.inner())
|
||||
.map(Some)
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)));
|
||||
}
|
||||
}
|
||||
} else if let Some(mut cursor) =
|
||||
object_store.open_cursor().with_query(range).with_direction(direction).serde()?.await?
|
||||
{
|
||||
if let Some(record) = cursor.next_record_ser().await? {
|
||||
return T::from_indexed(record, self.serializer.inner())
|
||||
.map(Some)
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Adds an item to the corresponding IndexedDB object
|
||||
@@ -243,11 +275,11 @@ impl<'a> Transaction<'a> {
|
||||
{
|
||||
self.transaction
|
||||
.object_store(T::OBJECT_STORE)?
|
||||
.add_val_owned(
|
||||
.add(
|
||||
self.serializer
|
||||
.serialize(item)
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)))?,
|
||||
)?
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@@ -263,11 +295,11 @@ impl<'a> Transaction<'a> {
|
||||
{
|
||||
self.transaction
|
||||
.object_store(T::OBJECT_STORE)?
|
||||
.put_val_owned(
|
||||
.put(
|
||||
self.serializer
|
||||
.serialize(item)
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)))?,
|
||||
)?
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@@ -291,7 +323,7 @@ impl<'a> Transaction<'a> {
|
||||
.serialize_if(item, f)
|
||||
.map_err(|e| TransactionError::Serialization(Box::new(e)))?;
|
||||
if let Some(value) = option {
|
||||
self.transaction.object_store(T::OBJECT_STORE)?.put_val_owned(value)?.await?;
|
||||
self.transaction.object_store(T::OBJECT_STORE)?.put(value).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -305,18 +337,20 @@ impl<'a> Transaction<'a> {
|
||||
T: Indexed,
|
||||
K: IndexedKey<T> + Serialize,
|
||||
{
|
||||
let range = self.serializer.encode_key_range::<T, K>(range)?;
|
||||
let range = self.serializer.encode_key_range::<T, K>(range);
|
||||
let object_store = self.transaction.object_store(T::OBJECT_STORE)?;
|
||||
if let Some(index) = K::INDEX {
|
||||
let index = object_store.index(index)?;
|
||||
if let Some(cursor) = index.open_cursor_with_range(&range)?.await? {
|
||||
while cursor.key().is_some() {
|
||||
cursor.delete()?.await?;
|
||||
cursor.continue_cursor()?.await?;
|
||||
if let Some(mut cursor) = index.open_cursor().with_query(range).serde()?.await? {
|
||||
loop {
|
||||
cursor.delete()?;
|
||||
if cursor.next_record::<JsValue>().await?.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
object_store.delete_owned(&range)?.await?;
|
||||
object_store.delete(range).serde()?.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -440,6 +440,10 @@ fn run_wasm_pack_tests(cmd: Option<WasmFeatureSet>) -> Result<()> {
|
||||
.env(WASM_TIMEOUT_ENV_KEY, WASM_TIMEOUT_VALUE)
|
||||
.run()?;
|
||||
cmd!(sh, "wasm-pack test --firefox --headless --")
|
||||
.args(arg_set.split_whitespace())
|
||||
.env(WASM_TIMEOUT_ENV_KEY, WASM_TIMEOUT_VALUE)
|
||||
.run()?;
|
||||
cmd!(sh, "wasm-pack test --chrome --headless --")
|
||||
.args(arg_set.split_whitespace())
|
||||
.env(WASM_TIMEOUT_ENV_KEY, WASM_TIMEOUT_VALUE)
|
||||
.run()
|
||||
|
||||
Reference in New Issue
Block a user