Skip to content

Commit 1f8f7d7

Browse files
chore: revive and update the backend (#1016)
* 974 update dependencies to fix docker incompatibility * 974 update dependency to without known CVEs * 974 decouple actual organisms from the backend code * 974 add missing field from config * 974 replace deprecated class * 974 update docs
1 parent dc21226 commit 1f8f7d7

File tree

19 files changed

+181
-195
lines changed

19 files changed

+181
-195
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,15 @@ when there is new data above a configurable threshold for certain variant of an
3131

3232
This monorepo contains the following packages:
3333

34-
- [`backend/`](./backend): The backend for additional features of the dashboard website, currently the notification features.
34+
- [`backend/`](./backend):
35+
The backend for additional features of the dashboard website, currently the notification features.
36+
37+
- [`website/`](./website):
38+
The dashboard website: delivery of the (basically static, via Astro) HTML pages with the embedded Dashboard Components
39+
(which are included via npm),
40+
which retrieve data from LAPIS instances directly,
41+
and some additional client side features accessing the backend.
3542

36-
- [`website/`](./website): The dashboard website: delivery of the (basically static, via Astro) HTML pages with the embedded Dashboard Components (which are included via npm), which retrieve data from LAPIS instances directly, and some additional client side features accessing the backend.
3743

3844
## Local setup
3945

backend.inactive.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

backend/README.md

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
1-
> [!NOTE]
2-
>
3-
> This backend is *not currently used* as the subscription feature is
4-
> currently on hold and not fully implemented yet, and can currently
5-
> be ignored when setting up a dashboards installation. It is also
6-
> partially out of date. An exception are these two configuration
7-
> files which are shared with the frontend (`website` folder) and thus
8-
> in use and up to date:
9-
> [application-dashboards-prod.yaml](src/main/resources/application-dashboards-prod.yaml),
10-
> [application-dashboards-staging.yaml](src/main/resources/application-dashboards-staging.yaml)
11-
121
# Dashboards backend
132

14-
This backend exists to serve the subscription feature (unfinished,
15-
currently on hold, note the banner above). Genomic queries are served
16-
by LAPIS instances and do not go through this backend.
3+
This backend exists to serve the subscription feature (which is not fully implemented and currently on hold).
4+
Genomic queries are served by LAPIS instances and do not go through this backend.
175

186
See [docs/arc42/03-context.md](../docs/arc42/03-context.md) for its
197
place in the whole context.

backend/build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ dependencies {
2929
implementation("org.jetbrains.kotlin:kotlin-stdlib")
3030
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
3131
implementation("org.flywaydb:flyway-database-postgresql:11.0.0")
32-
implementation("org.postgresql:postgresql:42.7.4")
32+
implementation("org.postgresql:postgresql:42.7.7")
3333
implementation("org.jetbrains.exposed:exposed-spring-boot-starter:0.56.0")
3434
implementation("org.jetbrains.exposed:exposed-json:0.56.0")
3535
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
@@ -39,7 +39,8 @@ dependencies {
3939
exclude(group = "org.mockito")
4040
}
4141
testImplementation("com.ninja-squad:springmockk:4.0.2")
42-
testImplementation("org.testcontainers:postgresql:1.20.4")
42+
testImplementation("org.testcontainers:testcontainers:2.0.3")
43+
testImplementation("org.testcontainers:testcontainers-postgresql:2.0.3")
4344
testImplementation("org.mock-server:mockserver-netty:5.15.0")
4445
testImplementation("org.mock-server:mockserver-spring-test-listener:5.15.0")
4546
}

backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/Subscription.kt

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,6 @@ enum class DateWindow {
1313
LAST_6_MONTHS,
1414
}
1515

16-
enum class Organism {
17-
@JsonProperty("covid")
18-
Covid,
19-
20-
@JsonProperty("h5n1")
21-
H5N1,
22-
23-
@JsonProperty("mpox")
24-
Mpox,
25-
26-
@JsonProperty("westNile")
27-
WestNile,
28-
29-
@JsonProperty("rsvA")
30-
RsvA,
31-
32-
@JsonProperty("rsvB")
33-
RsvB,
34-
}
35-
3616
enum class EvaluationInterval {
3717
@JsonProperty("daily")
3818
DAILY,
@@ -87,15 +67,6 @@ sealed interface Trigger {
8767

8868
typealias LapisFilter = Map<String, String>
8969

90-
interface BaseSubscription {
91-
val name: String?
92-
val interval: EvaluationInterval?
93-
val organism: Organism?
94-
val dateWindow: DateWindow?
95-
val trigger: Trigger?
96-
val active: Boolean?
97-
}
98-
9970
@Schema(
10071
description = "A subscription",
10172
example = """
@@ -112,13 +83,13 @@ interface BaseSubscription {
11283
)
11384
data class Subscription(
11485
val id: String,
115-
override val name: String,
116-
override val interval: EvaluationInterval,
117-
override val active: Boolean,
118-
override val organism: Organism,
119-
override val dateWindow: DateWindow,
120-
override val trigger: Trigger,
121-
) : BaseSubscription
86+
val name: String,
87+
val interval: EvaluationInterval,
88+
val active: Boolean,
89+
val organism: String,
90+
val dateWindow: DateWindow,
91+
val trigger: Trigger,
92+
)
12293

12394
@Schema(
12495
description = "A subscription request",
@@ -133,13 +104,13 @@ data class Subscription(
133104
""",
134105
)
135106
data class SubscriptionRequest(
136-
override val name: String,
137-
override val interval: EvaluationInterval,
138-
override val organism: Organism,
139-
override val dateWindow: DateWindow,
140-
override val trigger: Trigger,
141-
override val active: Boolean,
142-
) : BaseSubscription
107+
val name: String,
108+
val interval: EvaluationInterval,
109+
val organism: String,
110+
val dateWindow: DateWindow,
111+
val trigger: Trigger,
112+
val active: Boolean,
113+
)
143114

144115
@Schema(
145116
description = "A subscription update request",
@@ -154,10 +125,10 @@ data class SubscriptionRequest(
154125
""",
155126
)
156127
data class SubscriptionUpdate(
157-
override val name: String? = null,
158-
override val interval: EvaluationInterval? = null,
159-
override val organism: Organism? = null,
160-
override val dateWindow: DateWindow? = null,
161-
override val trigger: Trigger? = null,
162-
override val active: Boolean? = null,
163-
) : BaseSubscription
128+
val name: String? = null,
129+
val interval: EvaluationInterval? = null,
130+
val organism: String? = null,
131+
val dateWindow: DateWindow? = null,
132+
val trigger: Trigger? = null,
133+
val active: Boolean? = null,
134+
)

backend/src/main/kotlin/org/genspectrum/dashboardsbackend/config/DashboardsConfig.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package org.genspectrum.dashboardsbackend.config
22

3-
import org.genspectrum.dashboardsbackend.api.Organism
43
import org.springframework.boot.context.properties.ConfigurationProperties
54

65
@ConfigurationProperties(prefix = "dashboards")
76
data class DashboardsConfig(
8-
val organisms: Map<Organism, OrganismConfig>,
7+
val organisms: Map<String, OrganismConfig>,
98
) {
10-
fun getOrganismConfig(organism: Organism) = organisms[organism]
9+
fun getOrganismConfig(organism: String) = organisms[organism]
1110
?: throw IllegalArgumentException("No configuration found for organism $organism")
1211
}
1312

@@ -26,4 +25,5 @@ data class ExternalNavigationLink(
2625
val url: String,
2726
val label: String,
2827
val menuIcon: String,
28+
val description: String,
2929
)

backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/subscription/SubscriptionModel.kt

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.genspectrum.dashboardsbackend.model.subscription
33
import org.genspectrum.dashboardsbackend.api.Subscription
44
import org.genspectrum.dashboardsbackend.api.SubscriptionRequest
55
import org.genspectrum.dashboardsbackend.api.SubscriptionUpdate
6+
import org.genspectrum.dashboardsbackend.config.DashboardsConfig
67
import org.genspectrum.dashboardsbackend.controller.BadRequestException
78
import org.genspectrum.dashboardsbackend.controller.NotFoundException
89
import org.jetbrains.exposed.sql.Database
@@ -15,6 +16,7 @@ import javax.sql.DataSource
1516
@Transactional
1617
class SubscriptionModel(
1718
pool: DataSource,
19+
private val dashboardsConfig: DashboardsConfig,
1820
) {
1921
init {
2022
Database.connect(pool)
@@ -34,17 +36,21 @@ class SubscriptionModel(
3436
}
3537
}
3638

37-
fun postSubscriptions(request: SubscriptionRequest, userId: String) = SubscriptionEntity
38-
.new {
39-
name = request.name
40-
interval = request.interval.name
41-
dateWindow = request.dateWindow.name
42-
trigger = request.trigger
43-
organism = request.organism.name
44-
active = request.active
45-
this.userId = userId
46-
}
47-
.toSubscription()
39+
fun postSubscriptions(request: SubscriptionRequest, userId: String): Subscription {
40+
validateIsValidOrganism(request.organism)
41+
42+
return SubscriptionEntity
43+
.new {
44+
name = request.name
45+
interval = request.interval.name
46+
dateWindow = request.dateWindow.name
47+
trigger = request.trigger
48+
organism = request.organism
49+
active = request.active
50+
this.userId = userId
51+
}
52+
.toSubscription()
53+
}
4854

4955
fun deleteSubscription(subscriptionId: String, userId: String) {
5056
val subscription = SubscriptionEntity.findForUser(convertToUuid(subscriptionId), userId)
@@ -54,6 +60,8 @@ class SubscriptionModel(
5460
}
5561

5662
fun putSubscription(subscriptionId: String, subscriptionUpdate: SubscriptionUpdate, userId: String): Subscription {
63+
subscriptionUpdate.organism?.also { validateIsValidOrganism(it) }
64+
5765
val subscription = SubscriptionEntity.findForUser(convertToUuid(subscriptionId), userId)
5866
?: throw NotFoundException("Subscription $subscriptionId not found")
5967

@@ -70,7 +78,7 @@ class SubscriptionModel(
7078
subscription.trigger = subscriptionUpdate.trigger
7179
}
7280
if (subscriptionUpdate.organism != null) {
73-
subscription.organism = subscriptionUpdate.organism.name
81+
subscription.organism = subscriptionUpdate.organism
7482
}
7583
if (subscriptionUpdate.active != null) {
7684
subscription.active = subscriptionUpdate.active
@@ -79,6 +87,12 @@ class SubscriptionModel(
7987
return subscription.toSubscription()
8088
}
8189

90+
private fun validateIsValidOrganism(organism: String) {
91+
if (!dashboardsConfig.organisms.containsKey(organism)) {
92+
throw BadRequestException("Organism '$organism' is not supported")
93+
}
94+
}
95+
8296
private fun convertToUuid(id: String) = try {
8397
UUID.fromString(id)
8498
} catch (_: IllegalArgumentException) {

backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/subscription/SubscriptionTable.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class SubscriptionEntity(id: EntityID<UUID>) : UUIDEntity(id) {
3939
name = name,
4040
interval = enumValueOf(interval),
4141
active = active,
42-
organism = enumValueOf(organism),
42+
organism = organism,
4343
dateWindow = enumValueOf(dateWindow),
4444
trigger = trigger,
4545
)

backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/triggerevaluation/LapisClient.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package org.genspectrum.dashboardsbackend.model.triggerevaluation
33
import com.fasterxml.jackson.databind.ObjectMapper
44
import com.fasterxml.jackson.module.kotlin.readValue
55
import org.genspectrum.dashboardsbackend.api.LapisFilter
6-
import org.genspectrum.dashboardsbackend.api.Organism
76
import org.genspectrum.dashboardsbackend.config.DashboardsConfig
87
import org.genspectrum.dashboardsbackend.log
98
import org.genspectrum.dashboardsbackend.logging.REQUEST_ID_HEADER
@@ -27,15 +26,15 @@ class LapisClientProvider(
2726
objectMapper: ObjectMapper,
2827
requestIdContext: RequestIdContext,
2928
) {
30-
private val clients = Organism.entries.associateWith {
29+
private val clients = dashboardsConfig.organisms.mapValues {
3130
LapisClient(
32-
baseUrl = dashboardsConfig.getOrganismConfig(it).lapis.url,
31+
baseUrl = it.value.lapis.url,
3332
objectMapper = objectMapper,
3433
requestIdContext = requestIdContext,
3534
)
3635
}
3736

38-
fun provide(organism: Organism) = clients[organism]
37+
fun provide(organism: String) = clients[organism]
3938
?: throw IllegalArgumentException("No LAPIS client for organism $organism registered")
4039
}
4140

backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/triggerevaluation/TriggerEvaluator.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package org.genspectrum.dashboardsbackend.model.triggerevaluation
33
import kotlinx.datetime.DateTimeUnit
44
import kotlinx.datetime.minus
55
import org.genspectrum.dashboardsbackend.api.DateWindow
6-
import org.genspectrum.dashboardsbackend.api.Organism
76
import org.genspectrum.dashboardsbackend.api.Subscription
87
import org.genspectrum.dashboardsbackend.api.Trigger
98
import org.genspectrum.dashboardsbackend.api.TriggerEvaluationResult
@@ -60,7 +59,7 @@ class TriggerEvaluator(
6059
)
6160
}
6261

63-
private fun additionalFilters(organism: Organism): Map<String, String> {
62+
private fun additionalFilters(organism: String): Map<String, String> {
6463
return dashboardsConfig.getOrganismConfig(organism).lapis.additionalFilters ?: emptyMap()
6564
}
6665
}
@@ -76,9 +75,7 @@ private class CountComputation(
7675
private val threshold: Int,
7776
) : TriggerComputation {
7877
override fun evaluate(): TriggerEvaluationResult {
79-
val lapisResponse = lapisClient.aggregated(lapisFilter)
80-
81-
return when (lapisResponse) {
78+
return when (val lapisResponse = lapisClient.aggregated(lapisFilter)) {
8279
is LapisAggregatedResponse -> {
8380
if (lapisResponse.data.isEmpty()) {
8481
log.error {

0 commit comments

Comments
 (0)