flank
flank copied to clipboard
Structural output of domain layer
Motivation
Currently, flank is putting all output data directly to console, which is useful for command-line but not for different types of UI like the desktop.
Goal
- Domain layer is producing structural output instead of direct logging to console.
- Structural output from the domain layer is handled in the presentation layer.
- CLI is converting structures into console logs and printing them.
API
/**
* The abstraction which allows passing output from the domain to presentation.
* Implement in domain top-level interfaces for getting access to outputting result structures.
* @property out The reference to outputting result function.
*/
interface Output {
val out: (Any) -> Unit
}
Steps
- Prepare a list of logs that can be displayed by command.
- Basing on a list of logs prepare corresponding structures.
- Extend domain top-level using
Outputinterface - Register A mapper in
clicommand. - Replace all
logLnoccurrences withoutfunction call - Move the mapper to the proper package. (TODO - Consider which package is proper)
Hint
For step 2. If you have to add a new type that can be identified in the presentation layer as something that should be converted to the console log and you also have to decide where to place it, you can follow the rules:
- If the type denotes the execution status of domain logic run, place this type in domain interface scope.
- If the type is related to API structure, add it to this related structure scope. (This one is shown in the example)
Example
Based on ListAndroidOrientations
Step 1
List<Orientation>- test_runner/src/main/kotlin/ftl/domain/ListAndroidOrientations.kt#L16
Step 2
List<Orientation> is a generic type that is losing parameter type info in runtime, so it's necessary to create a
wrapping structure that could be resolved by type matching.
Add Orientaton.Available structure for representing list available orientations the specific platform
data class Orientation(
val id: String,
val name: String,
val tags: List<String>,
) {
data class Available(
val platform: Platform,
val list: List<Orientation>
)
interface Fetch : (String, Platform) -> Available
}
Step 3
Extend using the Output interface.
interface ListAndroidOrientations : Output {
val configPath: String
}
Step 4
Add out implementation to AndroidOrientationsListCommand
class AndroidOrientationsListCommand :
Runnable,
ListAndroidOrientations {
override fun run() = invoke()
override val out = outputLogger {
when (this) {
is Orientation.Available -> list.toCliTable()
else -> throwUnknownType()
}
}
}
Step 5
Replace logLn with out call
operator fun ListAndroidOrientations.invoke() {
fetchOrientation(
AndroidArgs.loadOrDefault(Paths.get(configPath)).project,
Platform.ANDROID
).out()
}
Step 6
Consider the proper package for API structures to console output formatters - #1872 If issue #1872 isn't finished yet, place the formatters where you wish. This will be taken into account or refactored further.
Utils
fun outputLogger(map: Any.() -> String): Any.() -> Unit = {
logLn(map())
}
fun Any.throwUnknownType(): Nothing =
throw IllegalArgumentException(javaClass.toGenericString())
The commands grouped difficulty.
Simple
- DescribeAndroidLocales
- DescribeAndroidModels
- DescribeAndroidTestEnvironment
- DescribeAndroidVersions
- DescribeIosLocales
- DescribeIosModels
- DescribeIosTestEnvironment
- DescribeIosVersions
- DescribeNetworkProfiles
- ListAndroidLocales
- ListAndroidModels
- ListAndroidOrientations
- ListAndroidVersions
- ListIosLocales
- ListIosModels
- ListIosOrientations
- ListIosVersions
- ListIPBlocks
- ListNetworkProfiles
- ListProvidedSoftware
Average
- RunDoctorAndroid
- RunIosDoctor
- CancelLastRun
Dragon
- LoginGoogleAccount (passing logs from the client/adapter)
- RefreshLastRun
- RunTestAndroid
- RunIosTest