Skip to content

Commit

Permalink
Introduce some architecture and dependency injection for status-histo…
Browse files Browse the repository at this point in the history
…ry. (#47)
  • Loading branch information
TWiStErRob authored Dec 24, 2023
1 parent d913a08 commit 4068198
Show file tree
Hide file tree
Showing 33 changed files with 1,278 additions and 169 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ build/
/Android/src/main/assets/LondonTravel.data.*.sql

/.idea/
!/.idea/runConfigurations/
*.ipr
*.iml
*.iws
Expand Down
21 changes: 21 additions & 0 deletions .idea/runConfigurations/Run_status_history_server.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ After this runs hit:
### Update AppEngine

```shell
$ gradlew appengineRun
> Task :AppEngine:checkCloudSdk FAILED
$ gradlew :web:status-history:appengineRun
> Task :web:status-history:checkCloudSdk FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':AppEngine:checkCloudSdk'.
Execution failed for task ':web:status-history:checkCloudSdk'.
> Specified Cloud SDK version (347.0.0) does not match installed version (319.0.0).
```

Expand Down
30 changes: 30 additions & 0 deletions domain/status/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
plugins {
id("net.twisterrob.blt.convention")
id("org.jetbrains.kotlin.multiplatform")
id("com.google.devtools.ksp")
}

kotlin {
jvm()
sourceSets {
commonMain {
dependencies {
api(libs.kotlin.datetime)
api(libs.kotlin.serialization)
//implementation("io.github.oshai:kotlin-logging:6.0.1")
}
}
commonTest {
dependencies {
implementation(libs.kotlin.test)
implementation(libs.test.mockative)
}
}
}
}

configurations
.matching { Configuration it -> it.name.startsWith("ksp") && it.name.endsWith("Test") }
.configureEach { Configuration it ->
project.dependencies.add(it.name, libs.test.mockative.processor)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.twisterrob.travel.domain.london.status

import net.twisterrob.travel.domain.london.status.api.FeedParser
import net.twisterrob.travel.domain.london.status.api.HistoryUseCase
import net.twisterrob.travel.domain.london.status.api.ParsedStatusItem
import net.twisterrob.travel.domain.london.status.api.StatusHistoryRepository
import net.twisterrob.travel.domain.london.status.api.StatusInteractor

class DomainHistoryUseCase(
private val statusHistoryRepository: StatusHistoryRepository,
private val statusInteractor: StatusInteractor,
private val feedParser: FeedParser,
) : HistoryUseCase {

/**
* @param max maximum number of items to return, current is not included in the count.
* @param includeCurrent whether to include the current status in the result.
*/
override fun history(feed: Feed, max: Int, includeCurrent: Boolean): List<ParsedStatusItem> {
val result = mutableListOf<StatusItem>()
if (includeCurrent) {
result.add(statusInteractor.getCurrent(feed))
}
val history = statusHistoryRepository.getAll(feed, max)
result.addAll(history)
return result.map { it.parse() }
}

private fun StatusItem.parse(): ParsedStatusItem =
when (this) {
is StatusItem.SuccessfulStatusItem -> {
try {
val feedContents = feedParser.parse(this.feed, this.content)
ParsedStatusItem.ParsedFeed(this, feedContents)
} catch (ex: Exception) {
ParsedStatusItem.ParseFailed(this, Stacktrace(ex.stackTraceToString()))
}
}

is StatusItem.FailedStatusItem -> {
ParsedStatusItem.AlreadyFailed(this)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package net.twisterrob.travel.domain.london.status

import net.twisterrob.travel.domain.london.status.api.RefreshResult
import net.twisterrob.travel.domain.london.status.api.RefreshUseCase
import net.twisterrob.travel.domain.london.status.api.StatusHistoryRepository
import net.twisterrob.travel.domain.london.status.api.StatusInteractor

class DomainRefreshUseCase(
private val statusHistoryRepository: StatusHistoryRepository,
private val statusInteractor: StatusInteractor,
) : RefreshUseCase {

override fun refreshLatest(feed: Feed): RefreshResult {
val current = statusInteractor.getCurrent(feed)
val latest = statusHistoryRepository.getAll(feed, 1).singleOrNull()

return when {
latest == null -> {
statusHistoryRepository.add(current)
RefreshResult.Created(current)
}

sameContent(latest, current) -> {
RefreshResult.NoChange(current, latest)
}

sameError(latest, current) -> {
RefreshResult.NoChange(current, latest)
}

else -> {
statusHistoryRepository.add(current)
RefreshResult.Refreshed(current, latest)
}
}
}

private fun sameContent(latest: StatusItem, current: StatusItem): Boolean =
if (latest is StatusItem.SuccessfulStatusItem && current is StatusItem.SuccessfulStatusItem) {
latest.content == current.content
} else {
false
}

private fun sameError(latest: StatusItem, current: StatusItem): Boolean =
if (latest is StatusItem.FailedStatusItem && current is StatusItem.FailedStatusItem) {
latest.error == current.error
} else {
false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package net.twisterrob.travel.domain.london.status

enum class Feed {
TubeDepartureBoardsLineStatus,
TubeDepartureBoardsLineStatusIncidents,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.twisterrob.travel.domain.london.status

data class Stacktrace(
val stacktrace: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.twisterrob.travel.domain.london.status

data class StatusContent(
val content: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.twisterrob.travel.domain.london.status

import kotlinx.datetime.Instant

sealed class StatusItem {

abstract val retrievedDate: Instant
abstract val feed: Feed

data class SuccessfulStatusItem(
override val feed: Feed,
val content: StatusContent,
override val retrievedDate: Instant,
) : StatusItem()

data class FailedStatusItem(
override val feed: Feed,
val error: Stacktrace,
override val retrievedDate: Instant,
) : StatusItem()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.twisterrob.travel.domain.london.status.api

import net.twisterrob.travel.domain.london.status.Feed
import net.twisterrob.travel.domain.london.status.StatusContent

interface FeedParser {

@Throws(Exception::class)
fun parse(feed: Feed, content: StatusContent): Any
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.twisterrob.travel.domain.london.status.api

import net.twisterrob.travel.domain.london.status.Feed
import net.twisterrob.travel.domain.london.status.Stacktrace
import net.twisterrob.travel.domain.london.status.StatusItem

interface HistoryUseCase {

fun history(feed: Feed, max: Int, includeCurrent: Boolean): List<ParsedStatusItem>
}

sealed class ParsedStatusItem {

abstract val item: StatusItem

data class ParsedFeed(
override val item: StatusItem.SuccessfulStatusItem,
val content: Any,
) : ParsedStatusItem()

data class AlreadyFailed(
override val item: StatusItem.FailedStatusItem,
) : ParsedStatusItem()

data class ParseFailed(
override val item: StatusItem.SuccessfulStatusItem,
val error: Stacktrace,
) : ParsedStatusItem()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.twisterrob.travel.domain.london.status.api

import net.twisterrob.travel.domain.london.status.Feed
import net.twisterrob.travel.domain.london.status.StatusItem

interface RefreshUseCase {

fun refreshLatest(feed: Feed): RefreshResult
}

sealed class RefreshResult {

data class Created(
val current: StatusItem,
) : RefreshResult()

data class Refreshed(
val current: StatusItem,
val latest: StatusItem,
) : RefreshResult()

data class NoChange(
val current: StatusItem,
val latest: StatusItem,
) : RefreshResult()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package net.twisterrob.travel.domain.london.status.api

import net.twisterrob.travel.domain.london.status.Feed
import net.twisterrob.travel.domain.london.status.StatusItem

interface StatusHistoryRepository {

fun add(current: StatusItem)

fun getAll(feed: Feed, max: Int): List<StatusItem>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.twisterrob.travel.domain.london.status.api

import net.twisterrob.travel.domain.london.status.Feed
import net.twisterrob.travel.domain.london.status.StatusItem

interface StatusInteractor {

fun getCurrent(feed: Feed): StatusItem
}
Loading

0 comments on commit 4068198

Please sign in to comment.