diff --git a/src/main/kotlin/org/gitanimals/guild/app/LeaveGuildFacade.kt b/src/main/kotlin/org/gitanimals/guild/app/LeaveGuildFacade.kt new file mode 100644 index 0000000..cc0a2ce --- /dev/null +++ b/src/main/kotlin/org/gitanimals/guild/app/LeaveGuildFacade.kt @@ -0,0 +1,17 @@ +package org.gitanimals.guild.app + +import org.gitanimals.guild.domain.GuildService +import org.springframework.stereotype.Component + +@Component +class LeaveGuildFacade( + private val identityApi: IdentityApi, + private val guildService: GuildService, +) { + + fun leave(token: String, guildId: Long) { + val user = identityApi.getUserByToken(token) + + guildService.leave(guildId, user.id.toLong()) + } +} diff --git a/src/main/kotlin/org/gitanimals/guild/controller/GuildController.kt b/src/main/kotlin/org/gitanimals/guild/controller/GuildController.kt index 444fb4d..383e3c5 100644 --- a/src/main/kotlin/org/gitanimals/guild/controller/GuildController.kt +++ b/src/main/kotlin/org/gitanimals/guild/controller/GuildController.kt @@ -26,6 +26,7 @@ class GuildController( private val joinedGuildFacade: GetJoinedGuildFacade, private val searchGuildFacade: SearchGuildFacade, private val changeMainPersonaFacade: ChangeMainPersonaFacade, + private val leaveGuildFacade: LeaveGuildFacade, ) { @ResponseStatus(HttpStatus.OK) @@ -128,4 +129,10 @@ class GuildController( guildId = guildId, personaId = personaId, ) + + @DeleteMapping("/guilds/{guildId}/leave") + fun leaveGuild( + @RequestHeader(HttpHeaders.AUTHORIZATION) token: String, + @PathVariable("guildId") guildId: Long, + ) = leaveGuildFacade.leave(token, guildId) } diff --git a/src/main/kotlin/org/gitanimals/guild/domain/Guild.kt b/src/main/kotlin/org/gitanimals/guild/domain/Guild.kt index ecd18d8..bc907a1 100644 --- a/src/main/kotlin/org/gitanimals/guild/domain/Guild.kt +++ b/src/main/kotlin/org/gitanimals/guild/domain/Guild.kt @@ -202,6 +202,14 @@ class Guild( } } + fun leave(userId: Long) { + require(userId != leader.userId) { + "Leader cannot leave guild guildId: \"$id\", userId: \"$userId\"" + } + + members.removeIf { it.userId == userId } + } + companion object { fun create( diff --git a/src/main/kotlin/org/gitanimals/guild/domain/GuildService.kt b/src/main/kotlin/org/gitanimals/guild/domain/GuildService.kt index d0476ec..66aae0e 100644 --- a/src/main/kotlin/org/gitanimals/guild/domain/GuildService.kt +++ b/src/main/kotlin/org/gitanimals/guild/domain/GuildService.kt @@ -6,6 +6,8 @@ import org.hibernate.Hibernate import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.repository.findByIdOrNull +import org.springframework.orm.ObjectOptimisticLockingFailureException +import org.springframework.retry.annotation.Retryable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -43,6 +45,7 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun joinGuild( guildId: Long, memberUserId: Long, @@ -63,6 +66,7 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun acceptJoin(acceptorId: Long, guildId: Long, acceptUserId: Long) { val guild = guildRepository.findGuildByIdAndLeaderId(guildId, acceptorId) ?: throw IllegalArgumentException("Cannot accept join cause your not a leader.") @@ -71,6 +75,7 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun denyJoin(denierId: Long, guildId: Long, denyUserId: Long) { val guild = guildRepository.findGuildByIdAndLeaderId(guildId, denierId) ?: throw IllegalArgumentException("Cannot deny join cause your not a leader.") @@ -79,6 +84,7 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun kickMember(kickerId: Long, guildId: Long, kickUserId: Long) { val guild = guildRepository.findGuildByIdAndLeaderId(guildId, kickerId) ?: throw IllegalArgumentException("Cannot kick member cause your not a leader.") @@ -87,6 +93,7 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun changeGuild(changeRequesterId: Long, guildId: Long, request: ChangeGuildRequest) { val guild = guildRepository.findGuildByIdAndLeaderId(guildId, changeRequesterId) ?: throw IllegalArgumentException("Cannot kick member cause your not a leader.") @@ -95,12 +102,21 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun changeMainPersona(guildId: Long, userId: Long, personaId: Long, personaType: String) { val guild = getGuildById(guildId) guild.changeMainPersona(userId, personaId, personaType) } + @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) + fun leave(guildId: Long, userId: Long) { + val guild = getGuildById(guildId) + + guild.leave(userId) + } + fun getGuildById(id: Long, vararg lazyLoading: (Guild) -> Unit): Guild { val guild = guildRepository.findByIdOrNull(id) ?: throw IllegalArgumentException("Cannot fint guild by id \"$id\"") @@ -110,6 +126,7 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun updateContribution(username: String, contributions: Long) { val guilds = guildRepository.findAllGuildByUsernameWithMembers(username) @@ -138,6 +155,7 @@ class GuildService( } @Transactional + @Retryable(ObjectOptimisticLockingFailureException::class) fun deletePersonaSync( userId: Long, deletedPersonaId: Long,