git » sdk » commit 8573f01

Implement get/storeOmemoMetadata

author Eric Roberts
2026-08-26 18:14:56 UTC
committer Stephen Paul Weber
2026-08-26 18:16:17 UTC
parent 50d17c29551e1a00ccdc3cebe3ad191002027d57

Implement get/storeOmemoMetadata

borogove/OMEMO.hx +1 -0
borogove/Persistence.hx +2 -2
borogove/persistence/Dummy.hx +5 -3
borogove/persistence/IDB.js +5 -6
borogove/persistence/Sqlite.hx +33 -3
test/TestSqlite.hx +33 -0
test/persistence-tests.ts +38 -0

diff --git a/borogove/OMEMO.hx b/borogove/OMEMO.hx
index f5c6e07..097cce0 100644
--- a/borogove/OMEMO.hx
+++ b/borogove/OMEMO.hx
@@ -313,6 +313,7 @@ class OMEMODecryptionResult {
 	}
 }
 
+@:expose
 class OMEMOSessionMetadata {
 	// True when we have successfully received and decrypted any
 	// non-prekey message from this session
diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx
index 894a06f..257ab17 100644
--- a/borogove/Persistence.hx
+++ b/borogove/Persistence.hx
@@ -425,11 +425,11 @@ interface Persistence {
 	/**
 		Store extra metadata associated with an OMEMO session
 	**/
-	public function storeOmemoMetadata(account:String, address:String, metadata:OMEMOSessionMetadata):Void;
+	public function storeOmemoMetadata(account:String, address:String, metadata:OMEMOSessionMetadata):Promise<OMEMOSessionMetadata>;
 
 	/**
 		Load stored metadata associated with an OMEMO session
 	**/
-	public function getOmemoMetadata(account:String, address:String): Promise<OMEMOSessionMetadata>;
+	public function getOmemoMetadata(account:String, address:String): Promise<Null<OMEMOSessionMetadata>>;
 #end
 }
diff --git a/borogove/persistence/Dummy.hx b/borogove/persistence/Dummy.hx
index a86d289..9c1bebd 100644
--- a/borogove/persistence/Dummy.hx
+++ b/borogove/persistence/Dummy.hx
@@ -269,11 +269,13 @@ class Dummy implements Persistence {
 	}
 
 	@HaxeCBridge.noemit
-	public function storeOmemoMetadata(account:String, address:String, metadata:OMEMOSessionMetadata):Void { }
+	public function storeOmemoMetadata(account:String, address:String, metadata:OMEMOSessionMetadata):Promise<OMEMOSessionMetadata> {
+		return Promise.resolve(metadata);
+	}
 
 	@HaxeCBridge.noemit
-	public function getOmemoMetadata(account:String, address:String): Promise<OMEMOSessionMetadata> {
-		return Promise.reject("Not found");
+	public function getOmemoMetadata(account:String, address:String): Promise<Null<OMEMOSessionMetadata>> {
+		return Promise.resolve(null);
 	}
 #end
 }
diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js
index f90bad4..c34c9fc 100644
--- a/borogove/persistence/IDB.js
+++ b/borogove/persistence/IDB.js
@@ -1472,23 +1472,22 @@ tx.onerror = console.error;
 			return session;
 		},
 
-		storeOmemoMetadata(account, address, metadata) {
+		async storeOmemoMetadata(account, address, metadata) {
 			const tx = db.transaction(["omemo_sessions_meta"], "readwrite");
 			const store = tx.objectStore("omemo_sessions_meta");
-			promisifyRequest(store.put({
+			await promisifyRequest(store.put({
 				account: account,
 				address: address,
 				metadata: metadata,
-			})).catch((e) => {
-				console.error("Failed to store OMEMO session metadata: " + e);
-			});
+			}));
+			return metadata;
 		},
 
 		async getOmemoMetadata(account, address) {
 			const tx = db.transaction(["omemo_sessions_meta"], "readonly");
 			const store = tx.objectStore("omemo_sessions_meta");
 			const result = await promisifyRequest(store.get([account, address]));
-			return result?.metadata;
+			return result?.metadata ?? null;
 		},
 
 		async removeOmemoSession(account, address) {
diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx
index d3809c2..73463e6 100644
--- a/borogove/persistence/Sqlite.hx
+++ b/borogove/persistence/Sqlite.hx
@@ -335,6 +335,19 @@ class Sqlite implements Persistence implements KeyValueStore {
 						"PRAGMA user_version = 19"]);
 					}
 					return Promise.resolve(null);
+				}).then(_ -> {
+					if (version < 20) {
+						return exec(["CREATE TABLE omemo_sessions_meta (
+							account_id TEXT NOT NULL,
+							address TEXT NOT NULL,
+							received_session_message_ok INTEGER NOT NULL,
+							last_message_decrypted_ok INTEGER NOT NULL,
+							sent_key_exchange INTEGER NOT NULL,
+							PRIMARY KEY (account_id, address)
+						) STRICT",
+						"PRAGMA user_version = 20"]);
+					}
+					return Promise.resolve(null);
 				});
 			});
 		});
@@ -1632,11 +1645,28 @@ class Sqlite implements Persistence implements KeyValueStore {
 	}
 
 	@HaxeCBridge.noemit
-	public function storeOmemoMetadata(account:String, address:String, metadata:OMEMOSessionMetadata):Void { }
+	public function storeOmemoMetadata(account:String, address:String, metadata:OMEMOSessionMetadata):Promise<OMEMOSessionMetadata> {
+		return db.exec(
+			"INSERT OR REPLACE INTO omemo_sessions_meta VALUES (?,?,?,?,?)",
+			[account, address, metadata.receivedSessionMessageOk, metadata.lastMessageDecryptedOk, metadata.sentKeyExchange],
+		).then(_ -> metadata);
+	}
 
 	@HaxeCBridge.noemit
-	public function getOmemoMetadata(account:String, address:String): Promise<OMEMOSessionMetadata> {
-		return Promise.reject("TODO");
+	public function getOmemoMetadata(account:String, address:String): Promise<Null<OMEMOSessionMetadata>> {
+		return db.exec(
+			"SELECT received_session_message_ok, last_message_decrypted_ok, sent_key_exchange FROM omemo_sessions_meta WHERE account_id=? AND address=? LIMIT 1",
+			[account, address],
+		).then(result -> {
+			for (row in result) {
+				return new OMEMOSessionMetadata(
+					row.received_session_message_ok == 1,
+					row.last_message_decrypted_ok == 1,
+					row.sent_key_exchange == 1,
+				);
+			}
+			return null;
+		});
 	}
 #end
 }
diff --git a/test/TestSqlite.hx b/test/TestSqlite.hx
index e3b2211..daf01fe 100644
--- a/test/TestSqlite.hx
+++ b/test/TestSqlite.hx
@@ -1527,6 +1527,39 @@ class TestSqlite extends utest.Test {
 			});
 	}
 
+	public function testGetOmemoMetadataNotFound(async: Async) {
+		persistence
+			.getOmemoMetadata("metadata-notfound@example.com", "contact@example.com/1")
+			.then(result -> {
+				Assert.equals(null, result);
+				async.done();
+			})
+			.catchError(e -> {
+				Assert.fail(Std.string(e));
+				async.done();
+			});
+	}
+
+	public function testOmemoMetadata(async: Async) {
+		final account = "metadata-existing@example.com";
+		final address = "contact@example.com/1";
+		final metadata = new OMEMOSessionMetadata(true, false, true);
+
+		persistence
+			.storeOmemoMetadata(account, address, metadata)
+			.then(_ -> persistence.getOmemoMetadata(account, address))
+			.then(result -> {
+				Assert.equals(metadata.receivedSessionMessageOk, result.receivedSessionMessageOk);
+				Assert.equals(metadata.lastMessageDecryptedOk, result.lastMessageDecryptedOk);
+				Assert.equals(metadata.sentKeyExchange, result.sentKeyExchange);
+				async.done();
+			})
+			.catchError(e -> {
+				Assert.fail(Std.string(e));
+				async.done();
+			});
+	}
+
 	public function testOmemoSession(async: Async) {
 		final account = "session-existing@example.com";
 		final address = "contact@example.com/1";
diff --git a/test/persistence-tests.ts b/test/persistence-tests.ts
index 7c8257f..a1c7061 100644
--- a/test/persistence-tests.ts
+++ b/test/persistence-tests.ts
@@ -1782,6 +1782,44 @@ export function sharedPersistenceTests(test: PersistenceTest) {
 		expect(result).toBeNull();
 	});
 
+	test("getOmemoMetadata returns null when none is stored", async ({
+		page,
+		persistence,
+	}) => {
+		const result = await page.evaluate(
+			async ({ persistence }) =>
+				persistence.getOmemoMetadata(
+					"omemo-metadata-not-found@example.com",
+					"contact@example.com/1",
+				),
+			{ persistence },
+		);
+
+		expect(result).toBeNull();
+	});
+
+	test("storeOmemoMetadata and getOmemoMetadata", async ({
+		page,
+		persistence,
+	}) => {
+		const account = "omemo-metadata-existing@example.com";
+		const address = "contact@example.com/1";
+		const metadata = {
+			receivedSessionMessageOk: true,
+			lastMessageDecryptedOk: false,
+			sentKeyExchange: true,
+		};
+		const result = await page.evaluate(
+			async ({ persistence, account, address, metadata }) => {
+				await persistence.storeOmemoMetadata(account, address, metadata);
+				return persistence.getOmemoMetadata(account, address);
+			},
+			{ persistence, account, address, metadata },
+		);
+
+		expect(result).toEqual(metadata);
+	});
+
 	test("storeOmemoSession, getOmemoSession, and removeOmemoSession", async ({
 		page,
 		persistence,