Большой рефакторинг
Из domain выкинуты типы vault, теперь он ничего не знает о Yandex. Объявления провайдеров вынесены в vault-api, а реализации в data
This commit is contained in:
23
vault-api/build.gradle.kts
Normal file
23
vault-api/build.gradle.kts
Normal file
@@ -0,0 +1,23 @@
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
|
||||
plugins {
|
||||
id("java-library")
|
||||
alias(libs.plugins.jetbrains.kotlin.jvm)
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Jar>().configureEach {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":domain"))
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
testImplementation(libs.junit)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
/**
|
||||
* Поддерживаемые облачные провайдеры удалённых vault'ов.
|
||||
*
|
||||
* Бренд «локального» устройства тут не указывается — для дискриминации UI
|
||||
* используется [VaultDescriptor.LocalDevice] vs [VaultDescriptor.LinkedRemote].
|
||||
*/
|
||||
enum class CloudBrand {
|
||||
YANDEX,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVault
|
||||
|
||||
/**
|
||||
* Vault, дополнительно знающий свою категорию для UI/маршрутизации.
|
||||
*
|
||||
* Все реализации vault'ов в `:data` обязаны быть [DescribedVault] —
|
||||
* это единственный способ для presentation/app узнать, что это за vault,
|
||||
* не имея зависимости от `:data`.
|
||||
*/
|
||||
interface DescribedVault : IVault {
|
||||
val descriptor: VaultDescriptor
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
/**
|
||||
* Запуск OAuth-сценария привязки удалённого vault'а для конкретного [CloudBrand].
|
||||
*
|
||||
* Реализация в `:app` (привязана к Activity). presentation вызывает [beginLink]
|
||||
* из контекста Compose-экрана и получает [VaultLinkOutcome] асинхронно.
|
||||
*
|
||||
* Намеренно императивный API через колбэк, чтобы корректно встроиться
|
||||
* в `ActivityResultLauncher` Yandex Auth SDK без удержания Activity синглтоном.
|
||||
*/
|
||||
fun interface RemoteVaultAuthenticator {
|
||||
/**
|
||||
* Начать сценарий линка для бренда [brand].
|
||||
*
|
||||
* Если бренд не поддерживается — реализация должна синхронно вызвать
|
||||
* `onResult(VaultLinkOutcome.Failed(...))`, не бросая исключение.
|
||||
*/
|
||||
fun beginLink(brand: CloudBrand, onResult: (VaultLinkOutcome) -> Unit)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVaultInfo
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Категория vault'а для UI и ветвления — sealed-иерархия в одном модуле,
|
||||
* чтобы `when` был exhaustive.
|
||||
*
|
||||
* `domain` про эти подтипы ничего не знает; внешнее кольцо (`:vault-api`)
|
||||
* расширяет [IVaultInfo] (только `uuid`) до структуры с дополнительной
|
||||
* категоризацией.
|
||||
*/
|
||||
sealed interface VaultDescriptor : IVaultInfo {
|
||||
/** Vault, физически живущий на устройстве; без привязки к облачному аккаунту. */
|
||||
data class LocalDevice(
|
||||
override val uuid: UUID,
|
||||
) : VaultDescriptor
|
||||
|
||||
/** Vault, привязанный к облачному аккаунту через OAuth. */
|
||||
data class LinkedRemote(
|
||||
override val uuid: UUID,
|
||||
val brand: CloudBrand,
|
||||
val accountDisplayName: String,
|
||||
) : VaultDescriptor
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
/**
|
||||
* Результат сценария OAuth-линка нового удалённого vault'а.
|
||||
*
|
||||
* presentation сводит это к: успех → отдать `registration` в [VaultRegistrar.register];
|
||||
* cancel → ничего не делать; failure → показать сообщение.
|
||||
*/
|
||||
sealed interface VaultLinkOutcome {
|
||||
/** Пользователь успешно вошёл; в [registration] лежат данные для регистрации. */
|
||||
data class Success(val registration: VaultRegistration) : VaultLinkOutcome
|
||||
|
||||
/** Пользователь отменил вход. */
|
||||
data object Cancelled : VaultLinkOutcome
|
||||
|
||||
/** Ошибка SDK/сети/сервера; [message] годится для показа пользователю. */
|
||||
data class Failed(val message: String) : VaultLinkOutcome
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Контракт регистрации/удаления удалённого vault'а.
|
||||
* Реализуется тем же объектом, что и `domain.IVaultsManager`.
|
||||
*/
|
||||
interface VaultRegistrar {
|
||||
/**
|
||||
* Зарегистрировать новый удалённый vault по «полезной нагрузке» из
|
||||
* [VaultLinkOutcome.Success]. Если тип [registration] неизвестен реализации,
|
||||
* бросает [IllegalArgumentException].
|
||||
*/
|
||||
suspend fun register(registration: VaultRegistration)
|
||||
|
||||
/** Удалить ранее зарегистрированный vault по идентификатору. */
|
||||
suspend fun unregister(vaultUuid: UUID)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
/**
|
||||
* Маркер «полезной нагрузки» для регистрации удалённого vault'а через [VaultRegistrar].
|
||||
*
|
||||
* Намеренно НЕ sealed: конкретные реализации (`YandexRegistration`, …) живут в `:data`
|
||||
* рядом с соответствующими реализациями vault'а, чтобы `:data` не разнесёшь по
|
||||
* нескольким модулям без необходимости. Цена — отсутствие exhaustive-when через
|
||||
* границу модуля, лечится fail-fast веткой `else` в `VaultsManager.register(...)`.
|
||||
*
|
||||
* presentation/app никогда не «открывают» этот тип — они только перепасовывают
|
||||
* объект из [VaultLinkOutcome.Success] в [VaultRegistrar.register].
|
||||
*/
|
||||
interface VaultRegistration
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.github.nullptroma.wallenc.vaultapi
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVault
|
||||
|
||||
/**
|
||||
* Хелперы фильтрации списка vault'ов по [VaultDescriptor].
|
||||
*
|
||||
* Все реализации vault'а обязаны быть [DescribedVault];
|
||||
* [described] отбрасывает те, что не соответствуют контракту (на случай
|
||||
* вспомогательных тест-двойников и т.п.).
|
||||
*/
|
||||
fun List<IVault>.described(): List<DescribedVault> = filterIsInstance<DescribedVault>()
|
||||
|
||||
val List<DescribedVault>.locals: List<DescribedVault>
|
||||
get() = filter { it.descriptor is VaultDescriptor.LocalDevice }
|
||||
|
||||
val List<DescribedVault>.remotes: List<DescribedVault>
|
||||
get() = filter { it.descriptor is VaultDescriptor.LinkedRemote }
|
||||
|
||||
fun List<DescribedVault>.byBrand(brand: CloudBrand): List<DescribedVault> = filter {
|
||||
(it.descriptor as? VaultDescriptor.LinkedRemote)?.brand == brand
|
||||
}
|
||||
Reference in New Issue
Block a user