Skip to content

Commit c039b97

Browse files
authored
#60 Connect people and pictures, clean abandoned pictures add MinIO testcontainer, change logging (#80)
Signed-off-by: vityaman <vityaman.dev@yandex.ru>
1 parent 5f9b250 commit c039b97

File tree

40 files changed

+292
-69
lines changed

40 files changed

+292
-69
lines changed

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/logic/logging/LoggingAuthService.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ class LoggingAuthService(val origin: AuthService) : AuthService {
1212
runCatching { origin.authenticate(telegram) }
1313
.onSuccess { log.debug("Authenticated telegram user with id ${telegram.user.id}") }
1414
.onFailure { e ->
15-
log.warn("Failed to authenticate telegram with id ${telegram.user.id}: ${e.message}")
15+
buildString {
16+
append("Failed to authenticate telegram ")
17+
append("with id ${telegram.user.id}: ${e.message}")
18+
}.let { log.warn(it) }
1619
}
1720
.getOrThrow()
18-
}
21+
}

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/security/auth/JwtTokenIssuer.kt

-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
package ru.ifmo.se.dating.authik.security.auth
22

33
import io.jsonwebtoken.Jwts
4-
import org.springframework.beans.factory.annotation.Value
5-
import org.springframework.context.annotation.Bean
6-
import org.springframework.context.annotation.Configuration
74
import ru.ifmo.se.dating.security.auth.AccessToken
85
import ru.ifmo.se.dating.security.auth.Jwt
9-
import ru.ifmo.se.dating.security.key.Keys
106
import java.security.PrivateKey
117
import java.time.Clock
128
import java.util.*
139
import kotlin.time.toJavaDuration
14-
import kotlin.time.toKotlinDuration
1510
import java.time.Duration as JavaDuration
1611
import kotlin.time.Duration as KotlinDuration
1712

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/security/auth/LoggingTokenIssuer.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ class LoggingTokenIssuer(private val origin: TokenIssuer) : TokenIssuer {
1010
runCatching { origin.issue(payload) }
1111
.onSuccess { log.info("Issued access token for user with id ${payload.userId}") }
1212
.onFailure { e ->
13-
log.warn("Failed to issue access token for user with id ${payload.userId}: ${e.message}")
13+
buildString {
14+
append("Failed to issue access token for ")
15+
append("user with id ${payload.userId}: ${e.message}")
16+
}.let { log.warn(it) }
1417
}
1518
.getOrThrow()
1619
}

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/logging/Slf4jLog.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ class Slf4jLog(name: String) : Log {
1212
origin.warn(message)
1313

1414
override fun warn(message: String, e: Throwable) =
15-
origin.warn("${message}: ${e.message}", e)
15+
origin.warn("$message: ${e.message}", e)
1616

1717
override fun error(message: String, e: Throwable) =
18-
origin.error("${message}: ${e.message}", e)
18+
origin.error("$message: ${e.message}", e)
1919

2020
override fun debug(message: String) =
2121
origin.debug(message)

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/logic/TransactionalOutbox.kt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import kotlinx.coroutines.flow.Flow
44
import kotlinx.coroutines.flow.toCollection
55
import ru.ifmo.se.dating.storage.TxEnv
66

7+
@Suppress("ComplexInterface")
78
interface TransactionalOutbox<E, Id> {
89
val tx: TxEnv
910
fun publishable(): Flow<Id>

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/security/auth/JwtTokenDecoder.kt

-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ package ru.ifmo.se.dating.security.auth
33
import io.jsonwebtoken.ExpiredJwtException
44
import io.jsonwebtoken.Jwts
55
import io.jsonwebtoken.MalformedJwtException
6-
import org.springframework.beans.factory.annotation.Value
7-
import org.springframework.context.annotation.Bean
8-
import org.springframework.context.annotation.Configuration
96
import ru.ifmo.se.dating.exception.AuthenticationException
10-
import ru.ifmo.se.dating.security.key.Keys
117
import java.security.PublicKey
128
import java.time.Clock
139
import java.util.*

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/security/ssl/SSLContextConfig.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,28 @@ class SSLContextConfig(
3131
fun sslContext(): SslContext {
3232
log.info("Loading an SSL Context...")
3333

34-
log.info("Loading a keystore with type $keyStoreType...")
34+
log.debug("Loading a keystore with type $keyStoreType...")
3535
val keystore = KeyStore.getInstance(keyStoreType)
3636
keyStore.inputStream.use { inputStream ->
3737
keystore.load(inputStream, keyStorePassword.toCharArray())
3838
}
3939

40-
log.info("Creating a Key Manager...")
40+
log.debug("Creating a Key Manager...")
4141
val keyManager = KeyManagerFactory.getDefaultAlgorithm()
4242
.let { KeyManagerFactory.getInstance(it) }
4343
.apply { init(keystore, keyStorePassword.toCharArray()) }
4444

45-
log.info("Creating a Trust Manager...")
45+
log.debug("Creating a Trust Manager...")
4646
val trustManager = TrustManagerFactory.getDefaultAlgorithm()
4747
.let { TrustManagerFactory.getInstance(it) }
4848
.apply { init(keystore) }
4949

50-
log.info("Building an SSL Context...")
50+
log.debug("Building an SSL Context...")
5151
return SslContextBuilder.forClient()
5252
.keyManager(keyManager)
5353
.trustManager(trustManager)
5454
.protocols(sslProtocol)
5555
.build()
56-
.also { log.info("SSL Context was built") }
56+
.also { log.debug("SSL Context was built") }
5757
}
5858
}

backend/foundation/src/main/resources/application-foundation.yml

+3
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ security:
2828
logging:
2929
level:
3030
web: INFO
31+
liquibase: WARN
32+
pattern:
33+
console: "%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSS}){faint} %clr([%level]) %clr(%logger{36}){blue}: %msg%n"

backend/gateway/src/main/resources/application.yml

+7
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,10 @@ springdoc:
133133
url: openapi/matchmaker/api.yml
134134
- name: people
135135
url: openapi/people/api.yml
136+
logging:
137+
level:
138+
gateway: WARN
139+
group:
140+
gateway: org.springframework.cloud.gateway.route
141+
pattern:
142+
console: "%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSS}){faint} %clr([%level]) %clr(%logger{36}){blue}: %msg%n"

backend/gradle/libs.versions.toml

+1
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,6 @@ org-postgresql-r2dbc-postgresql = { module = "org.postgresql:r2dbc-postgresql",
7575
junit-junit = { module = "junit:junit", version.ref = "junit-junit" }
7676
org-testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "org-testcontainers" }
7777
org-testcontainers-r2dbc = { module = "org.testcontainers:r2dbc", version.ref = "org-testcontainers" }
78+
org-testcontainers-minio = { module = "org.testcontainers:minio", version.ref = "org-testcontainers" }
7879
org-testcontainers-junit-jupiter = { module = "org.testcontainers:junit-jupiter", version.ref = "org-testcontainers" }
7980
io-projectreactor-reactor-test = { module = "io.projectreactor:reactor-test", version.ref = "io-projectreactor-reactor-test" }

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/logging/LoggingAttitudeService.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ class LoggingAttitudeService(private val origin: AttitudeService) : AttitudeServ
4242

4343
override fun suggestions(client: User.Id, limit: Int): Flow<User.Id> =
4444
runCatching { origin.suggestions(client, limit) }
45-
.onSuccess { log.debug("Got no more than $limit suggestions for client with id $client") }
45+
.onSuccess {
46+
log.debug("Got no more than $limit suggestions for client with id $client")
47+
}
4648
.onFailure { e ->
4749
log.warn("Failed to retrieve suggestions for client with id $client: ${e.message}")
4850
}

backend/matchmaker/src/main/kotlin/ru/ifmo/se/dating/matchmaker/logic/spring/SpringAttitudeService.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package ru.ifmo.se.dating.matchmaker.logic.spring
22

3-
import org.springframework.stereotype.Service;
3+
import org.springframework.stereotype.Service
44
import ru.ifmo.se.dating.matchmaker.logic.AttitudeService
55
import ru.ifmo.se.dating.matchmaker.logic.basic.BasicAttitudeService
66
import ru.ifmo.se.dating.matchmaker.logic.logging.LoggingAttitudeService
7-
import ru.ifmo.se.dating.matchmaker.storage.AttitudeStorage;
7+
import ru.ifmo.se.dating.matchmaker.storage.AttitudeStorage
88

99
@Service
1010
class SpringAttitudeService(

backend/people/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ plugins {
77
dependencies {
88
implementation(libs.io.minio.minio)
99
implementation(project(":foundation"))
10+
testImplementation(libs.org.testcontainers.minio)
1011
testImplementation(project(":foundation-test"))
1112
}
1213

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/api/HttpPeopleApi.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ class HttpPeopleApi(
121121
throw AuthorizationException("caller $callerId can't post photo to $targetId profile")
122122
}
123123

124-
val pictureId = pictureService.save(Picture.Content(body.contentAsByteArray))
124+
val pictureId = Picture.Draft(
125+
ownerId = targetId,
126+
content = Picture.Content(body.contentAsByteArray),
127+
).let { pictureService.save(it) }
125128

126129
return PictureMessage(id = pictureId.number.toLong()).let { ResponseEntity.ok(it) }
127130
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/api/mapping/PersonMapping.kt

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fun Person.toMessage() = PersonMessage(
3333
locationId = locationId.number.toLong(),
3434
interests = emptySet(),
3535
zodiac = ZodiacSignMessage.leo,
36+
pictures = pictureIds.map { PictureMessage(id = it.number.toLong()) }.toSet()
3637
)
3738

3839
fun Person.Draft.toMessage() = PersonDraftMessage(
@@ -45,4 +46,5 @@ fun Person.Draft.toMessage() = PersonDraftMessage(
4546
facultyId = facultyId?.number?.toLong(),
4647
locationId = locationId?.number?.toLong(),
4748
interests = emptySet(),
49+
pictures = pictureIds.map { PictureMessage(id = it.number.toLong()) }.toSet()
4850
)

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/PictureService.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ru.ifmo.se.dating.people.model.Picture
44

55
interface PictureService {
66
suspend fun getById(id: Picture.Id): Picture.Content
7-
suspend fun save(content: Picture.Content): Picture.Id
7+
suspend fun save(draft: Picture.Draft): Picture.Id
88
suspend fun remove(id: Picture.Id)
9+
suspend fun recover()
910
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/basic/BasicPersonOutbox.kt

-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package ru.ifmo.se.dating.people.logic.basic
22

33
import kotlinx.coroutines.flow.Flow
4-
import kotlinx.coroutines.runBlocking
5-
import org.springframework.scheduling.annotation.Scheduled
64
import ru.ifmo.se.dating.matchmaker.client.model.generated.PersonStatusMessage
75
import ru.ifmo.se.dating.matchmaker.client.model.generated.PersonUpdateMessage
86
import ru.ifmo.se.dating.people.external.MatchmakerApi
@@ -13,7 +11,6 @@ import ru.ifmo.se.dating.people.storage.PersonStorage
1311
import ru.ifmo.se.dating.security.auth.User
1412
import ru.ifmo.se.dating.storage.FetchPolicy
1513
import ru.ifmo.se.dating.storage.TxEnv
16-
import java.util.concurrent.TimeUnit
1714

1815
class BasicPersonOutbox(
1916
private val storage: PersonStorage,
@@ -46,13 +43,4 @@ class BasicPersonOutbox(
4643

4744
override fun publishable(): Flow<User.Id> =
4845
storage.selectNotSentIds(limit = 64)
49-
50-
@Scheduled(
51-
fixedRateString = "30",
52-
initialDelayString = "30",
53-
timeUnit = TimeUnit.SECONDS,
54-
)
55-
fun doRecovery(): Unit = runBlocking {
56-
recover()
57-
}
5846
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/basic/BasicPersonService.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ class BasicPersonService(
4747
throw ConflictException("person already saved")
4848
}
4949

50-
if (variant is Person.Draft && expected.copy(version = variant.version) != variant) {
50+
if (
51+
variant is Person.Draft && expected.copy(
52+
pictureIds = variant.pictureIds,
53+
version = variant.version,
54+
) != variant
55+
) {
5156
throw ConflictException("provided person does not match an existing")
5257
}
5358

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/basic/BasicPictureService.kt

+10-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ class BasicPictureService(
1212
override suspend fun getById(id: Picture.Id): Picture.Content =
1313
contentStorage.download(id)
1414

15-
override suspend fun save(content: Picture.Content): Picture.Id {
16-
val picture = recordStorage.insert()
17-
contentStorage.upload(picture.id, content)
15+
override suspend fun save(draft: Picture.Draft): Picture.Id {
16+
val picture = recordStorage.insert(draft.ownerId)
17+
contentStorage.upload(picture.id, draft.content)
1818
recordStorage.setIsReferenced(picture.id, isReferenced = true)
1919
return picture.id
2020
}
@@ -24,4 +24,11 @@ class BasicPictureService(
2424
contentStorage.remove(id)
2525
recordStorage.delete(id)
2626
}
27+
28+
override suspend fun recover() {
29+
recordStorage.selectAbandoned().collect {
30+
contentStorage.remove(it.id)
31+
recordStorage.delete(it.id)
32+
}
33+
}
2734
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/logging/LoggingFacultyService.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ class LoggingFacultyService(private val origin: FacultyService) : FacultyService
2525
.onSuccess { log.debug("Got all faculties") }
2626
.onFailure { e -> log.warn("Failed to get all faculties: ${e.message}") }
2727
.getOrThrow()
28-
}
28+
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/logging/LoggingPersonService.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ class LoggingPersonService(private val origin: PersonService) : PersonService {
4747
}.let { log.warn(it, e) }
4848
}
4949
.getOrThrow()
50-
}
50+
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/logging/LoggingPictureService.kt

+21-6
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,33 @@ class LoggingPictureService(private val origin: PictureService) : PictureService
1010
override suspend fun getById(id: Picture.Id): Picture.Content =
1111
runCatching { origin.getById(id) }
1212
.onSuccess { log.info("Retrieved picture with id $id") }
13-
.onFailure { e -> log.warn("Failed to retrieve picture with id $id", e) }
13+
.onFailure { e -> log.warn("Failed to retrieve picture with id $id: ${e.message}") }
1414
.getOrThrow()
1515

16-
override suspend fun save(content: Picture.Content): Picture.Id =
17-
runCatching { origin.save(content) }
18-
.onSuccess { pictureId -> log.info("Saved picture with id $pictureId") }
19-
.onFailure { e -> log.warn("Failed to save picture", e) }
16+
override suspend fun save(draft: Picture.Draft): Picture.Id =
17+
runCatching { origin.save(draft) }
18+
.onSuccess { pictureId ->
19+
log.info("Saved picture with id $pictureId for an owner with id ${draft.ownerId}")
20+
}
21+
.onFailure { e ->
22+
buildString {
23+
append("Failed to save picture for an ")
24+
append("owner with id ${draft.ownerId}: ${e.message}")
25+
}.let { log.warn(it) }
26+
}
2027
.getOrThrow()
2128

2229
override suspend fun remove(id: Picture.Id) =
2330
runCatching { origin.remove(id) }
2431
.onSuccess { log.info("Removed picture with id $id") }
25-
.onFailure { e -> log.warn("Failed to remove picture with id $id", e) }
32+
.onFailure { e -> log.warn("Failed to remove picture with id $id: ${e.message}") }
2633
.getOrThrow()
34+
35+
override suspend fun recover() {
36+
log.info("Starting pictures recovery...")
37+
runCatching { origin.recover() }
38+
.onSuccess { log.info("Successfully cleaned abandoned pictures") }
39+
.onFailure { e -> log.warn("Failed to clean some abandoned pictures", e) }
40+
.getOrThrow()
41+
}
2742
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/spring/SpringPersonOutbox.kt

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ru.ifmo.se.dating.people.logic.spring
22

33
import kotlinx.coroutines.flow.Flow
4+
import kotlinx.coroutines.runBlocking
5+
import org.springframework.scheduling.annotation.Scheduled
46
import org.springframework.stereotype.Service
57
import ru.ifmo.se.dating.logic.LoggingTransactionalOutbox
68
import ru.ifmo.se.dating.people.external.MatchmakerApi
@@ -10,6 +12,7 @@ import ru.ifmo.se.dating.people.model.PersonVariant
1012
import ru.ifmo.se.dating.people.storage.PersonStorage
1113
import ru.ifmo.se.dating.security.auth.User
1214
import ru.ifmo.se.dating.storage.TxEnv
15+
import java.util.concurrent.TimeUnit
1316

1417
@Service
1518
class SpringPersonOutbox(
@@ -41,4 +44,13 @@ class SpringPersonOutbox(
4144

4245
override suspend fun markPublished(event: PersonVariant) =
4346
origin.markPublished(event)
47+
48+
@Scheduled(
49+
fixedRateString = "30",
50+
initialDelayString = "30",
51+
timeUnit = TimeUnit.SECONDS,
52+
)
53+
fun doRecovery(): Unit = runBlocking {
54+
recover()
55+
}
4456
}

backend/people/src/main/kotlin/ru/ifmo/se/dating/people/logic/spring/SpringPictureService.kt

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package ru.ifmo.se.dating.people.logic.spring
22

3+
import kotlinx.coroutines.runBlocking
4+
import org.springframework.scheduling.annotation.Scheduled
35
import org.springframework.stereotype.Service
46
import ru.ifmo.se.dating.people.logic.PictureService
57
import ru.ifmo.se.dating.people.logic.basic.BasicPictureService
68
import ru.ifmo.se.dating.people.logic.logging.LoggingPictureService
79
import ru.ifmo.se.dating.people.storage.PictureContentStorage
810
import ru.ifmo.se.dating.people.storage.PictureRecordStorage
11+
import java.util.concurrent.TimeUnit
912

1013
@Service
1114
class SpringPictureService(
@@ -17,4 +20,13 @@ LoggingPictureService(
1720
recordStorage = recordStorage,
1821
contentStorage = contentStorage,
1922
)
20-
)
23+
) {
24+
@Scheduled(
25+
fixedRateString = "30",
26+
initialDelayString = "30",
27+
timeUnit = TimeUnit.SECONDS,
28+
)
29+
fun doRecovery(): Unit = runBlocking {
30+
recover()
31+
}
32+
}

0 commit comments

Comments
 (0)