From 936d6920273323d98ebdf039b7a6d816b7fd98c0 Mon Sep 17 00:00:00 2001 From: Cedrick Cooke Date: Wed, 16 Nov 2022 10:45:00 -0800 Subject: [PATCH] #78: add support for out-of-line keys (#80) --- .gitignore | 1 + core/src/jsMain/kotlin/Transaction.kt | 64 ++++++++++++++++--- .../kotlin/AutoIncrementKeyObjectStore.kt | 30 +++++++++ .../src/jsTest/kotlin/InLineKeyObjectStore.kt | 30 +++++++++ .../jsTest/kotlin/OutOfLineKeyObjectStore.kt | 30 +++++++++ external/src/jsMain/kotlin/IDBDatabase.kt | 1 + 6 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 core/src/jsTest/kotlin/AutoIncrementKeyObjectStore.kt create mode 100644 core/src/jsTest/kotlin/InLineKeyObjectStore.kt create mode 100644 core/src/jsTest/kotlin/OutOfLineKeyObjectStore.kt diff --git a/.gitignore b/.gitignore index 9e83621..7f5ffc8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build/ .DS_Store local.properties +yarn.lock diff --git a/core/src/jsMain/kotlin/Transaction.kt b/core/src/jsMain/kotlin/Transaction.kt index 494c835..baeac7f 100644 --- a/core/src/jsMain/kotlin/Transaction.kt +++ b/core/src/jsMain/kotlin/Transaction.kt @@ -158,37 +158,78 @@ public open class WriteTransaction internal constructor( ) : Transaction(transaction) { /** - * Adds a new item to the database. If an item with the same key already exists, this will fail. + * Adds a new item to the database using an in-line or auto-incrementing key. If an item with the same + * key already exists, this will fail. * * This API is delicate. If you're passing in Kotlin objects directly, you're probably doing it wrong. * * Generally, you'll want to create an explicit `external interface` and pass that in, to guarantee that Kotlin * doesn't mangle, prefix, or otherwise mess with your field names. */ - public suspend fun ObjectStore.add(item: dynamic) { + public suspend fun ObjectStore.add(item: dynamic): dynamic { val request = objectStore.add(item) - request.onNextEvent("success", "error") { event -> + return request.onNextEvent("success", "error") { event -> when (event.type) { "error" -> throw ErrorEventException(event) - else -> Unit + else -> request.result + } + } + } + + /** + * Adds a new item to the database using an explicit out-of-line key. If an item with the same key already + * exists, this will fail. + * + * This API is delicate. If you're passing in Kotlin objects directly, you're probably doing it wrong. + * + * Generally, you'll want to create an explicit `external interface` and pass that in, to guarantee that Kotlin + * doesn't mangle, prefix, or otherwise mess with your field names. + */ + public suspend fun ObjectStore.add(item: dynamic, key: Key): dynamic { + val request = objectStore.add(item, key.toJs()) + return request.onNextEvent("success", "error") { event -> + when (event.type) { + "error" -> throw ErrorEventException(event) + else -> request.result } } } /** - * Adds an item to, or updates an item in, the database. If an item with the same key already exists, this will replace that item. + * Adds an item to or updates an item in the database using an in-line or auto-incrementing key. If an item + * with the same key already exists, this will replace that item. Note that with auto-incrementing keys a new + * item will always be inserted. * * This API is delicate. If you're passing in Kotlin objects directly, you're probably doing it wrong. * * Generally, you'll want to create an explicit `external interface` and pass that in, to guarantee that Kotlin * doesn't mangle, prefix, or otherwise mess with your field names. */ - public suspend fun ObjectStore.put(item: dynamic) { + public suspend fun ObjectStore.put(item: dynamic): dynamic { val request = objectStore.put(item) - request.onNextEvent("success", "error") { event -> + return request.onNextEvent("success", "error") { event -> when (event.type) { "error" -> throw ErrorEventException(event) - else -> Unit + else -> request.result + } + } + } + + /** + * Adds an item to or updates an item in the database using an explicit out-of-line key. If an item with the + * same key already exists, this will replace that item. + * + * This API is delicate. If you're passing in Kotlin objects directly, you're probably doing it wrong. + * + * Generally, you'll want to create an explicit `external interface` and pass that in, to guarantee that Kotlin + * doesn't mangle, prefix, or otherwise mess with your field names. + */ + public suspend fun ObjectStore.put(item: dynamic, key: Key): dynamic { + val request = objectStore.put(item, key.toJs()) + return request.onNextEvent("success", "error") { event -> + when (event.type) { + "error" -> throw ErrorEventException(event) + else -> request.result } } } @@ -237,9 +278,16 @@ public open class WriteTransaction internal constructor( public class VersionChangeTransaction internal constructor( transaction: IDBTransaction, ) : WriteTransaction(transaction) { + + /** Creates an object-store that uses explicit out-of-line keys. */ + public fun Database.createObjectStore(name: String): ObjectStore = + ObjectStore(database.createObjectStore(name)) + + /** Creates an object-store that uses in-line keys. */ public fun Database.createObjectStore(name: String, keyPath: KeyPath): ObjectStore = ObjectStore(database.createObjectStore(name, keyPath.toWrappedJs())) + /** Creates an object-store that uses out-of-line keys with a key-generator. */ public fun Database.createObjectStore(name: String, autoIncrement: AutoIncrement): ObjectStore = ObjectStore(database.createObjectStore(name, autoIncrement.toJs())) diff --git a/core/src/jsTest/kotlin/AutoIncrementKeyObjectStore.kt b/core/src/jsTest/kotlin/AutoIncrementKeyObjectStore.kt new file mode 100644 index 0000000..fd3de61 --- /dev/null +++ b/core/src/jsTest/kotlin/AutoIncrementKeyObjectStore.kt @@ -0,0 +1,30 @@ +package com.juul.indexeddb + +import kotlin.test.Test +import kotlin.test.assertEquals + +class AutoIncrementKeyObjectStore { + + @Test + fun simpleReadWrite() = runTest { + val database = openDatabase("auto-increment-keys", 1) { database, oldVersion, newVersion -> + if (oldVersion < 1) { + database.createObjectStore("users", AutoIncrement) + } + } + onCleanup { + database.close() + deleteDatabase("auto-increment-keys") + } + + val id = database.writeTransaction("users") { + objectStore("users").add(jso { username = "Username" }) as Double + } + + val user = database.transaction("users") { + objectStore("users") + .get(Key(id)) + } + assertEquals("Username", user.username) + } +} diff --git a/core/src/jsTest/kotlin/InLineKeyObjectStore.kt b/core/src/jsTest/kotlin/InLineKeyObjectStore.kt new file mode 100644 index 0000000..247ee84 --- /dev/null +++ b/core/src/jsTest/kotlin/InLineKeyObjectStore.kt @@ -0,0 +1,30 @@ +package com.juul.indexeddb + +import kotlin.test.Test +import kotlin.test.assertEquals + +class InLineKeyObjectStore { + + @Test + fun simpleReadWrite() = runTest { + val database = openDatabase("in-line-keys", 1) { database, oldVersion, newVersion -> + if (oldVersion < 1) { + database.createObjectStore("users", KeyPath("id")) + } + } + onCleanup { + database.close() + deleteDatabase("in-line-keys") + } + + database.writeTransaction("users") { + objectStore("users").add(jso { id = "7740f7c4-f889-498a-bc6d-f88dabdcfb9a"; username = "Username" }) + } + + val user = database.transaction("users") { + objectStore("users") + .get(Key("7740f7c4-f889-498a-bc6d-f88dabdcfb9a")) + } + assertEquals("Username", user.username) + } +} diff --git a/core/src/jsTest/kotlin/OutOfLineKeyObjectStore.kt b/core/src/jsTest/kotlin/OutOfLineKeyObjectStore.kt new file mode 100644 index 0000000..60941dc --- /dev/null +++ b/core/src/jsTest/kotlin/OutOfLineKeyObjectStore.kt @@ -0,0 +1,30 @@ +package com.juul.indexeddb + +import kotlin.test.Test +import kotlin.test.assertEquals + +class OutOfLineKeyObjectStore { + + @Test + fun simpleReadWrite() = runTest { + val database = openDatabase("out-of-line-keys", 1) { database, oldVersion, newVersion -> + if (oldVersion < 1) { + database.createObjectStore("users") + } + } + onCleanup { + database.close() + deleteDatabase("out-of-line-keys") + } + + database.writeTransaction("users") { + objectStore("users").add(jso { username = "Username" }, Key("7740f7c4-f889-498a-bc6d-f88dabdcfb9a")) + } + + val user = database.transaction("users") { + objectStore("users") + .get(Key("7740f7c4-f889-498a-bc6d-f88dabdcfb9a")) + } + assertEquals("Username", user.username) + } +} diff --git a/external/src/jsMain/kotlin/IDBDatabase.kt b/external/src/jsMain/kotlin/IDBDatabase.kt index 7f9b5d4..5800a25 100644 --- a/external/src/jsMain/kotlin/IDBDatabase.kt +++ b/external/src/jsMain/kotlin/IDBDatabase.kt @@ -8,6 +8,7 @@ public external class IDBDatabase : EventTarget { public val version: Int public val objectStoreNames: Array public fun close() + public fun createObjectStore(name: String): IDBObjectStore public fun createObjectStore(name: String, options: dynamic): IDBObjectStore public fun deleteObjectStore(name: String) public fun transaction(storeNames: Array, mode: String): IDBTransaction