From 75139f4361dfae0ff8db5dc94a02831934cd50f3 Mon Sep 17 00:00:00 2001 From: Carlton Whitehead Date: Tue, 18 Jun 2024 08:10:22 -0400 Subject: [PATCH] SNAPSHOT dmvapp sample presentation layer --- toolkit/presentation/presentation/pom.xml | 5 ++ .../adapter/LoadableItemAdapter.kt | 2 +- .../presentation/model/BaseItemModel.kt | 69 ++++++++----------- .../toolkit/presentation/model/ItemModel.kt | 30 ++++---- .../model/ModelNotReadyToCommitException.kt | 5 -- .../model/ModelValidationException.kt | 6 -- .../toolkit/presentation/model/Validated.kt | 7 -- .../presentation/model/ValidationContent.kt | 28 -------- .../toolkit/presentation/model/Violation.kt | 5 -- .../presenter/LoadableItemPresenter.kt | 41 ++++------- .../presenter/SecondDraftPresenter.kt | 32 +++++++++ toolkit/samples/common/pom.xml | 8 +++ .../DriversLicenseApplicationPresenter.kt | 18 +++++ .../DriversLicenseApplicationItemModel.kt | 31 +++++++++ .../model/DriversLicenseApplicationModel.kt | 9 +++ .../state/DriversLicenseApplicationState.kt | 8 +++ .../DriversLicenseApplicationModelFeedback.kt | 22 ++++++ ...DriversLicenseApplicationModelValidator.kt | 18 +++++ 18 files changed, 212 insertions(+), 132 deletions(-) delete mode 100644 toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelNotReadyToCommitException.kt delete mode 100644 toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelValidationException.kt delete mode 100644 toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Validated.kt delete mode 100644 toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ValidationContent.kt delete mode 100644 toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Violation.kt create mode 100644 toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/SecondDraftPresenter.kt create mode 100644 toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/DriversLicenseApplicationPresenter.kt create mode 100644 toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationItemModel.kt create mode 100644 toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationModel.kt create mode 100644 toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/state/DriversLicenseApplicationState.kt create mode 100644 toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelFeedback.kt create mode 100644 toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelValidator.kt diff --git a/toolkit/presentation/presentation/pom.xml b/toolkit/presentation/presentation/pom.xml index 670b4211..3ba96d8d 100644 --- a/toolkit/presentation/presentation/pom.xml +++ b/toolkit/presentation/presentation/pom.xml @@ -19,6 +19,11 @@ toolkit-konstraints 0.1.0-SNAPSHOT + + tech.coner.trailer + toolkit-validation + 0.1.0-SNAPSHOT + \ No newline at end of file diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/adapter/LoadableItemAdapter.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/adapter/LoadableItemAdapter.kt index eee15b89..5e017465 100644 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/adapter/LoadableItemAdapter.kt +++ b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/adapter/LoadableItemAdapter.kt @@ -5,7 +5,7 @@ import tech.coner.trailer.toolkit.presentation.model.ItemModel abstract class LoadableItemAdapter where ITEM_MODEL : ItemModel { - abstract val argumentToModelAdapter: ((ARGUMENT) -> ARGUMENT_MODEL)? + abstract val argumentToModelAdapter: ((ARGUMENT) -> ARGUMENT_MODEL) abstract val itemToModelAdapter: (ITEM) -> ITEM_MODEL abstract val modelToItemAdapter: (ITEM_MODEL) -> ITEM } diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/BaseItemModel.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/BaseItemModel.kt index 3e1f007c..b5e661e3 100644 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/BaseItemModel.kt +++ b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/BaseItemModel.kt @@ -1,76 +1,65 @@ package tech.coner.trailer.toolkit.presentation.model -import kotlin.reflect.KProperty1 -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update -import tech.coner.trailer.toolkit.konstraints.CompositeConstraint -import tech.coner.trailer.toolkit.konstraints.ConstraintViolationException +import tech.coner.trailer.toolkit.validation.Feedback +import tech.coner.trailer.toolkit.validation.ValidationResult +import tech.coner.trailer.toolkit.validation.Validator -abstract class BaseItemModel> : ItemModel { - abstract val constraints: C +abstract class BaseItemModel + : ItemModel + where VALIDATOR_FEEDBACK : Feedback { - abstract val initialItem: I + abstract val validator: Validator + abstract val validatorContext: VALIDATOR_CONTEXT + + abstract val initialItem: ITEM private val _itemFlow by lazy { MutableStateFlow(initialItem) } final override val itemFlow by lazy { _itemFlow.asStateFlow() } - final override val item: I + final override val item: ITEM get() = _itemFlow.value private val _pendingItemFlow by lazy { MutableStateFlow(initialItem) } final override val pendingItemFlow by lazy { _pendingItemFlow.asStateFlow() } - final override var pendingItem: I + final override var pendingItem: ITEM get() = pendingItemFlow.value set(value) = mutatePendingItem { value } - private val _pendingItemValidationFlow by lazy { MutableStateFlow(emptyList()) } + + private val _pendingItemValidationFlow by lazy { + MutableStateFlow>( + ValidationResult(emptyMap()) + ) + } final override val pendingItemValidationFlow by lazy { _pendingItemValidationFlow.asStateFlow() } final override val pendingItemValidation get() = _pendingItemValidationFlow.value - final override fun mutatePendingItem(forceValidate: Boolean?, mutatePendingItemFn: (I) -> I) { + final override fun mutatePendingItem(forceValidate: Boolean?, mutatePendingItemFn: (ITEM) -> ITEM) { _pendingItemFlow.update { pending -> mutatePendingItemFn(pending) } if (forceValidate == true) { validate() } } - override fun

validatedPropertyFlow(property: KProperty1, fn: (I) -> P): Flow> { - val constraints = constraints.propertyConstraints[property] - ?: throw Exception("propertyConstraints does not contain any constraints for property: $property") - return pendingItemFlow.map { item -> - Validated( - fn(item), - constraints - .mapNotNull { constraint -> constraint(item).exceptionOrNull() as ConstraintViolationException? } - .map { Violation(it) } - ) - } - } - final override val isPendingItemValid - get() = _pendingItemValidationFlow.value.isValid() + get() = _pendingItemValidationFlow.value.isValid final override val isPendingItemDirty get() = item != pendingItem - final override fun validate(): List { - return constraints.all - .mapNotNull { it.invoke(pendingItem).exceptionOrNull() as? ConstraintViolationException } - .map { ValidationContent.Error(it.message ?: "Invalid" /* TODO not hard-code english string */) } + final override fun validate(): ValidationResult { + return validator(validatorContext, pendingItem) .also { _pendingItemValidationFlow.value = it } } - override fun commit(forceValidate: Boolean): Result { - if (forceValidate) { - validate() - .also { - if (!it.isValid()) { - return Result.failure(ModelNotReadyToCommitException()) - } - } + override fun commit(requireValid: Boolean, successFn: (ITEM) -> Unit) { + if (requireValid) { + if (!validate().isValid) { + return + } } - return pendingItem + pendingItem .also { _itemFlow.value = it } - .let { Result.success(it) } + .also { successFn(it) } } } \ No newline at end of file diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ItemModel.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ItemModel.kt index 417004ab..c6447efe 100644 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ItemModel.kt +++ b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ItemModel.kt @@ -1,23 +1,27 @@ package tech.coner.trailer.toolkit.presentation.model -import kotlin.reflect.KProperty1 import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import tech.coner.trailer.toolkit.validation.Feedback +import tech.coner.trailer.toolkit.validation.ValidationResult + +interface ItemModel : Model { + val itemFlow: StateFlow + val item: ITEM + + val pendingItemFlow: StateFlow + var pendingItem: ITEM -interface ItemModel : Model { - val itemFlow: StateFlow - val item: I - val pendingItemFlow: StateFlow - var pendingItem: I - val pendingItemValidationFlow: Flow> - val pendingItemValidation: List - val isPendingItemValid: Boolean val isPendingItemDirty: Boolean - fun mutatePendingItem(forceValidate: Boolean? = null, mutatePendingItemFn: (I) -> I) - fun

validatedPropertyFlow(property: KProperty1, fn: (I) -> P): Flow> + val pendingItemValidationFlow: Flow> + val pendingItemValidation: ValidationResult + + val isPendingItemValid: Boolean + + fun mutatePendingItem(forceValidate: Boolean? = null, mutatePendingItemFn: (ITEM) -> ITEM) - fun validate(): List + fun validate(): ValidationResult - fun commit(forceValidate: Boolean = true): Result + fun commit(requireValid: Boolean = true, successFn: (ITEM) -> Unit) } \ No newline at end of file diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelNotReadyToCommitException.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelNotReadyToCommitException.kt deleted file mode 100644 index cd7150be..00000000 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelNotReadyToCommitException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package tech.coner.trailer.toolkit.presentation.model - -class ModelNotReadyToCommitException(cause: Throwable? = null) : Throwable( - message = "Model was not ready to commit" -) diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelValidationException.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelValidationException.kt deleted file mode 100644 index b4671458..00000000 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ModelValidationException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package tech.coner.trailer.toolkit.presentation.model - -class ModelValidationException( - val violations: List -) : Throwable() { -} \ No newline at end of file diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Validated.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Validated.kt deleted file mode 100644 index e79f72f3..00000000 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Validated.kt +++ /dev/null @@ -1,7 +0,0 @@ -package tech.coner.trailer.toolkit.presentation.model - -class Validated(val value: V, val violations: List) { - - val isValid: Boolean - get() = violations.isEmpty() -} diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ValidationContent.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ValidationContent.kt deleted file mode 100644 index 799f176f..00000000 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/ValidationContent.kt +++ /dev/null @@ -1,28 +0,0 @@ -package tech.coner.trailer.toolkit.presentation.model - -sealed class ValidationContent { - abstract val valid: Boolean - abstract val message: String - - data class Error(override val message: String) : ValidationContent() { - override val valid: Boolean = false - } - - sealed class ValidValidationContent : ValidationContent() { - override val valid: Boolean = true - } - - data class Warning(override val message: String) : ValidValidationContent() - data class Success(override val message: String) : ValidValidationContent() - data class Info(override val message: String) : ValidValidationContent() - -} - -fun List.isValid(): Boolean { - return isEmpty() - || all { it.valid } -} - -fun List.isInvalid(): Boolean { - return isNotEmpty() && any { !it.valid } -} \ No newline at end of file diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Violation.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Violation.kt deleted file mode 100644 index b6951cb5..00000000 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/model/Violation.kt +++ /dev/null @@ -1,5 +0,0 @@ -package tech.coner.trailer.toolkit.presentation.model - -import tech.coner.trailer.toolkit.konstraints.ConstraintViolationException - -class Violation(val cause: ConstraintViolationException) diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/LoadableItemPresenter.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/LoadableItemPresenter.kt index b85c9289..7439363e 100644 --- a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/LoadableItemPresenter.kt +++ b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/LoadableItemPresenter.kt @@ -1,19 +1,12 @@ package tech.coner.trailer.toolkit.presentation.presenter -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withTimeout import tech.coner.trailer.toolkit.presentation.adapter.LoadableItemAdapter import tech.coner.trailer.toolkit.presentation.model.ItemModel import tech.coner.trailer.toolkit.presentation.model.LoadableModel +import tech.coner.trailer.toolkit.presentation.model.Model import tech.coner.trailer.toolkit.presentation.state.LoadableItemState abstract class LoadableItemPresenter< @@ -21,22 +14,24 @@ abstract class LoadableItemPresenter< ITEM, ARGUMENT_MODEL, ITEM_MODEL - > - : CoroutineScope - where ITEM_MODEL : ItemModel { + >( + override val initialState: LoadableItemState = LoadableItemState(LoadableModel.Empty()) +) + : SecondDraftPresenter< + ARGUMENT, + ARGUMENT_MODEL, + LoadableItemState + >(), CoroutineScope + where ARGUMENT_MODEL : Model, + ITEM_MODEL : ItemModel { protected abstract val adapter: LoadableItemAdapter - protected abstract val argument: ARGUMENT? - val argumentModel: ARGUMENT_MODEL? by lazy { - argument?.let { adapter.argumentToModelAdapter?.invoke(it) } + abstract override val argument: ARGUMENT + override val argumentModel: ARGUMENT_MODEL by lazy { + argument.let { adapter.argumentToModelAdapter.invoke(it) } } - private val initialState = LoadableItemState(LoadableModel.Empty()) - private val _stateMutex = Mutex() - private val _stateFlow: MutableStateFlow> by lazy { MutableStateFlow(initialState) } - val stateFlow: StateFlow> by lazy { _stateFlow.asStateFlow() } - suspend fun load() { update { old -> old.copy(LoadableModel.Loading()) } performLoad() @@ -68,12 +63,4 @@ abstract class LoadableItemPresenter< } } as LoadableModel.Loaded } - - protected suspend fun update(reduceFn: (old: LoadableItemState) -> LoadableItemState) { - _stateMutex.withLock { - withTimeout(10.milliseconds) { - _stateFlow.update(reduceFn) - } - } - } } \ No newline at end of file diff --git a/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/SecondDraftPresenter.kt b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/SecondDraftPresenter.kt new file mode 100644 index 00000000..39e921b0 --- /dev/null +++ b/toolkit/presentation/presentation/src/main/kotlin/tech/coner/trailer/toolkit/presentation/presenter/SecondDraftPresenter.kt @@ -0,0 +1,32 @@ +package tech.coner.trailer.toolkit.presentation.presenter + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import tech.coner.trailer.toolkit.presentation.model.Model +import tech.coner.trailer.toolkit.presentation.state.State + +abstract class SecondDraftPresenter + where STATE : State { + + abstract val initialState: STATE + private val _stateMutex = Mutex() + private val _stateFlow: MutableStateFlow by lazy { MutableStateFlow(initialState) } + val stateFlow: StateFlow by lazy { _stateFlow.asStateFlow() } + + protected suspend fun update(reduceFn: (old: STATE) -> STATE) { + _stateMutex.withLock { + _stateFlow.update(reduceFn) + } + } + + interface WithArgument { + + val argument: ARGUMENT + val argumentModel: ARGUMENT_MODEL + + } +} \ No newline at end of file diff --git a/toolkit/samples/common/pom.xml b/toolkit/samples/common/pom.xml index 89a8ab9f..77703d36 100644 --- a/toolkit/samples/common/pom.xml +++ b/toolkit/samples/common/pom.xml @@ -23,10 +23,18 @@ toolkit-validation 0.1.0-SNAPSHOT + + tech.coner.trailer + toolkit-presentation + 0.1.0-SNAPSHOT + + + tech.coner.trailer toolkit-validation-testsupport 0.1.0-SNAPSHOT + test diff --git a/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/DriversLicenseApplicationPresenter.kt b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/DriversLicenseApplicationPresenter.kt new file mode 100644 index 00000000..23368b0a --- /dev/null +++ b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/DriversLicenseApplicationPresenter.kt @@ -0,0 +1,18 @@ +package tech.coner.trailer.toolkit.sample.dmvapp.presentation + +import tech.coner.trailer.toolkit.presentation.presenter.SecondDraftPresenter +import tech.coner.trailer.toolkit.sample.dmvapp.presentation.model.DriversLicenseApplicationItemModel +import tech.coner.trailer.toolkit.sample.dmvapp.presentation.model.DriversLicenseApplicationModel +import tech.coner.trailer.toolkit.sample.dmvapp.presentation.state.DriversLicenseApplicationState + +class DriversLicenseApplicationPresenter( + initialState: DriversLicenseApplicationState? = null +) : SecondDraftPresenter() { + + override val initialState = initialState + ?: DriversLicenseApplicationState( + model = DriversLicenseApplicationItemModel( + + ) + ) +} \ No newline at end of file diff --git a/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationItemModel.kt b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationItemModel.kt new file mode 100644 index 00000000..4361a6ab --- /dev/null +++ b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationItemModel.kt @@ -0,0 +1,31 @@ +package tech.coner.trailer.toolkit.sample.dmvapp.presentation.model + +import kotlinx.coroutines.flow.map +import tech.coner.trailer.toolkit.presentation.model.BaseItemModel +import tech.coner.trailer.toolkit.sample.dmvapp.domain.entity.LicenseType +import tech.coner.trailer.toolkit.sample.dmvapp.domain.validation.DriversLicenseApplicationFeedback +import tech.coner.trailer.toolkit.validation.Validator + +class DriversLicenseApplicationItemModel( + override val validator: Validator, +) : BaseItemModel() { + + override val initialItem: DriversLicenseApplicationModel = DriversLicenseApplicationModel() + override val validatorContext = Unit + + var name: String? + get() = pendingItem.name + set(value) = mutatePendingItem { it.copy(name = value) } + val nameFlow = pendingItemFlow.map { it.name } + + var age: Int? + get() = pendingItem.age + set(value) = mutatePendingItem { it.copy(age = age) } + val ageFlow = pendingItemFlow.map { it.age } + + var licenseType: LicenseType? + get() = pendingItem.licenseType + set(value) = mutatePendingItem { it.copy(licenseType = licenseType) } + val licenseTypeFLow = pendingItemFlow.map { it.licenseType } + +} diff --git a/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationModel.kt b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationModel.kt new file mode 100644 index 00000000..1d534ab5 --- /dev/null +++ b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/model/DriversLicenseApplicationModel.kt @@ -0,0 +1,9 @@ +package tech.coner.trailer.toolkit.sample.dmvapp.presentation.model + +import tech.coner.trailer.toolkit.sample.dmvapp.domain.entity.LicenseType + +data class DriversLicenseApplicationModel( + val name: String? = null, + val age: Int? = null, + val licenseType: LicenseType? = null +) diff --git a/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/state/DriversLicenseApplicationState.kt b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/state/DriversLicenseApplicationState.kt new file mode 100644 index 00000000..7e779985 --- /dev/null +++ b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/state/DriversLicenseApplicationState.kt @@ -0,0 +1,8 @@ +package tech.coner.trailer.toolkit.sample.dmvapp.presentation.state + +import tech.coner.trailer.toolkit.presentation.state.State +import tech.coner.trailer.toolkit.sample.dmvapp.presentation.model.DriversLicenseApplicationItemModel + +data class DriversLicenseApplicationState( + val model: DriversLicenseApplicationItemModel +) : State diff --git a/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelFeedback.kt b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelFeedback.kt new file mode 100644 index 00000000..d3c1d519 --- /dev/null +++ b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelFeedback.kt @@ -0,0 +1,22 @@ +package tech.coner.trailer.toolkit.sample.dmvapp.presentation.validation + +import tech.coner.trailer.toolkit.sample.dmvapp.domain.validation.DriversLicenseApplicationFeedback +import tech.coner.trailer.toolkit.validation.Feedback +import tech.coner.trailer.toolkit.validation.Severity.Error + +sealed class DriversLicenseApplicationModelFeedback : Feedback { + + data object NameIsRequired : DriversLicenseApplicationModelFeedback() { + override val severity = Error + } + data object AgeIsRequired : DriversLicenseApplicationModelFeedback() { + override val severity = Error + } + data object LicenseTypeIsRequired : DriversLicenseApplicationModelFeedback() { + override val severity = Error + } + + data class DelegatedFeedback( + val feedback: DriversLicenseApplicationFeedback + ) +} diff --git a/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelValidator.kt b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelValidator.kt new file mode 100644 index 00000000..33d72e44 --- /dev/null +++ b/toolkit/samples/dmvapp/src/main/kotlin/tech/coner/trailer/toolkit/sample/dmvapp/presentation/validation/DriversLicenseApplicationModelValidator.kt @@ -0,0 +1,18 @@ +package tech.coner.trailer.toolkit.sample.dmvapp.presentation.validation + +import tech.coner.trailer.toolkit.sample.dmvapp.presentation.model.DriversLicenseApplicationModel +import tech.coner.trailer.toolkit.validation.Validator + +fun DriversLicenseApplicationModelValidator() = Validator { + DriversLicenseApplicationModel::name { name -> + DriversLicenseApplicationModelFeedback.NameIsRequired.takeIf { name == null } + } + DriversLicenseApplicationModel::age { age -> + DriversLicenseApplicationModelFeedback.AgeIsRequired.takeIf { age == null } + } + DriversLicenseApplicationModel::licenseType { licenseType -> + DriversLicenseApplicationModelFeedback.LicenseTypeIsRequired.takeIf { licenseType == null } + } + stopIfInvalid() + mapInput() +} \ No newline at end of file