Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions lib/src/main/kotlin/xyz/block/domainapi/util/Controller.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import xyz.block.domainapi.ProcessingState
import xyz.block.domainapi.ResultCode

/**
* An abstract class that represents a controller in the implementation of a domain API based on a state machine.
* An interface that represents a controller in the implementation of a domain API based on a state machine.
* Concrete implementations are responsible for dealing with interactions through the _execute_ and _resume_ endpoints
* for a specific state of the process value.
*/
abstract class Controller<ID, S : State<ID, V, S>, V : Value<ID, V, S>, R>(
interface Controller<ID, S : State<ID, V, S>, V : Value<ID, V, S>, R> {

/** Implementers must supply the state machine (e.g., via constructor). */
val stateMachine: StateMachine<ID, V, S>
) {
/**
* Called when a client calls the execute or resume endpoints.
*
Expand All @@ -29,26 +30,50 @@ abstract class Controller<ID, S : State<ID, V, S>, V : Value<ID, V, S>, R>(
value: V,
inputs: List<Input<R>>,
operation: Operation
): Result<ProcessingState<V, R>> =
): Result<ProcessingState<V, R>> = result {
result {
handleCancelled(value, inputs).bind() ?: processInputs(value, inputs, operation).bind()
}
}.onFailure {
handleFailure(it, value).bind()
}.bind()
}

/**
* Concrete classes implement this function to process the inputs sent by the client.
*
* @param value The current process value.
* @param inputs Any inputs sent by the client.
* @param operation Indicates which operation was called by the client.
*
* @return The resulting [ProcessingState].
*/
abstract fun processInputs(
value: V,
inputs: List<Input<R>>,
operation: Operation
): Result<ProcessingState<V, R>>

/**
* Called when something goes wrong.
*
* @param failure The failure that happened.
* @param value The current value.
* @return The updated value if failing implies, for example, transitioning to a failed state.
*/
abstract fun handleFailure(failure: Throwable, value: V): Result<V>

/**
* What to do if the client sends a `CANCELLED` hurdle response. By default, a
* [xyz.block.domainapi.DomainApiError.ProcessWasCancelled] exception is returned.
*/
open fun handleCancelled(
value: V,
requirementResults: List<Input<R>>
): Result<ProcessingState<V, R>?> =
result {
requirementResults.find { it.result == ResultCode.CANCELLED }?.let {
raise(DomainApiError.ProcessWasCancelled(it.id.toString()))
}
): Result<ProcessingState<V, R>?> = result {
requirementResults.find { it.result == ResultCode.CANCELLED }?.let {
raise(DomainApiError.ProcessWasCancelled(it.id.toString()))
}
}
}

enum class Operation { CREATE, EXECUTE, RESUME }
Loading