Skip to content

Commit be88e0a

Browse files
committed
feat(event cache store): Implement renaming media keys
1 parent 50473ba commit be88e0a

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed

crates/matrix-sdk-base/src/event_cache_store/integration_tests.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ use crate::media::{MediaFormat, MediaRequest, MediaThumbnailSettings};
3131
pub trait EventCacheStoreIntegrationTests {
3232
/// Test media content storage.
3333
async fn test_media_content(&self);
34+
35+
/// Test replacing a MXID.
36+
async fn test_replace_media_key(&self);
3437
}
3538

3639
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -139,6 +142,42 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore {
139142
"other media was removed"
140143
);
141144
}
145+
146+
async fn test_replace_media_key(&self) {
147+
let uri = mxc_uri!("mxc://sendqueue.local/tr4n-s4ct-10n1-d");
148+
let req =
149+
MediaRequest { source: MediaSource::Plain(uri.to_owned()), format: MediaFormat::File };
150+
151+
let content = "hello".as_bytes().to_owned();
152+
153+
// Media isn't present in the cache.
154+
assert!(self.get_media_content(&req).await.unwrap().is_none(), "unexpected media found");
155+
156+
// Add the media.
157+
self.add_media_content(&req, content.clone()).await.expect("adding media failed");
158+
159+
// Sanity-check: media is found after adding it.
160+
assert_eq!(self.get_media_content(&req).await.unwrap().unwrap(), b"hello");
161+
162+
// Replacing a media request works.
163+
let new_uri = mxc_uri!("mxc://matrix.org/tr4n-s4ct-10n1-d");
164+
let new_req = MediaRequest {
165+
source: MediaSource::Plain(new_uri.to_owned()),
166+
format: MediaFormat::File,
167+
};
168+
self.replace_media_key(&req, &new_req)
169+
.await
170+
.expect("replacing the media request key failed");
171+
172+
// Finding with the previous request doesn't work anymore.
173+
assert!(
174+
self.get_media_content(&req).await.unwrap().is_none(),
175+
"unexpected media found with the old key"
176+
);
177+
178+
// Finding with the new request does work.
179+
assert_eq!(self.get_media_content(&new_req).await.unwrap().unwrap(), b"hello");
180+
}
142181
}
143182

144183
/// Macro building to allow your `EventCacheStore` implementation to run the
@@ -184,6 +223,13 @@ macro_rules! event_cache_store_integration_tests {
184223
get_event_cache_store().await.unwrap().into_event_cache_store();
185224
event_cache_store.test_media_content().await;
186225
}
226+
227+
#[async_test]
228+
async fn test_replace_media_key() {
229+
let event_cache_store =
230+
get_event_cache_store().await.unwrap().into_event_cache_store();
231+
event_cache_store.test_replace_media_key().await;
232+
}
187233
}
188234
};
189235
}

crates/matrix-sdk-base/src/event_cache_store/memory_store.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,35 @@ impl EventCacheStore for MemoryStore {
6060
Ok(())
6161
}
6262

63+
async fn replace_media_key(
64+
&self,
65+
from: &MediaRequest,
66+
to: &MediaRequest,
67+
) -> Result<(), Self::Error> {
68+
let expected_key = from.unique_key();
69+
70+
let mut medias = self.media.write().unwrap();
71+
if let Some((mxc, key, _)) = medias.iter_mut().find(|(_, key, _)| *key == expected_key) {
72+
*mxc = to.uri().to_owned();
73+
*key = to.unique_key();
74+
}
75+
76+
Ok(())
77+
}
78+
6379
async fn get_media_content(&self, request: &MediaRequest) -> Result<Option<Vec<u8>>> {
64-
let media = self.media.read().unwrap();
6580
let expected_key = request.unique_key();
6681

82+
let media = self.media.read().unwrap();
6783
Ok(media.iter().find_map(|(_media_uri, media_key, media_content)| {
6884
(media_key == &expected_key).then(|| media_content.to_owned())
6985
}))
7086
}
7187

7288
async fn remove_media_content(&self, request: &MediaRequest) -> Result<()> {
73-
let mut media = self.media.write().unwrap();
7489
let expected_key = request.unique_key();
90+
91+
let mut media = self.media.write().unwrap();
7592
let Some(index) = media
7693
.iter()
7794
.position(|(_media_uri, media_key, _media_content)| media_key == &expected_key)

crates/matrix-sdk-base/src/event_cache_store/traits.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ pub trait EventCacheStore: AsyncTraitDeps {
4242
content: Vec<u8>,
4343
) -> Result<(), Self::Error>;
4444

45+
/// Replaces the given media's content key with another one.
46+
///
47+
/// This should be used whenever a temporary (local) MXID has been used, and
48+
/// it must now be replaced with its actual remote counterpart (after
49+
/// uploading some content, or creating an empty MXC URI).
50+
///
51+
/// ⚠ No check is performed to ensure that the media formats are consistent,
52+
/// i.e. it's possible to update with a thumbnail key a media that was
53+
/// keyed as a file before. The caller is responsible of ensuring that
54+
/// the replacement makes sense, according to their use case.
55+
///
56+
/// # Arguments
57+
///
58+
/// * `from` - The previous `MediaRequest` of the file.
59+
///
60+
/// * `to` - The new `MediaRequest` of the file.
61+
async fn replace_media_key(
62+
&self,
63+
from: &MediaRequest,
64+
to: &MediaRequest,
65+
) -> Result<(), Self::Error>;
66+
4567
/// Get a media file's content out of the media store.
4668
///
4769
/// # Arguments
@@ -91,6 +113,14 @@ impl<T: EventCacheStore> EventCacheStore for EraseEventCacheStoreError<T> {
91113
self.0.add_media_content(request, content).await.map_err(Into::into)
92114
}
93115

116+
async fn replace_media_key(
117+
&self,
118+
from: &MediaRequest,
119+
to: &MediaRequest,
120+
) -> Result<(), Self::Error> {
121+
self.0.replace_media_key(from, to).await.map_err(Into::into)
122+
}
123+
94124
async fn get_media_content(
95125
&self,
96126
request: &MediaRequest,

crates/matrix-sdk-sqlite/src/event_cache_store.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ impl EventCacheStore for SqliteEventCacheStore {
155155
Ok(())
156156
}
157157

158+
async fn replace_media_key(
159+
&self,
160+
from: &MediaRequest,
161+
to: &MediaRequest,
162+
) -> Result<(), Self::Error> {
163+
let prev_uri = self.encode_key(keys::MEDIA, from.source.unique_key());
164+
let prev_format = self.encode_key(keys::MEDIA, from.format.unique_key());
165+
166+
let new_uri = self.encode_key(keys::MEDIA, to.source.unique_key());
167+
let new_format = self.encode_key(keys::MEDIA, to.format.unique_key());
168+
169+
let conn = self.acquire().await?;
170+
conn.execute(
171+
r#"UPDATE media SET uri = ?, format = ?, last_access = CAST(strftime('%s') as INT)
172+
WHERE uri = ? AND format = ?"#,
173+
(new_uri, new_format, prev_uri, prev_format),
174+
)
175+
.await?;
176+
177+
Ok(())
178+
}
179+
158180
async fn get_media_content(&self, request: &MediaRequest) -> Result<Option<Vec<u8>>> {
159181
let uri = self.encode_key(keys::MEDIA, request.source.unique_key());
160182
let format = self.encode_key(keys::MEDIA, request.format.unique_key());

0 commit comments

Comments
 (0)