feat: アプリケーションサービスの操作ログを残せるように

This commit is contained in:
usbharu 2024-06-07 00:37:34 +09:00
parent cf48ae651b
commit a13fe45d0d
7 changed files with 185 additions and 24 deletions

View File

@ -16,6 +16,8 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.CommandExecutor
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
@ -27,12 +29,13 @@ import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainServi
import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.net.URI
@Service
class RegisterLocalActorApplicationService(
private val transaction: Transaction,
transaction: Transaction,
private val actorDomainService: LocalActorDomainService,
private val actorRepository: ActorRepository,
private val actorFactoryImpl: ActorFactoryImpl,
@ -41,28 +44,31 @@ class RegisterLocalActorApplicationService(
private val userDetailDomainService: UserDetailDomainService,
private val userDetailRepository: UserDetailRepository,
private val idGenerateService: IdGenerateService,
) {
suspend fun register(registerLocalActor: RegisterLocalActor): URI {
return transaction.transaction {
if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) {
// todo 適切な例外を考える
throw Exception("Username already exists")
}
val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!!
) : AbstractApplicationService<RegisterLocalActor, URI>(transaction, Companion.logger) {
val actor = actorFactoryImpl.createLocal(
registerLocalActor.name,
actorDomainService.generateKeyPair(),
instance.id
)
actorRepository.save(actor)
val userDetail = UserDetail.create(
id = UserDetailId(idGenerateService.generateId()),
actorId = actor.id,
password = userDetailDomainService.hashPassword(registerLocalActor.password),
)
userDetailRepository.save(userDetail)
actor.url
override suspend fun internalExecute(command: RegisterLocalActor, executor: CommandExecutor): URI {
if (actorDomainService.usernameAlreadyUse(command.name)) {
// todo 適切な例外を考える
throw Exception("Username already exists")
}
val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!!
val actor = actorFactoryImpl.createLocal(
command.name,
actorDomainService.generateKeyPair(),
instance.id
)
actorRepository.save(actor)
val userDetail = UserDetail.create(
id = UserDetailId(idGenerateService.generateId()),
actorId = actor.id,
password = userDetailDomainService.hashPassword(command.password),
)
userDetailRepository.save(userDetail)
return actor.url
}
companion object {
val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java)
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.application.shared
import kotlinx.coroutines.CancellationException
import org.slf4j.Logger
abstract class AbstractApplicationService<T : Any, R>(
protected val transaction: Transaction,
protected val logger: Logger,
) : ApplicationService<T, R> {
override suspend fun execute(command: T, executor: CommandExecutor): R {
return try {
logger.debug("START ${command::class.simpleName} by $executor")
val response = transaction.transaction<R> {
internalExecute(command, executor)
}
logger.info("SUCCESS ${command::class.simpleName} by ${executor.executor}")
response
} catch (e: CancellationException) {
logger.debug("Coroutine canceled", e)
throw e
} catch (e: Exception) {
logger.warn("Command execution error", e)
throw e
}
}
protected abstract suspend fun internalExecute(command: T, executor: CommandExecutor): R
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.application.shared
interface ApplicationService<T : Any, R> {
suspend fun execute(command: T, executor: CommandExecutor): R
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.application.shared
interface CommandExecutor {
val executor: String
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.springframework
import dev.usbharu.hideout.core.application.shared.CommandExecutor
open class HttpCommandExecutor(
override val executor: String,
val ip: String,
val userAgent: String,
) : CommandExecutor {
override fun toString(): String {
return "HttpCommandExecutor(executor='$executor', ip='$ip', userAgent='$userAgent')"
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.springframework
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
@Component
class SpringMvcCommandExecutorFactory {
fun getCommandExecutor(): HttpCommandExecutor {
val name = SecurityContextHolder.getContext().authentication?.name ?: "ANONYMOUS"
val request = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request
return HttpCommandExecutor(name, request.remoteAddr, request.getHeader("user-agent").orEmpty())
}
}

View File

@ -18,6 +18,7 @@ package dev.usbharu.hideout.core.interfaces.api.auth
import dev.usbharu.hideout.core.application.actor.RegisterLocalActor
import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService
import dev.usbharu.hideout.core.infrastructure.springframework.SpringMvcCommandExecutorFactory
import jakarta.servlet.http.HttpServletRequest
import org.springframework.stereotype.Controller
import org.springframework.validation.annotation.Validated
@ -26,7 +27,10 @@ import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PostMapping
@Controller
class AuthController(private val registerLocalActorApplicationService: RegisterLocalActorApplicationService) {
class AuthController(
private val registerLocalActorApplicationService: RegisterLocalActorApplicationService,
private val springMvcCommandExecutorFactory: SpringMvcCommandExecutorFactory,
) {
@GetMapping("/auth/sign_up")
fun signUp(): String {
return "sign_up"
@ -35,7 +39,10 @@ class AuthController(private val registerLocalActorApplicationService: RegisterL
@PostMapping("/auth/sign_up")
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, request: HttpServletRequest): String {
val registerLocalActor = RegisterLocalActor(signUpForm.username, signUpForm.password)
val uri = registerLocalActorApplicationService.register(registerLocalActor)
val uri = registerLocalActorApplicationService.execute(
registerLocalActor,
springMvcCommandExecutorFactory.getCommandExecutor()
)
request.login(signUpForm.username, signUpForm.password)
return "redirect:$uri"
}