Skip to content

Commit 5554b13

Browse files
authored
release: 2.1.6 (#387)
2 parents 19a2804 + 457480e commit 5554b13

30 files changed

+804
-72
lines changed

gradle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,6 @@ slackVersion=1.40.2
4949

5050
### Jackson version ###
5151
jacksonVersion=2.15.3
52+
53+
### Redisson ###
54+
redissonVersion=3.47.0

gradle/core.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ dependencies {
22
implementation "org.rooftopmsa:netx:${netxVersion}"
33
implementation "com.github.ben-manes.caffeine:caffeine:${caffeineCacheVersion}"
44
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:${jacksonVersion}"
5+
implementation "org.redisson:redisson-spring-boot-starter:${redissonVersion}"
56
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.gitanimals.core
2+
3+
import org.springframework.http.HttpStatus
4+
import org.springframework.http.client.ClientHttpResponse
5+
import org.springframework.web.client.ResponseErrorHandler
6+
7+
class HttpClientErrorHandler : ResponseErrorHandler {
8+
9+
override fun hasError(response: ClientHttpResponse): Boolean {
10+
return response.statusCode.isError
11+
}
12+
13+
override fun handleError(response: ClientHttpResponse) {
14+
val body = response.body.bufferedReader().use { it.readText() }
15+
when {
16+
response.statusCode.isSameCodeAs(HttpStatus.UNAUTHORIZED) ->
17+
throw AuthorizationException(body)
18+
19+
response.statusCode.is4xxClientError ->
20+
throw IllegalArgumentException(body)
21+
22+
response.statusCode.is5xxServerError ->
23+
throw IllegalStateException(body)
24+
}
25+
}
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.gitanimals.core
2+
3+
import org.gitanimals.core.auth.UserEntryPoint
4+
5+
fun interface UpdateUserOrchestrator {
6+
7+
fun updateUsername(request: UpdateUserNameRequest)
8+
9+
data class UpdateUserNameRequest(
10+
val id: Long,
11+
val authenticationId: String,
12+
val entryPoint: UserEntryPoint,
13+
val previousName: String,
14+
val changeName: String,
15+
)
16+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.gitanimals.core.lock
2+
3+
import org.springframework.stereotype.Component
4+
import kotlin.time.Duration.Companion.seconds
5+
6+
object LOCK_KEY_PREFIX {
7+
const val CREATE_NEW_USER = "CREATE_NEW_USER"
8+
const val SET_AUTH = "SET_AUTH"
9+
}
10+
11+
object DistributedLock {
12+
13+
private lateinit var lockService: DistributedLockService
14+
15+
fun <T> withLock(
16+
key: String,
17+
leaseMillis: Long = 10.seconds.inWholeMilliseconds,
18+
waitMillis: Long = 3.seconds.inWholeMilliseconds,
19+
action: () -> T,
20+
): T {
21+
return lockService.withLock(
22+
key = key,
23+
leaseMillis = leaseMillis,
24+
waitMillis = waitMillis,
25+
action = action,
26+
)
27+
}
28+
29+
interface DistributedLockService {
30+
31+
fun <T> withLock(
32+
key: String,
33+
leaseMillis: Long = 10.seconds.inWholeMilliseconds,
34+
waitMillis: Long = 3.seconds.inWholeMilliseconds,
35+
action: () -> T,
36+
): T
37+
}
38+
39+
@Component
40+
class DistributedLockServiceInjector(distributedLockService: DistributedLockService) {
41+
42+
init {
43+
lockService = distributedLockService
44+
}
45+
}
46+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.gitanimals.core.lock
2+
3+
class LockAcquireFailException(
4+
override val message: String,
5+
) : RuntimeException(message)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.gitanimals.core.lock
2+
3+
import org.redisson.Redisson
4+
import org.redisson.api.RedissonClient
5+
import org.redisson.config.Config
6+
import org.springframework.beans.factory.annotation.Value
7+
import org.springframework.context.annotation.Bean
8+
import org.springframework.context.annotation.Configuration
9+
import org.springframework.stereotype.Component
10+
import java.util.concurrent.TimeUnit
11+
12+
@Component
13+
class RedisDistributedLockService(
14+
private val redissonClient: RedissonClient,
15+
) : DistributedLock.DistributedLockService {
16+
17+
override fun <T> withLock(
18+
key: String,
19+
leaseMillis: Long,
20+
waitMillis: Long,
21+
action: () -> T
22+
): T {
23+
val lock = redissonClient.getLock(key)
24+
val acquired = lock.tryLock(waitMillis, leaseMillis, TimeUnit.MILLISECONDS)
25+
26+
return if (acquired) {
27+
runCatching {
28+
action.invoke()
29+
}.getOrElse {
30+
throw it
31+
}.also {
32+
lock.unlock()
33+
}
34+
} else {
35+
throw LockAcquireFailException(message = "Cannot acquire lock")
36+
}
37+
}
38+
}
39+
40+
@Configuration
41+
class RedissonConfig(
42+
@Value("\${netx.host}") private val host: String,
43+
@Value("\${netx.port}") private val port: String,
44+
@Value("\${netx.password:0000}") private val password: String,
45+
) {
46+
47+
@Bean
48+
fun redissonClient(): RedissonClient {
49+
val config = Config()
50+
51+
config.useSingleServer()
52+
.setAddress("redis://$host:$port")
53+
// .setPassword(password)
54+
55+
return Redisson.create(config)
56+
}
57+
}

src/main/kotlin/org/gitanimals/guild/domain/Guild.kt

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
package org.gitanimals.guild.domain
22

33
import jakarta.persistence.*
4-
import org.gitanimals.core.AggregateRoot
5-
import org.gitanimals.core.FieldType
6-
import org.gitanimals.core.IdGenerator
7-
import org.gitanimals.core.PersonaType
8-
import org.gitanimals.core.DomainEventPublisher
4+
import org.gitanimals.core.*
95
import org.gitanimals.guild.domain.event.GuildContributionUpdated
106
import org.gitanimals.guild.domain.extension.GuildFieldTypeExtension.isGuildField
117
import org.gitanimals.guild.domain.request.ChangeGuildRequest
@@ -16,7 +12,8 @@ import org.hibernate.annotations.BatchSize
1612
@Table(
1713
name = "guild",
1814
indexes = [
19-
Index(name = "guild_idx_title", unique = true, columnList = "title")
15+
Index(name = "guild_idx_title", unique = true, columnList = "title"),
16+
Index(name = "guild_idx_leader_name", columnList = "name"),
2017
]
2118
)
2219
class Guild(
@@ -161,8 +158,7 @@ class Guild(
161158
val beforeContributions = this.getTotalContributions()
162159
if (leader.name == username) {
163160
leader.contributions = contributions
164-
}
165-
else {
161+
} else {
166162
members.firstOrNull { it.name == username }?.setContributions(contributions)
167163
}
168164
val afterContributions = this.getTotalContributions()
@@ -238,6 +234,15 @@ class Guild(
238234
members.removeIf { it.userId == userId }
239235
}
240236

237+
fun updateUserName(previousName: String, changeName: String) {
238+
if (leader.name == previousName) {
239+
leader.name = changeName
240+
}
241+
242+
members.filter { it.name == previousName }.forEach { it.name = changeName }
243+
waitMembers.filter { it.name == previousName }.forEach { it.name = changeName }
244+
}
245+
241246
companion object {
242247

243248
private const val LEADER_SIZE = 1

src/main/kotlin/org/gitanimals/guild/domain/GuildRepository.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.gitanimals.guild.domain
22

33
import org.springframework.data.domain.Page
44
import org.springframework.data.domain.Pageable
5+
import org.springframework.data.domain.Slice
56
import org.springframework.data.jpa.repository.JpaRepository
67
import org.springframework.data.jpa.repository.Query
78
import org.springframework.data.repository.query.Param
@@ -105,4 +106,14 @@ interface GuildRepository : JpaRepository<Guild, Long> {
105106
"""
106107
)
107108
fun searchByContributionCountDesc(pageable: Pageable): Page<Guild>
109+
110+
@Query(
111+
"""
112+
select g from Guild as g
113+
left join fetch g.members as m
114+
left join g.waitMembers as wm
115+
where g.leader.name = :name or m.name = :name or wm.name = :name
116+
"""
117+
)
118+
fun findAllByNameContains(@Param("name") name: String, pageable: Pageable): Slice<Guild>
108119
}

src/main/kotlin/org/gitanimals/guild/domain/GuildService.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,16 @@ class GuildService(
150150
}
151151
}
152152

153+
@Transactional
154+
@Retryable(ObjectOptimisticLockingFailureException::class)
155+
fun updateUsername(previousName: String, changeName: String, pageable: Pageable) {
156+
val users = guildRepository.findAllByNameContains(name = previousName, pageable = pageable)
157+
158+
users.content.forEach {
159+
it.updateUserName(previousName, changeName)
160+
}
161+
}
162+
153163
fun search(text: String, pageNumber: Int, filter: SearchFilter): Page<Guild> {
154164
return if (text.isBlank()) {
155165
when (filter) {

0 commit comments

Comments
 (0)