refactor(errors): унифицировал доменные ошибки и добавил failed-статус задач
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
package com.github.nullptroma.wallenc.domain.vault.errors
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.errors.WallencException
|
||||
import com.github.nullptroma.wallenc.domain.errors.toWallencException
|
||||
import com.github.nullptroma.wallenc.domain.vault.network.yandexdisk.YandexDiskAuthException
|
||||
import retrofit2.HttpException
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
|
||||
fun Throwable.toVaultWallencException(): WallencException = when (this) {
|
||||
is WallencException -> this
|
||||
is YandexDiskAuthException -> WallencException.Auth.Failed
|
||||
is HttpException -> WallencException.Network.HttpFailed(
|
||||
operation = "http",
|
||||
statusCode = code(),
|
||||
cause = this,
|
||||
)
|
||||
is FileNotFoundException -> WallencException.Storage.FileNotFound
|
||||
is IOException -> mapVaultIo(this)
|
||||
is IllegalStateException -> mapIllegalState(this)
|
||||
is IllegalArgumentException -> mapIllegalArgument(this)
|
||||
else -> toWallencException()
|
||||
}
|
||||
|
||||
private fun mapVaultIo(e: IOException): WallencException {
|
||||
val msg = e.message.orEmpty()
|
||||
return when {
|
||||
msg.contains("OAuth token is missing", ignoreCase = true) ->
|
||||
WallencException.Auth.TokenMissing
|
||||
msg.contains("HTTP 423", ignoreCase = true) || msg.contains("423 after retries", ignoreCase = true) ->
|
||||
WallencException.Network.ResourceLocked
|
||||
msg.contains("async operation timed out", ignoreCase = true) ->
|
||||
WallencException.Network.OperationTimedOut
|
||||
msg.contains("async operation failed", ignoreCase = true) ->
|
||||
WallencException.Network.OperationFailed
|
||||
else -> WallencException.Network.IoFailed(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapIllegalState(e: IllegalStateException): WallencException = when {
|
||||
e.message == "Not a file" -> WallencException.Storage.NotAFile
|
||||
e.message == "Not a directory" -> WallencException.Storage.NotADirectory
|
||||
e.message?.startsWith("Path segment is a file:") == true -> WallencException.Storage.PathIsFile
|
||||
e.message?.startsWith("Cannot openWrite over directory:") == true ->
|
||||
WallencException.Storage.CannotWriteOverDirectory
|
||||
e.message?.startsWith("Expected file after upload:") == true ->
|
||||
WallencException.Storage.UnexpectedState
|
||||
else -> WallencException.Storage.UnexpectedState
|
||||
}
|
||||
|
||||
private fun mapIllegalArgument(e: IllegalArgumentException): WallencException = when {
|
||||
e.message == "Deleting root path is forbidden" -> WallencException.Storage.DeleteRootForbidden
|
||||
else -> WallencException.Unknown(e)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.nullptroma.wallenc.domain.vault.storages
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.errors.WallencException
|
||||
import com.github.nullptroma.wallenc.domain.vault.model.StorageKeyMap
|
||||
import com.github.nullptroma.wallenc.domain.vault.ports.StorageKeyMapStore
|
||||
import com.github.nullptroma.wallenc.domain.vault.storages.encrypt.EncryptedStorage
|
||||
@@ -109,9 +110,9 @@ class UnlockManager(
|
||||
rememberPassword: Boolean
|
||||
): EncryptedStorage = withContext(ioDispatcher) {
|
||||
return@withContext mutex.withLock {
|
||||
val encInfo = storage.metaInfo.value.encInfo ?: throw Exception("EncInfo is null") // TODO
|
||||
val encInfo = storage.metaInfo.value.encInfo ?: throw WallencException.Storage.EncInfoMissing
|
||||
if (!Encryptor.checkKey(key, encInfo))
|
||||
throw Exception("Incorrect Key")
|
||||
throw WallencException.Storage.IncorrectKey
|
||||
|
||||
val opened = _openedStorages.value.toMutableMap()
|
||||
val cur = opened[storage.uuid]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.github.nullptroma.wallenc.domain.vault.storages.encrypt
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.errors.WallencException
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.vault.storages.common.BaseStorage
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
|
||||
@@ -27,7 +29,7 @@ class EncryptedStorage private constructor(
|
||||
private val scope = CoroutineScope(ioDispatcher + job)
|
||||
|
||||
private val encInfo =
|
||||
source.metaInfo.value.encInfo ?: throw Exception("Storage is not encrypted") // TODO
|
||||
source.metaInfo.value.encInfo ?: throw WallencException.Storage.NotEncrypted
|
||||
|
||||
override val isVirtualStorage: Boolean = true
|
||||
|
||||
@@ -50,7 +52,7 @@ class EncryptedStorage private constructor(
|
||||
|
||||
private fun checkKey() {
|
||||
if (!Encryptor.checkKey(key, encInfo))
|
||||
throw Exception("Incorrect key") // TODO
|
||||
throw WallencException.Storage.IncorrectKey
|
||||
}
|
||||
|
||||
fun getKey(): EncryptKey = EncryptKey(key.bytes)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.github.nullptroma.wallenc.domain.vault.storages.local
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.errors.WallencException
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
@@ -230,7 +232,7 @@ class LocalStorageAccessor(
|
||||
dirCallback: (suspend (File, CommonDirectory) -> Unit)? = null
|
||||
) {
|
||||
if (!checkAvailable())
|
||||
throw Exception("Not available")
|
||||
throw WallencException.Storage.NotAvailable
|
||||
val basePath = Path(_filesystemBasePath.pathString, baseStoragePath)
|
||||
val workedFiles = mutableSetOf<String>()
|
||||
val workedMetaFiles = mutableSetOf<String>()
|
||||
@@ -396,7 +398,7 @@ class LocalStorageAccessor(
|
||||
|
||||
override suspend fun getFileInfo(path: String): IFile {
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
?: throw Exception("Что то пошло не так") // TODO
|
||||
?: throw WallencException.Storage.UnexpectedState
|
||||
return CommonFile(
|
||||
metaInfo = pair.meta,
|
||||
)
|
||||
@@ -404,7 +406,7 @@ class LocalStorageAccessor(
|
||||
|
||||
override suspend fun getDirInfo(path: String): IDirectory {
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
?: throw Exception("Что то пошло не так") // TODO
|
||||
?: throw WallencException.Storage.UnexpectedState
|
||||
return CommonDirectory(
|
||||
metaInfo = pair.meta,
|
||||
elementsCount = null
|
||||
@@ -413,7 +415,7 @@ class LocalStorageAccessor(
|
||||
|
||||
override suspend fun setHidden(path: String, hidden: Boolean) {
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
?: throw Exception("Что то пошло не так") // TODO
|
||||
?: throw WallencException.Storage.UnexpectedState
|
||||
if (pair.meta.isHidden == hidden)
|
||||
return
|
||||
val newMeta = pair.meta.copy(isHidden = hidden)
|
||||
@@ -440,7 +442,7 @@ class LocalStorageAccessor(
|
||||
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||
val file = path.toFile()
|
||||
if (file.exists() && file.isDirectory) {
|
||||
throw Exception("Что то пошло не так") // TODO
|
||||
throw WallencException.Storage.UnexpectedState
|
||||
} else if(!file.exists()) {
|
||||
val parent = Path(storagePath).parent
|
||||
createDir(parent.pathString)
|
||||
@@ -451,7 +453,7 @@ class LocalStorageAccessor(
|
||||
}
|
||||
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, file)
|
||||
?: throw Exception("Что то пошло не так") // TODO
|
||||
?: throw WallencException.Storage.UnexpectedState
|
||||
val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant(), size = Files.size(pair.file.toPath()))
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
_filesUpdates.emit(
|
||||
@@ -468,13 +470,13 @@ class LocalStorageAccessor(
|
||||
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||
val file = path.toFile()
|
||||
if (file.exists() && !file.isDirectory) {
|
||||
throw Exception("Что то пошло не так") // TODO
|
||||
throw WallencException.Storage.UnexpectedState
|
||||
} else if(!file.exists()) {
|
||||
Files.createDirectories(path)
|
||||
}
|
||||
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, file)
|
||||
?: throw Exception("Что то пошло не так") // TODO
|
||||
?: throw WallencException.Storage.UnexpectedState
|
||||
val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant())
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
_dirsUpdates.emit(
|
||||
@@ -512,7 +514,7 @@ class LocalStorageAccessor(
|
||||
|
||||
override suspend fun delete(path: String) = withContext(ioDispatcher) {
|
||||
if (path == "/" || path.isBlank()) {
|
||||
throw IllegalArgumentException("Deleting root path is forbidden")
|
||||
throw WallencException.Storage.DeleteRootForbidden
|
||||
}
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
if (pair != null) {
|
||||
@@ -527,7 +529,7 @@ class LocalStorageAccessor(
|
||||
override suspend fun openWrite(path: String): OutputStream = withContext(ioDispatcher) {
|
||||
touchFileInternal(path, recordJournal = false)
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
?: throw Exception("Файла нет") // TODO
|
||||
?: throw WallencException.Storage.FileNotFound
|
||||
return@withContext pair.file.outputStream().onClosed {
|
||||
CoroutineScope(ioDispatcher).launch {
|
||||
touchFileInternal(path, recordJournal = false)
|
||||
@@ -539,13 +541,13 @@ class LocalStorageAccessor(
|
||||
|
||||
override suspend fun openRead(path: String): InputStream = withContext(ioDispatcher) {
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
?: throw Exception("Файла нет") // TODO
|
||||
?: throw WallencException.Storage.FileNotFound
|
||||
return@withContext pair.file.inputStream()
|
||||
}
|
||||
|
||||
override suspend fun moveToTrash(path: String) = withContext(ioDispatcher) {
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
?: throw Exception("Файла нет") // TODO
|
||||
?: throw WallencException.Storage.FileNotFound
|
||||
val newMeta = pair.meta.copy(isDeleted = true)
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
appendSyncEntry(path = path, operation = StorageSyncOperation.DELETE)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.github.nullptroma.wallenc.domain.vault.storages.yandex
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.errors.WallencException
|
||||
import com.github.nullptroma.wallenc.domain.vault.errors.toVaultWallencException
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.github.nullptroma.wallenc.domain.vault.network.yandexdisk.YandexDiskAuthException
|
||||
import com.github.nullptroma.wallenc.domain.vault.network.yandexdisk.dto.ResourceDto
|
||||
@@ -104,19 +107,23 @@ class YandexStorageAccessor(
|
||||
} catch (e: YandexDiskAuthException) {
|
||||
reportAuthFailure()
|
||||
_storageReady.value = false
|
||||
throw e
|
||||
throw WallencException.Auth.Failed
|
||||
} catch (e: Exception) {
|
||||
_storageReady.value = false
|
||||
throw Exception("Yandex storage init failed", e)
|
||||
throw e.toVaultWallencException()
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> guard(block: () -> T): T {
|
||||
try {
|
||||
return block()
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: YandexDiskAuthException) {
|
||||
reportAuthFailure()
|
||||
throw e
|
||||
throw WallencException.Auth.Failed
|
||||
} catch (e: Throwable) {
|
||||
throw e.toVaultWallencException()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,13 +452,13 @@ class YandexStorageAccessor(
|
||||
|
||||
override suspend fun getFileInfo(path: String): IFile = withContext(ioDispatcher) {
|
||||
val r = guard { repo.get(toDiskPath(path)) }
|
||||
if (r.type != "file") throw IllegalStateException("Not a file")
|
||||
if (r.type != "file") throw WallencException.Storage.NotAFile
|
||||
r.toCommonFile(path)
|
||||
}
|
||||
|
||||
override suspend fun getDirInfo(path: String): IDirectory = withContext(ioDispatcher) {
|
||||
val r = guard { repo.get(toDiskPath(path)) }
|
||||
if (r.type != "dir") throw IllegalStateException("Not a directory")
|
||||
if (r.type != "dir") throw WallencException.Storage.NotADirectory
|
||||
r.toCommonDir(path)
|
||||
}
|
||||
|
||||
@@ -490,7 +497,7 @@ class YandexStorageAccessor(
|
||||
val diskPath = toDiskPath(acc)
|
||||
when (guard { repo.getOrNull(diskPath) }?.type) {
|
||||
"dir" -> continue
|
||||
"file" -> throw IllegalStateException("Path segment is a file: $acc")
|
||||
"file" -> throw WallencException.Storage.PathIsFile
|
||||
else -> guard { repo.createFolder(diskPath) }
|
||||
}
|
||||
}
|
||||
@@ -498,7 +505,7 @@ class YandexStorageAccessor(
|
||||
|
||||
override suspend fun delete(path: String) = withContext(ioDispatcher) {
|
||||
if (path == "/" || path.isBlank()) {
|
||||
throw IllegalArgumentException("Deleting root path is forbidden")
|
||||
throw WallencException.Storage.DeleteRootForbidden
|
||||
}
|
||||
val diskPath = toDiskPath(path)
|
||||
val prior = guard { repo.getOrNull(diskPath) }
|
||||
@@ -531,14 +538,14 @@ class YandexStorageAccessor(
|
||||
val diskPath = toDiskPath(path)
|
||||
val prior = guard { repo.getOrNull(diskPath) }
|
||||
if (prior?.type == "dir") {
|
||||
throw IllegalStateException("Cannot openWrite over directory: $path")
|
||||
throw WallencException.Storage.CannotWriteOverDirectory
|
||||
}
|
||||
val hadFile = prior?.type == "file"
|
||||
val priorSize = if (prior?.type == "file") prior.size ?: 0L else 0L
|
||||
guard { repo.uploadFile(diskPath, tmp, overwrite = true) }
|
||||
val after = guard { getMetadataAfterWrite(diskPath) }
|
||||
if (after.type != "file") {
|
||||
throw IllegalStateException("Expected file after upload: $path")
|
||||
throw WallencException.Storage.UnexpectedState
|
||||
}
|
||||
val newSize = after.size ?: 0L
|
||||
_size.value = ((_size.value ?: 0L) + newSize - priorSize).coerceAtLeast(0L)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.github.nullptroma.wallenc.domain.vault.vaults.local
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.errors.WallencException
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.vault.storages.local.LocalStorage
|
||||
@@ -64,7 +66,7 @@ class LocalVault(
|
||||
private suspend fun readStorages() {
|
||||
val path = path.value
|
||||
if (path == null || !_isAvailable.value) {
|
||||
throw Exception("Not available")
|
||||
throw WallencException.Storage.NotAvailable
|
||||
}
|
||||
|
||||
val dirs = path.listFiles()?.filter { it.isDirectory }
|
||||
@@ -79,7 +81,7 @@ class LocalVault(
|
||||
override suspend fun createStorage(): LocalStorage = withContext(ioDispatcher) {
|
||||
val path = path.value
|
||||
if (path == null || !_isAvailable.value) {
|
||||
throw Exception("Not available")
|
||||
throw WallencException.Storage.NotAvailable
|
||||
}
|
||||
|
||||
val storageUuid = UUID.randomUUID()
|
||||
@@ -104,7 +106,7 @@ class LocalVault(
|
||||
override suspend fun remove(storage: IStorage) = withContext(ioDispatcher) {
|
||||
val path = path.value
|
||||
if (path == null || !_isAvailable.value) {
|
||||
throw Exception("Not available")
|
||||
throw WallencException.Storage.NotAvailable
|
||||
}
|
||||
|
||||
val curStorages = _storages.value.toMutableList()
|
||||
|
||||
Reference in New Issue
Block a user