Skip to content

Commit

Permalink
Open FileSystemDataSource and add shouldSkipRequest and impl delete (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
hugolefrancois authored Jun 18, 2024
1 parent 2ed7dbc commit 248e0b1
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
public final class com/mirego/trikot/datasources/flow/generic/FileSystemDataSource : com/mirego/trikot/datasources/flow/BaseExpiringExecutableFlowDataSource {
public fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;)V
public synthetic fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public class com/mirego/trikot/datasources/flow/generic/FileSystemDataSource : com/mirego/trikot/datasources/flow/BaseExpiringExecutableFlowDataSource {
public fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;Lcom/mirego/trikot/datasources/flow/FlowDataSource;)V
public synthetic fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;Lcom/mirego/trikot/datasources/flow/FlowDataSource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun delete (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun internalRead (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public synthetic fun internalRead (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun save (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;Lcom/mirego/trikot/datasources/flow/FlowDataSourceExpiringValue;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public synthetic fun save (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun shouldSkipRequest (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;)Z
}

Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
public final class com/mirego/trikot/datasources/flow/generic/FileSystemDataSource : com/mirego/trikot/datasources/flow/BaseExpiringExecutableFlowDataSource {
public fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;)V
public synthetic fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public class com/mirego/trikot/datasources/flow/generic/FileSystemDataSource : com/mirego/trikot/datasources/flow/BaseExpiringExecutableFlowDataSource {
public fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;Lcom/mirego/trikot/datasources/flow/FlowDataSource;)V
public synthetic fun <init> (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/KSerializer;Ljava/lang/String;Lokio/FileSystem;Lcom/mirego/trikot/datasources/flow/FlowDataSource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun delete (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun internalRead (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public synthetic fun internalRead (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun save (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;Lcom/mirego/trikot/datasources/flow/FlowDataSourceExpiringValue;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public synthetic fun save (Lcom/mirego/trikot/datasources/flow/FlowDataSourceRequest;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun shouldSkipRequest (Lcom/mirego/trikot/datasources/flow/ExpiringFlowDataSourceRequest;)Z
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.mirego.trikot.datasources.flow.generic
import com.mirego.trikot.datasources.flow.BaseExpiringExecutableFlowDataSource
import com.mirego.trikot.datasources.flow.DataSourceDispatchers
import com.mirego.trikot.datasources.flow.ExpiringFlowDataSourceRequest
import com.mirego.trikot.datasources.flow.FlowDataSource
import com.mirego.trikot.datasources.flow.FlowDataSourceExpiringValue
import com.mirego.trikot.datasources.flow.filesystem.NativeFileSystem
import kotlinx.coroutines.withContext
Expand All @@ -14,16 +15,21 @@ import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath

class FileSystemDataSource<R : ExpiringFlowDataSourceRequest, T>(
open class FileSystemDataSource<R : ExpiringFlowDataSourceRequest, T>(
private val json: Json,
private val dataSerializer: KSerializer<T>,
private val diskCachePath: String,
private val fileManager: FileSystem = NativeFileSystem.fileSystem
) : BaseExpiringExecutableFlowDataSource<R, T>() {
private val fileManager: FileSystem = NativeFileSystem.fileSystem,
cacheDataSource: FlowDataSource<R, FlowDataSourceExpiringValue<T>>? = null,
) : BaseExpiringExecutableFlowDataSource<R, T>(cacheDataSource) {

public override suspend fun internalRead(request: R): FlowDataSourceExpiringValue<T> {
if (shouldSkipRequest(request)) {
throw Throwable("Skipping internal read")
}

return withContext(DataSourceDispatchers.IO) {
val filePath = buildFilePath(request)
val filePath = buildFilePath(request.cacheableId)
try {
val data = json.decodeFromString(dataSerializer, getFileAsString(filePath))
FlowDataSourceExpiringValue(
Expand All @@ -38,10 +44,14 @@ class FileSystemDataSource<R : ExpiringFlowDataSourceRequest, T>(
}

override suspend fun save(request: R, data: FlowDataSourceExpiringValue<T>?) {
if (shouldSkipRequest(request)) {
return
}

data?.value?.let { dataToSave ->
withContext(DataSourceDispatchers.IO) {
try {
val filePath = buildFilePath(request)
val filePath = buildFilePath(request.cacheableId)
saveStringValueToFile(filePath, json.encodeToString(dataSerializer, dataToSave))
} catch (exception: Throwable) {
println("Failed to save json to disk cache. Error: $exception")
Expand All @@ -50,6 +60,21 @@ class FileSystemDataSource<R : ExpiringFlowDataSourceRequest, T>(
}
}

override suspend fun delete(cacheableId: String) {
withContext(DataSourceDispatchers.IO) {
try {
val filePath = buildFilePath(cacheableId)
fileManager.delete(filePath)
} catch (exception: Throwable) {
println("Failed to delete file to disk. Error: $exception")
}
}
}

open fun shouldSkipRequest(request: R): Boolean {
return false
}

private fun getFileAsString(filePath: Path) = if (fileManager.exists(filePath)) {
fileManager.read(filePath) {
readUtf8()
Expand All @@ -71,5 +96,5 @@ class FileSystemDataSource<R : ExpiringFlowDataSourceRequest, T>(
0
}

private fun buildFilePath(request: R) = "$diskCachePath/${request.cacheableId}.json".toPath()
private fun buildFilePath(cacheableId: String) = "$diskCachePath/$cacheableId.json".toPath()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ package com.mirego.trikot.datasources.flow.generic
import com.mirego.trikot.datasources.flow.ExpiringFlowDataSourceRequest
import com.mirego.trikot.datasources.flow.FlowDataSourceExpiringValue
import com.mirego.trikot.datasources.flow.FlowDataSourceRequest
import com.mirego.trikot.datasources.flow.filesystem.NativeFileSystem
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Clock
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okio.FileNotFoundException
import okio.FileSystem
import okio.Path.Companion.toPath
import okio.fakefilesystem.FakeFileSystem
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class FileSystemDataSourceTests {
Expand All @@ -22,6 +27,7 @@ class FileSystemDataSourceTests {

private val fileSystem = FakeFileSystem()
private val fileSystemDataSource = FileSystemDataSource<BasicRequest, TestData>(json, dataSerializer, diskCachePath, fileSystem)
private val fileSystemDataSourceSkipAll = SkipAllFileSystemDataSource<BasicRequest, TestData>(json, dataSerializer, diskCachePath, fileSystem)

@Test
fun test_read_valid_data() = runTest {
Expand Down Expand Up @@ -65,6 +71,34 @@ class FileSystemDataSourceTests {
assertEquals("{\"value\":\"savedValue\"}", savedContent)
}

@Test
fun test_read_skip() = runTest {
val testRequest = BasicRequest("test", 1000L, FlowDataSourceRequest.Type.REFRESH_CACHE)
val testDataJson = "{\"value\": \"value\"}"
val filePath = "$diskCachePath/${testRequest.cacheableId}.json".toPath()

val parentDirectory = filePath.parent ?: throw AssertionError("Parent directory should not be null")
fileSystem.createDirectories(parentDirectory)
fileSystem.write(filePath) {
writeUtf8(testDataJson)
}

assertFails {
fileSystemDataSourceSkipAll.internalRead(testRequest)
}
}

@Test
fun test_save_data_skip() = runTest {
val testRequest = BasicRequest("saveTest", 1000L, FlowDataSourceRequest.Type.REFRESH_CACHE)
val testData = TestData("savedValue")
val filePath = "$diskCachePath/${testRequest.cacheableId}.json".toPath()

fileSystemDataSourceSkipAll.save(testRequest, FlowDataSourceExpiringValue(testData, Clock.System.now().toEpochMilliseconds()))

assertFalse(fileSystem.exists(filePath))
}

@Serializable
private data class TestData(val value: String)

Expand All @@ -73,4 +107,15 @@ class FileSystemDataSourceTests {
override val expiredInMilliseconds: Long = 0,
override val requestType: FlowDataSourceRequest.Type
) : ExpiringFlowDataSourceRequest

private class SkipAllFileSystemDataSource<R : ExpiringFlowDataSourceRequest, TestData>(
json: Json,
dataSerializer: KSerializer<TestData>,
diskCachePath: String,
fileManager: FileSystem = NativeFileSystem.fileSystem,
) : FileSystemDataSource<R, TestData>(json, dataSerializer, diskCachePath, fileManager) {
override fun shouldSkipRequest(request: R): Boolean {
return true
}
}
}

0 comments on commit 248e0b1

Please sign in to comment.