git » sdk » commit b2f33f2

Implement get/storeOmemoDeviceList

author Eric Roberts
2026-08-26 18:14:48 UTC
committer Stephen Paul Weber
2026-08-26 18:16:17 UTC
parent 959b45252e716145858b695c8a9c790e7c8cf491

Implement get/storeOmemoDeviceList

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

diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx
index 03d7834..4ea7dec 100644
--- a/borogove/Persistence.hx
+++ b/borogove/Persistence.hx
@@ -365,7 +365,7 @@ interface Persistence {
 	/**
 		Store the known OMEMO device list for a contact or account
 	**/
-	public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Void;
+	public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Promise<Array<Int>>;
 
 	/**
 		Store an OMEMO pre-key
diff --git a/borogove/persistence/Dummy.hx b/borogove/persistence/Dummy.hx
index 46c56ab..bed3908 100644
--- a/borogove/persistence/Dummy.hx
+++ b/borogove/persistence/Dummy.hx
@@ -209,7 +209,9 @@ class Dummy implements Persistence {
 	}
 
 	@HaxeCBridge.noemit
-	public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Void { }
+	public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Promise<Array<Int>> {
+		return Promise.resolve(deviceIds);
+	}
 
 	@HaxeCBridge.noemit
 	public function storeOmemoPreKey(identifier:String, keyId:Int, keyPair:PreKeyPair):Void { }
diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js
index 07ae61b..90b3c62 100644
--- a/borogove/persistence/IDB.js
+++ b/borogove/persistence/IDB.js
@@ -1157,15 +1157,17 @@ tx.onerror = console.error;
 			return keypair;
 		},
 
-		storeOmemoDeviceList(chatId, deviceIds) {
+		async storeOmemoDeviceList(chatId, deviceIds) {
 			const tx = db.transaction(["keyvaluepairs"], "readwrite");
 			const store = tx.objectStore("keyvaluepairs");
 			const key = "omemo:devices:"+chatId;
 			if(deviceIds.length>0) {
-				store.put(deviceIds, key);
+				await promisifyRequest(store.put(deviceIds, key));
 			} else {
-				store.delete(key);
+				await promisifyRequest(store.delete(key));
 			}
+
+			return deviceIds;
 		},
 
 		async getOmemoDeviceList(chatId) {
diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx
index 38807c0..5016c94 100644
--- a/borogove/persistence/Sqlite.hx
+++ b/borogove/persistence/Sqlite.hx
@@ -278,6 +278,16 @@ class Sqlite implements Persistence implements KeyValueStore {
 						"PRAGMA user_version = 14"]);
 					}
 					return Promise.resolve(null);
+				}).then(_ -> {
+					if (version < 15) {
+						return exec(["CREATE TABLE omemo_devices (
+							identifier TEXT NOT NULL,
+							device_id INTEGER NOT NULL,
+							PRIMARY KEY (identifier, device_id)
+						) STRICT",
+						"PRAGMA user_version = 15"]);
+					}
+					return Promise.resolve(null);
 				});
 			});
 		});
@@ -1421,11 +1431,34 @@ class Sqlite implements Persistence implements KeyValueStore {
 
 	@HaxeCBridge.noemit
 	public function getOmemoDeviceList(identifier:String): Promise<Array<Int>> {
-		return Promise.resolve([]);
+		return db.exec(
+			"SELECT device_id FROM omemo_devices WHERE identifier=?",
+			[identifier],
+		).then(result -> {
+			return { iterator: () -> result }.map(row -> row.device_id).array();
+		});
 	}
 
 	@HaxeCBridge.noemit
-	public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Void { }
+	public function storeOmemoDeviceList(identifier:String, deviceIds:Array<Int>):Promise<Array<Int>> {
+		return db.exec(
+			"DELETE FROM omemo_devices WHERE identifier=?",
+			[identifier],
+		).then(_ -> {
+			if (deviceIds.length == 0) {
+				return Promise.resolve(deviceIds);
+			}
+
+			final placeholders = deviceIds.map(_ -> "(?,?)").join(", ");
+			final params = deviceIds.flatMap(
+				(deviceId) -> ([identifier, deviceId] : Array<Dynamic>)
+			);
+			db.exec(
+				"INSERT INTO omemo_devices (identifier, device_id) VALUES " + placeholders,
+				params,
+			).then(_ -> deviceIds);
+		});
+	}
 
 	@HaxeCBridge.noemit
 	public function storeOmemoPreKey(identifier:String, keyId:Int, keyPair:PreKeyPair):Void { }
diff --git a/test/TestSqlite.hx b/test/TestSqlite.hx
index d9a9fd3..6db1098 100644
--- a/test/TestSqlite.hx
+++ b/test/TestSqlite.hx
@@ -1366,4 +1366,42 @@ class TestSqlite extends utest.Test {
 			});
 	}
 
+	public function testGetOmemoDeviceListNotFound(async: Async) {
+		persistence
+			.getOmemoDeviceList("devices-notfound@example.com")
+			.then(result -> {
+				Assert.same([], result);
+				async.done();
+			})
+			.catchError(e -> {
+				Assert.fail(Std.string(e));
+				async.done();
+			});
+	}
+
+	public function testOmemoDeviceList(async: Async) {
+		final identifier = "devices-existing@example.com";
+		persistence
+			.storeOmemoDeviceList(identifier, [1, 2, 3])
+			.then(_ -> persistence.getOmemoDeviceList(identifier))
+			.then(initial -> {
+				Assert.same([1, 2, 3], initial);
+				return persistence.storeOmemoDeviceList(identifier, [4, 5]);
+			})
+			.then(_ -> persistence.getOmemoDeviceList(identifier))
+			.then(replaced -> {
+				Assert.same([4, 5], replaced);
+				return persistence.storeOmemoDeviceList(identifier, []);
+			})
+			.then(_ -> persistence.getOmemoDeviceList(identifier))
+			.then(cleared -> {
+				Assert.same([], cleared);
+				async.done();
+			})
+			.catchError(e -> {
+				Assert.fail(Std.string(e));
+				async.done();
+			});
+	}
+
 }
diff --git a/test/persistence-tests.ts b/test/persistence-tests.ts
index 8e74c03..61387af 100644
--- a/test/persistence-tests.ts
+++ b/test/persistence-tests.ts
@@ -1542,4 +1542,58 @@ export function sharedPersistenceTests(test: PersistenceTest) {
 		expect(result.loadedPrivKey).toEqual([0, 1, 2, 127, 128, 255]);
 		expect(result.loadedPubKey).toEqual([255, 128, 127, 2, 1, 0]);
 	});
+
+	test("getOmemoDeviceList returns an empty list when none is stored", async ({
+		page,
+		persistence,
+	}) => {
+		const result = await page.evaluate(
+			async (persistence) =>
+				persistence.getOmemoDeviceList(
+					"omemo-devices-not-found@example.com",
+				),
+			persistence,
+		);
+
+		expect(result).toEqual([]);
+	});
+
+	test("storeOmemoDeviceList replaces and clears the device list", async ({
+		page,
+		persistence,
+	}) => {
+		const identifier = "omemo-devices-existing@example.com";
+		const initialDeviceIds = [12345, 67890];
+		const replacementDeviceIds = [24680];
+		const result = await page.evaluate(
+			async ({
+				persistence,
+				identifier,
+				initialDeviceIds,
+				replacementDeviceIds,
+			}) => {
+				await persistence.storeOmemoDeviceList(identifier, initialDeviceIds);
+				const initial = await persistence.getOmemoDeviceList(identifier);
+				await persistence.storeOmemoDeviceList(
+					identifier,
+					replacementDeviceIds,
+				);
+				const afterReplace = await persistence.getOmemoDeviceList(identifier);
+				await persistence.storeOmemoDeviceList(identifier, []);
+				const afterClear = await persistence.getOmemoDeviceList(identifier);
+
+				return { initial, afterReplace, afterClear };
+			},
+			{
+				persistence,
+				identifier,
+				initialDeviceIds,
+				replacementDeviceIds,
+			},
+		);
+
+		expect(result.initial).toEqual(initialDeviceIds);
+		expect(result.afterReplace).toEqual(replacementDeviceIds);
+		expect(result.afterClear).toEqual([]);
+	});
 }