Skip to content

Commit

Permalink
Updated ServiceTokensRepository to implement new MutableRepository ab…
Browse files Browse the repository at this point in the history
…straction
  • Loading branch information
chRyNaN committed Feb 25, 2025
1 parent 3faeffb commit ec3e40e
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,68 @@ import com.mooncloak.kodetools.konstruct.annotations.Inject
import com.mooncloak.vpn.api.shared.service.ServiceTokens
import com.mooncloak.vpn.api.shared.token.Token
import com.mooncloak.vpn.api.shared.token.TokenType
import com.mooncloak.vpn.app.storage.sqlite.database.MooncloakDatabase
import com.mooncloak.vpn.app.shared.storage.database.MooncloakDatabaseProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock

public class ServiceTokensDatabaseSource @Inject public constructor(
private val database: MooncloakDatabase,
private val databaseProvider: MooncloakDatabaseProvider,
private val clock: Clock
) : ServiceTokensRepository {

private val mutex = Mutex(locked = false)

override suspend fun getLatest(): ServiceTokens? =
withContext(Dispatchers.IO) {
database.serviceTokensQueries.selectLatest()
val database = databaseProvider.get()

return@withContext database.serviceTokensQueries.selectLatest()
.executeAsOneOrNull()
?.toServiceTokens()
}

override suspend fun get(id: String): ServiceTokens =
withContext(Dispatchers.IO) {
database.serviceTokensQueries.selectById(id = id)
val database = databaseProvider.get()

return@withContext database.serviceTokensQueries.selectById(id = id)
.executeAsOneOrNull()
?.toServiceTokens()
?: throw NoSuchElementException("No ServiceTokens found with id '$id'.")
}

override suspend fun add(tokens: ServiceTokens) {
override suspend fun get(count: Int, offset: Int): List<ServiceTokens> =
withContext(Dispatchers.IO) {
val database = databaseProvider.get()

return@withContext database.serviceTokensQueries.selectPage(
count = count.toLong(),
offset = offset.toLong()
).executeAsList()
.map { it.toServiceTokens() }
}

override suspend fun getAll(): List<ServiceTokens> =
withContext(Dispatchers.IO) {
val database = databaseProvider.get()

return@withContext database.serviceTokensQueries.selectAll()
.executeAsList()
.map { it.toServiceTokens() }
}

override suspend fun insert(id: String, value: () -> ServiceTokens): ServiceTokens =
withContext(Dispatchers.IO) {
mutex.withLock {
val database = databaseProvider.get()

val tokens = value.invoke()

require(id == tokens.id) { "Provided id '$id' and ServiceTokens.id '${tokens.id}' does not match." }

val now = clock.now()

database.serviceTokensQueries.insert(
Expand All @@ -51,20 +81,62 @@ public class ServiceTokensDatabaseSource @Inject public constructor(
scope = tokens.scopeString,
userId = tokens.userId
)

return@withContext tokens
}
}

override suspend fun update(id: String, update: ServiceTokens.() -> ServiceTokens): ServiceTokens =
withContext(Dispatchers.IO) {
mutex.withLock {
val database = databaseProvider.get()

database.transactionWithResult {
val current = database.serviceTokensQueries.selectById(id = id)
.executeAsOneOrNull()
?.toServiceTokens()
?: throw NoSuchElementException("No ServiceTokens found with id '$id'.")

val updated = current.update()
val now = clock.now()

require(id == updated.id) { "Provided id '$id' and updated ServiceTokens.id '${updated.id}' does not match." }

database.serviceTokensQueries.updateById(
id = id,
updated = now,
issued = updated.issued,
expiration = updated.expiration,
accessToken = updated.accessToken.value,
type = updated.tokenType.value,
refreshToken = updated.refreshToken?.value,
scope = updated.scopeString,
userId = updated.userId
)

updated
}
}
}
}

override suspend fun remove(id: String) {
withContext(Dispatchers.IO) {
mutex.withLock {
val database = databaseProvider.get()

database.serviceTokensQueries.deleteById(id = id)
}
}
}

override suspend fun clear() {
TODO("Not yet implemented")
withContext(Dispatchers.IO) {
mutex.withLock {
val database = databaseProvider.get()

database.serviceTokensQueries.deleteAll()
}
}
}

private fun com.mooncloak.vpn.app.storage.sqlite.database.ServiceTokens.toServiceTokens(): ServiceTokens =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,61 +1,27 @@
package com.mooncloak.vpn.app.shared.api.service

import com.mooncloak.vpn.api.shared.service.ServiceTokens
import kotlinx.coroutines.CancellationException
import com.mooncloak.vpn.data.shared.repository.MutableRepository

/**
* An storage abstraction for accessing [ServiceTokens].
*/
public interface ServiceTokensRepository {
public interface ServiceTokensRepository : MutableRepository<ServiceTokens> {

/**
* Retrieves the latest [ServiceTokens] instance.
*/
public suspend fun getLatest(): ServiceTokens?

/**
* Retrieves the [ServiceTokens] with the provided [id], or throws a [NoSuchElementException] if there is no
* [ServiceTokens] with the provided [id].
*
* @param [id] The [ServiceTokens.id] value of the [ServiceTokens] to retrieve.
*
* @throws [NoSuchElementException] if no [ServiceTokens] exists in the underlying storage with the provided [id].
*
* @return The [ServiceTokens] instance that has the provided [id] value.
*/
@Throws(NoSuchElementException::class, CancellationException::class)
public suspend fun get(id: String): ServiceTokens

/**
* Adds the provided [ServiceTokens] instance to the underlying storage.
*/
public suspend fun add(tokens: ServiceTokens)

/**
* Removes the [ServiceTokens] containing the provided [id] from the underlying storage if it exists.
*/
public suspend fun remove(id: String)

/**
* Clears all the [ServiceTokens] in the underlying storage.
*/
public suspend fun clear()
public suspend fun add(tokens: ServiceTokens) {
this.insert(
id = tokens.id,
value = { tokens }
)
}

public companion object
}

/**
* Retrieves the [ServiceTokens] with the provided [id], or `null` if there is no [ServiceTokens] with the provided
* [id].
*
* @param [id] The [ServiceTokens.id] value of the [ServiceTokens] to retrieve.
*
* @return The [ServiceTokens] instance that has the provided [id] value, or `null` if no [ServiceTokens] exists in the
* underlying storage with the provided [id].
*/
public suspend fun ServiceTokensRepository.getOrNull(id: String): ServiceTokens? =
try {
get(id = id)
} catch (_: NoSuchElementException) {
null
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ public class ServiceTokensSource @Inject public constructor(
return@withContext databaseSource.get(id = id)
}

override suspend fun get(count: Int, offset: Int): List<ServiceTokens> =
withContext(Dispatchers.IO) {
databaseSource.get(count = count, offset = offset)
}

override suspend fun getAll(): List<ServiceTokens> =
withContext(Dispatchers.IO) {
databaseSource.getAll()
}

override suspend fun add(tokens: ServiceTokens) {
withContext(Dispatchers.IO) {
mutex.withLock {
Expand All @@ -49,6 +59,32 @@ public class ServiceTokensSource @Inject public constructor(
}
}

override suspend fun insert(id: String, value: () -> ServiceTokens): ServiceTokens =
withContext(Dispatchers.IO) {
mutex.withLock {
val inserted = databaseSource.insert(id = id, value = value)

val latest = databaseSource.getLatest() ?: inserted

subscriptionStorage.tokens.set(latest)

return@withContext inserted
}
}

override suspend fun update(id: String, update: ServiceTokens.() -> ServiceTokens): ServiceTokens =
withContext(Dispatchers.IO) {
mutex.withLock {
val updated = databaseSource.update(id = id, update = update)

val latest = databaseSource.getLatest() ?: updated

subscriptionStorage.tokens.set(latest)

return@withContext updated
}
}

override suspend fun remove(id: String) {
withContext(Dispatchers.IO) {
mutex.withLock {
Expand All @@ -64,6 +100,12 @@ public class ServiceTokensSource @Inject public constructor(
}

override suspend fun clear() {
// TODO
withContext(Dispatchers.IO) {
mutex.withLock {
databaseSource.clear()

subscriptionStorage.tokens.remove()
}
}
}
}

0 comments on commit ec3e40e

Please sign in to comment.