diff --git a/platform/backend/api/src/main/kotlin/io/hamal/api/http/controller/Assembler.kt b/platform/backend/api/src/main/kotlin/io/hamal/api/http/controller/Assembler.kt index 6f20bc2d28..a042417105 100644 --- a/platform/backend/api/src/main/kotlin/io/hamal/api/http/controller/Assembler.kt +++ b/platform/backend/api/src/main/kotlin/io/hamal/api/http/controller/Assembler.kt @@ -26,6 +26,7 @@ fun Requested.toApiRequested(): ApiRequested = when (this) { is FuncUpdateRequested -> ApiFuncUpdateRequested(requestId, requestStatus, id) is NamespaceAppendRequested -> ApiNamespaceAppendRequested(requestId, requestStatus, id, workspaceId) is NamespaceUpdateRequested -> ApiNamespaceUpdateRequested(requestId, requestStatus, id) + is NamespaceDeleteRequested -> ApiNamespaceDeleteRequested(requestId, requestStatus, id) is StateSetRequested -> ApiStateSetRequested(requestId, requestStatus) is TopicAppendEventRequested -> ApiTopicAppendRequested(requestId, requestStatus, id) is TopicCreateRequested -> ApiTopicCreateRequested(requestId, requestStatus, id, workspaceId, namespaceId, type) diff --git a/platform/backend/api/src/main/kotlin/io/hamal/api/http/controller/namespace/NamespaceDeleteController.kt b/platform/backend/api/src/main/kotlin/io/hamal/api/http/controller/namespace/NamespaceDeleteController.kt new file mode 100644 index 0000000000..99e4cdf33b --- /dev/null +++ b/platform/backend/api/src/main/kotlin/io/hamal/api/http/controller/namespace/NamespaceDeleteController.kt @@ -0,0 +1,24 @@ +package io.hamal.api.http.controller.namespace + +import io.hamal.api.http.controller.accepted +import io.hamal.core.adapter.namespace.NamespaceDeletePort +import io.hamal.core.component.Retry +import io.hamal.lib.domain.vo.NamespaceId +import io.hamal.lib.sdk.api.ApiRequested +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RestController + +@RestController +internal class NamespaceDeleteController( + private val retry: Retry, + private val deleteNamespace: NamespaceDeletePort +) { + @DeleteMapping("/v1/namespaces/{namespaceId}") + fun delete( + @PathVariable("namespaceId") namespaceId: NamespaceId, + ): ResponseEntity = retry { + deleteNamespace(namespaceId).accepted() + } +} \ No newline at end of file diff --git a/platform/backend/core/src/integrationTest/kotlin/io/hamal/core/request/handler/namespace/DeleteHandlerTest.kt b/platform/backend/core/src/integrationTest/kotlin/io/hamal/core/request/handler/namespace/DeleteHandlerTest.kt new file mode 100644 index 0000000000..9e9474ec2b --- /dev/null +++ b/platform/backend/core/src/integrationTest/kotlin/io/hamal/core/request/handler/namespace/DeleteHandlerTest.kt @@ -0,0 +1,103 @@ +package io.hamal.core.request.handler.namespace + +import io.hamal.core.request.handler.BaseRequestHandlerTest +import io.hamal.lib.common.domain.CmdId.Companion.CmdId +import io.hamal.lib.common.domain.Limit +import io.hamal.lib.domain._enum.RequestStatuses.Submitted +import io.hamal.lib.domain.request.NamespaceAppendRequested +import io.hamal.lib.domain.request.NamespaceDeleteRequested +import io.hamal.lib.domain.vo.AuthId.Companion.AuthId +import io.hamal.lib.domain.vo.NamespaceFeatures +import io.hamal.lib.domain.vo.NamespaceId +import io.hamal.lib.domain.vo.NamespaceId.Companion.NamespaceId +import io.hamal.lib.domain.vo.NamespaceName.Companion.NamespaceName +import io.hamal.lib.domain.vo.NamespaceTreeId +import io.hamal.lib.domain.vo.RequestId.Companion.RequestId +import io.hamal.lib.domain.vo.RequestStatus.Companion.RequestStatus +import io.hamal.repository.api.NamespaceCmdRepository +import io.hamal.repository.api.NamespaceQueryRepository +import io.hamal.repository.api.NamespaceTreeCmdRepository +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasSize +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + + +internal class DeleteHandlerTest : BaseRequestHandlerTest() { + + @BeforeEach + fun beforeEach() { + namespaceCmdRepository.clear() + namespaceTreeRepository.clear() + + namespaceCmdRepository.create( + NamespaceCmdRepository.CreateCmd( + id = CmdId(2), + namespaceId = NamespaceId.root, + name = NamespaceName("root"), + workspaceId = testWorkspace.id, + features = NamespaceFeatures.default + ) + ) + namespaceTreeRepository.create( + NamespaceTreeCmdRepository.CreateCmd( + id = CmdId(2), + treeId = NamespaceTreeId.root, + rootNodeId = NamespaceId.root, + workspaceId = testWorkspace.id + ) + ) + } + + @Test + fun `Deletes namespace`() { + testAppendHandler(requestedAppendNamespace) + testDeleteHandler(requestedDeleteNamespace) + + val namespaces = namespaceQueryRepository.list( + NamespaceQueryRepository.NamespaceQuery( + limit = Limit.all, + workspaceIds = listOf(testWorkspace.id) + ) + ) + + assertThat(namespaces, hasSize(1)) + assertThat(namespaceQueryRepository.find(NamespaceId(5)), Matchers.nullValue()) + with(namespaces[0]) { + assertThat(namespaceId, equalTo(NamespaceId.root)) + assertThat(name, equalTo(NamespaceName("root"))) + assertThat(features, equalTo(NamespaceFeatures.default)) + } + } + + @Autowired + private lateinit var testAppendHandler: NamespaceAppendHandler + + @Autowired + private lateinit var testDeleteHandler: NamespaceDeleteHandler + + private val requestedDeleteNamespace by lazy { + NamespaceDeleteRequested( + requestId = RequestId(4), + requestedBy = AuthId(4), + requestStatus = RequestStatus(Submitted), + id = NamespaceId(5), + ) + } + + private val requestedAppendNamespace by lazy { + NamespaceAppendRequested( + requestId = RequestId(3), + requestedBy = AuthId(4), + requestStatus = RequestStatus(Submitted), + parentId = NamespaceId.root, + id = NamespaceId(5), + workspaceId = testWorkspace.id, + name = NamespaceName("awesome-namespace"), + features = null + ) + } +} \ No newline at end of file diff --git a/platform/backend/core/src/main/kotlin/io/hamal/core/adapter/namespace/Delete.kt b/platform/backend/core/src/main/kotlin/io/hamal/core/adapter/namespace/Delete.kt new file mode 100644 index 0000000000..0bc7309fd2 --- /dev/null +++ b/platform/backend/core/src/main/kotlin/io/hamal/core/adapter/namespace/Delete.kt @@ -0,0 +1,40 @@ +package io.hamal.core.adapter.namespace + +import io.hamal.core.adapter.request.RequestEnqueuePort +import io.hamal.core.security.SecurityContext +import io.hamal.lib.domain.GenerateDomainId +import io.hamal.lib.domain._enum.RequestStatuses.Submitted +import io.hamal.lib.domain.request.NamespaceDeleteRequested +import io.hamal.lib.domain.vo.NamespaceId +import io.hamal.lib.domain.vo.RequestId +import io.hamal.lib.domain.vo.RequestStatus.Companion.RequestStatus +import io.hamal.repository.api.NamespaceTreeQueryRepository +import org.springframework.stereotype.Component + +fun interface NamespaceDeletePort { + operator fun invoke(namespaceId: NamespaceId): NamespaceDeleteRequested +} + +@Component +class NamespaceDeleteAdapter( + private val generateDomainId: GenerateDomainId, + private val requestEnqueue: RequestEnqueuePort, + private val namespaceTreeQueryRepository: NamespaceTreeQueryRepository, + private val namespaceGetPort: NamespaceGetPort +) : NamespaceDeletePort { + override fun invoke(namespaceId: NamespaceId): NamespaceDeleteRequested { + namespaceGetPort(namespaceId) + + val root = namespaceTreeQueryRepository.get(namespaceId).root + if (root.value == namespaceId) { + throw IllegalArgumentException("Tried to delete root namespace") + } + + return NamespaceDeleteRequested( + requestId = generateDomainId(::RequestId), + requestedBy = SecurityContext.currentAuthId, + requestStatus = RequestStatus(Submitted), + id = namespaceId, + ).also(requestEnqueue::invoke) + } +} \ No newline at end of file diff --git a/platform/backend/core/src/main/kotlin/io/hamal/core/event/handler/namespace/NamespaceDeletedHandler.kt b/platform/backend/core/src/main/kotlin/io/hamal/core/event/handler/namespace/NamespaceDeletedHandler.kt new file mode 100644 index 0000000000..775e73bf58 --- /dev/null +++ b/platform/backend/core/src/main/kotlin/io/hamal/core/event/handler/namespace/NamespaceDeletedHandler.kt @@ -0,0 +1,27 @@ +package io.hamal.core.event.handler.namespace + +import io.hamal.core.adapter.namespace.NamespaceDeletePort +import io.hamal.core.adapter.namespace_tree.NamespaceTreeGetSubTreePort +import io.hamal.core.event.InternalEventHandler +import io.hamal.lib.common.domain.CmdId +import io.hamal.repository.api.event.NamespaceDeletedEvent +import org.springframework.stereotype.Component + +@Component +internal class NamespaceDeletedHandler( + private val getSubTree: NamespaceTreeGetSubTreePort, + private val deleteNamespace: NamespaceDeletePort, +) : InternalEventHandler { + + override fun handle(cmdId: CmdId, evt: NamespaceDeletedEvent) { + val subTree = getSubTree(evt.namespace.id) + + if (subTree.values.isNotEmpty()) { + subTree.values.forEach { + deleteNamespace(it) + } + } + } +} + + diff --git a/platform/backend/core/src/main/kotlin/io/hamal/core/request/handler/namespace/DeleteHandler.kt b/platform/backend/core/src/main/kotlin/io/hamal/core/request/handler/namespace/DeleteHandler.kt new file mode 100644 index 0000000000..b08433c62d --- /dev/null +++ b/platform/backend/core/src/main/kotlin/io/hamal/core/request/handler/namespace/DeleteHandler.kt @@ -0,0 +1,35 @@ +package io.hamal.core.request.handler.namespace + +import io.hamal.core.event.InternalEventEmitter +import io.hamal.core.request.RequestHandler +import io.hamal.core.request.handler.cmdId +import io.hamal.lib.common.domain.CmdId +import io.hamal.lib.domain.request.NamespaceDeleteRequested +import io.hamal.repository.api.Namespace +import io.hamal.repository.api.NamespaceCmdRepository +import io.hamal.repository.api.event.NamespaceDeletedEvent +import org.springframework.stereotype.Component + +@Component +class NamespaceDeleteHandler( + private val namespaceCmdRepository: NamespaceCmdRepository, + private val eventEmitter: InternalEventEmitter +) : RequestHandler(NamespaceDeleteRequested::class) { + override fun invoke(req: NamespaceDeleteRequested) { + deleteNamespace(req) + .also { emitEvent(req.cmdId(), it) } + } + + private fun deleteNamespace(req: NamespaceDeleteRequested): Namespace { + return namespaceCmdRepository.delete( + req.id, NamespaceCmdRepository.DeleteCmd( + id = req.cmdId(), + namespaceId = req.id + ) + ) + } + + private fun emitEvent(cmdId: CmdId, namespace: Namespace) { + eventEmitter.emit(cmdId, NamespaceDeletedEvent(namespace)) + } +} \ No newline at end of file diff --git a/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/Namespace.kt b/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/Namespace.kt index b6050b49a7..3f282836a8 100644 --- a/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/Namespace.kt +++ b/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/Namespace.kt @@ -28,6 +28,8 @@ interface NamespaceCmdRepository : CmdRepository { fun update(namespaceId: NamespaceId, cmd: UpdateCmd): Namespace + fun delete(namespaceId: NamespaceId, cmd: DeleteCmd): Namespace + data class CreateCmd( val id: CmdId, val namespaceId: NamespaceId, @@ -41,6 +43,11 @@ interface NamespaceCmdRepository : CmdRepository { val name: NamespaceName? = null, val features: NamespaceFeatures? = null ) + + data class DeleteCmd( + val id: CmdId, + val namespaceId: NamespaceId, + ) } interface NamespaceQueryRepository { diff --git a/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Event.kt b/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Event.kt index 73ea8e560a..8862b45e7c 100644 --- a/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Event.kt +++ b/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Event.kt @@ -33,6 +33,7 @@ val internalEventClasses = listOf( FeedbackCreatedEvent::class, NamespaceAppendedEvent::class, NamespaceUpdatedEvent::class, + NamespaceDeletedEvent::class, FuncCreatedEvent::class, FuncUpdatedEvent::class, FuncDeployedEvent::class, diff --git a/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Namespace.kt b/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Namespace.kt index 23ab6da0a8..f2512ff40c 100644 --- a/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Namespace.kt +++ b/platform/backend/repository/api/src/main/kotlin/io/hamal/repository/api/event/Namespace.kt @@ -11,3 +11,6 @@ data class NamespaceUpdatedEvent( val namespace: Namespace, ) : InternalEvent() +data class NamespaceDeletedEvent( + val namespace: Namespace, +) : InternalEvent() diff --git a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/ProjectionCurrent.kt b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/ProjectionCurrent.kt index 76f7a7da72..4edd6c9454 100644 --- a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/ProjectionCurrent.kt +++ b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/ProjectionCurrent.kt @@ -9,6 +9,10 @@ import io.hamal.repository.memory.record.ProjectionMemory internal class ProjectionCurrent : ProjectionMemory.BaseImpl() { + fun delete(obj: Namespace) { + projection.remove(obj.id) + } + fun find(namespaceId: NamespaceId): Namespace? = projection[namespaceId] fun find(namespaceName: NamespaceName): Namespace? = projection.values.find { it.name == namespaceName } diff --git a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/Repository.kt b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/Repository.kt index c9a25eef4e..ab483899ee 100644 --- a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/Repository.kt +++ b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace/Repository.kt @@ -3,8 +3,7 @@ package io.hamal.repository.memory.record.namespace import io.hamal.lib.common.domain.Count import io.hamal.lib.domain.vo.NamespaceId import io.hamal.repository.api.Namespace -import io.hamal.repository.api.NamespaceCmdRepository.CreateCmd -import io.hamal.repository.api.NamespaceCmdRepository.UpdateCmd +import io.hamal.repository.api.NamespaceCmdRepository.* import io.hamal.repository.api.NamespaceQueryRepository.NamespaceQuery import io.hamal.repository.api.NamespaceRepository import io.hamal.repository.memory.record.RecordMemoryRepository @@ -58,6 +57,22 @@ class NamespaceMemoryRepository : RecordMemoryRepository = lock.withLock { currentProjection.list(query) } diff --git a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/ProjectionCurrent.kt b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/ProjectionCurrent.kt index e70def6b79..7c9e718f21 100644 --- a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/ProjectionCurrent.kt +++ b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/ProjectionCurrent.kt @@ -64,5 +64,4 @@ internal class ProjectionCurrent : ProjectionMemory.BaseImpl() - } \ No newline at end of file diff --git a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/Repository.kt b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/Repository.kt index 7dd3d3192d..ccf9357b6c 100644 --- a/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/Repository.kt +++ b/platform/backend/repository/memory/src/main/kotlin/io/hamal/repository/memory/record/namespace_tree/Repository.kt @@ -4,7 +4,8 @@ import io.hamal.lib.common.domain.Count import io.hamal.lib.domain.vo.NamespaceId import io.hamal.lib.domain.vo.NamespaceTreeId import io.hamal.repository.api.NamespaceTree -import io.hamal.repository.api.NamespaceTreeCmdRepository +import io.hamal.repository.api.NamespaceTreeCmdRepository.AppendCmd +import io.hamal.repository.api.NamespaceTreeCmdRepository.CreateCmd import io.hamal.repository.api.NamespaceTreeQueryRepository.NamespaceTreeQuery import io.hamal.repository.api.NamespaceTreeRepository import io.hamal.repository.memory.record.RecordMemoryRepository @@ -19,7 +20,7 @@ class NamespaceTreeMemoryRepository : RecordMemoryRepository copy( + id = rec.entityId, + cmdId = rec.cmdId, + sequence = rec.sequence(), + recordedAt = rec.recordedAt() + ) } } diff --git a/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace/Record.kt b/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace/Record.kt index 8e8e93ebe1..a351f18763 100644 --- a/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace/Record.kt +++ b/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace/Record.kt @@ -19,7 +19,8 @@ sealed class NamespaceRecord( internal object Adapter : RecordAdapter( listOf( Created::class, - Updated::class + Updated::class, + Deleted::class ) ) @@ -37,5 +38,10 @@ sealed class NamespaceRecord( val name: NamespaceName, val features: NamespaceFeatures ) : NamespaceRecord() + + data class Deleted( + override val entityId: NamespaceId, + override val cmdId: CmdId + ) : NamespaceRecord() } diff --git a/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace_tree/Entity.kt b/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace_tree/Entity.kt index 5c6e856e35..3f2920384f 100644 --- a/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace_tree/Entity.kt +++ b/platform/backend/repository/record/src/main/kotlin/io/hamal/repository/record/namespace_tree/Entity.kt @@ -45,7 +45,6 @@ data class NamespaceTreeEntity( }, recordedAt = rec.recordedAt() ) - } } diff --git a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/ProjectionCurrent.kt b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/ProjectionCurrent.kt index a51ee575e0..a305b1e8e9 100644 --- a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/ProjectionCurrent.kt +++ b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/ProjectionCurrent.kt @@ -97,6 +97,19 @@ internal object ProjectionCurrent : ProjectionSqlite.CurrentImpl, obj: Namespace) { + tx.execute( + """ + DELETE FROM + current + WHERE + id = :id + """.trimIndent() + ) { + set("id", obj.id) + } + } + override fun setupSchema(connection: Connection) { connection.execute( """ diff --git a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/Repository.kt b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/Repository.kt index ca4a9e873b..9c0fb416af 100644 --- a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/Repository.kt +++ b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace/Repository.kt @@ -3,8 +3,7 @@ package io.hamal.repository.sqlite.record.namespace import io.hamal.lib.common.domain.Count import io.hamal.lib.domain.vo.NamespaceId import io.hamal.repository.api.Namespace -import io.hamal.repository.api.NamespaceCmdRepository -import io.hamal.repository.api.NamespaceCmdRepository.CreateCmd +import io.hamal.repository.api.NamespaceCmdRepository.* import io.hamal.repository.api.NamespaceQueryRepository.NamespaceQuery import io.hamal.repository.api.NamespaceRepository import io.hamal.repository.record.CreateDomainObject @@ -67,7 +66,7 @@ class NamespaceSqliteRepository( } } - override fun update(namespaceId: NamespaceId, cmd: NamespaceCmdRepository.UpdateCmd): Namespace { + override fun update(namespaceId: NamespaceId, cmd: UpdateCmd): Namespace { val cmdId = cmd.id return tx { if (commandAlreadyApplied(cmdId, namespaceId)) { @@ -87,6 +86,22 @@ class NamespaceSqliteRepository( } } + override fun delete(namespaceId: NamespaceId, cmd: DeleteCmd): Namespace { + return tx { + if (commandAlreadyApplied(cmd.id, namespaceId)) { + versionOf(namespaceId, cmd.id) + } else { + store( + NamespaceRecord.Deleted( + entityId = namespaceId, + cmdId = cmd.id + ) + ) + currentVersion(namespaceId).also { ProjectionCurrent.delete(this, it) } + } + } + } + override fun find(namespaceId: NamespaceId): Namespace? { return ProjectionCurrent.find(connection, namespaceId) } diff --git a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/ProjectionCurrent.kt b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/ProjectionCurrent.kt index dd7392d801..9198319cab 100644 --- a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/ProjectionCurrent.kt +++ b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/ProjectionCurrent.kt @@ -37,7 +37,6 @@ internal object ProjectionCurrent : ProjectionSqlite.CurrentImpl { return connection.executeQuery( """ diff --git a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/Repository.kt b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/Repository.kt index 46efd93781..4dc47d63fa 100644 --- a/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/Repository.kt +++ b/platform/backend/repository/sqlite/src/main/kotlin/io/hamal/repository/sqlite/record/namespace_tree/Repository.kt @@ -4,7 +4,7 @@ import io.hamal.lib.common.domain.Count import io.hamal.lib.domain.vo.NamespaceId import io.hamal.lib.domain.vo.NamespaceTreeId import io.hamal.repository.api.NamespaceTree -import io.hamal.repository.api.NamespaceTreeCmdRepository +import io.hamal.repository.api.NamespaceTreeCmdRepository.AppendCmd import io.hamal.repository.api.NamespaceTreeCmdRepository.CreateCmd import io.hamal.repository.api.NamespaceTreeQueryRepository.NamespaceTreeQuery import io.hamal.repository.api.NamespaceTreeRepository @@ -68,7 +68,7 @@ class NamespaceTreeSqliteRepository( } } - override fun append(cmd: NamespaceTreeCmdRepository.AppendCmd): NamespaceTree { + override fun append(cmd: AppendCmd): NamespaceTree { return tx { val treeId = cmd.treeId if (commandAlreadyApplied(cmd.id, treeId)) { @@ -94,7 +94,6 @@ class NamespaceTreeSqliteRepository( override fun list(query: NamespaceTreeQuery): List = ProjectionCurrent.list(connection, query) - override fun count(query: NamespaceTreeQuery): Count = ProjectionCurrent.count(connection, query) } \ No newline at end of file diff --git a/platform/backend/repository/testbed/src/test/kotlin/io/hamal/repository/NamespaceTest.kt b/platform/backend/repository/testbed/src/test/kotlin/io/hamal/repository/NamespaceTest.kt index de56a0de95..8e3aa14fe3 100644 --- a/platform/backend/repository/testbed/src/test/kotlin/io/hamal/repository/NamespaceTest.kt +++ b/platform/backend/repository/testbed/src/test/kotlin/io/hamal/repository/NamespaceTest.kt @@ -12,8 +12,7 @@ import io.hamal.lib.domain.vo.NamespaceName import io.hamal.lib.domain.vo.NamespaceName.Companion.NamespaceName import io.hamal.lib.domain.vo.WorkspaceId import io.hamal.lib.domain.vo.WorkspaceId.Companion.WorkspaceId -import io.hamal.repository.api.NamespaceCmdRepository.CreateCmd -import io.hamal.repository.api.NamespaceCmdRepository.UpdateCmd +import io.hamal.repository.api.NamespaceCmdRepository.* import io.hamal.repository.api.NamespaceQueryRepository.NamespaceQuery import io.hamal.repository.api.NamespaceRepository import io.hamal.repository.fixture.AbstractUnitTest @@ -49,7 +48,7 @@ internal class NamespaceRepositoryTest : AbstractUnitTest() { assertThat(id, equalTo(NamespaceId(234))) assertThat(workspaceId, equalTo(WorkspaceId(1))) assertThat(name, equalTo(NamespaceName("SomeNamespace"))) - + assertTrue(features.isActive(Schedule)) assertFalse(features.isActive(Webhook)) assertFalse(features.isActive(Endpoint)) @@ -91,6 +90,32 @@ internal class NamespaceRepositoryTest : AbstractUnitTest() { } } + @TestFactory + fun `Deletes a namespace`() = runWith(NamespaceRepository::class) { + createNamespace( + cmdId = CmdId(1), + namespaceId = NamespaceId(5), + workspaceId = WorkspaceId(3), + name = NamespaceName("first-namespace-name") + ) + + delete( + NamespaceId(5), + DeleteCmd( + id = CmdId(2), + namespaceId = NamespaceId(5) + ) + ) + + + val exception = assertThrows { + get(NamespaceId(5)) + } + assertThat(exception.message, equalTo("Namespace not found")) + + verifyCount(0) + } + @Nested inner class UpdatesNamespaceTest { diff --git a/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Namespace.kt b/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Namespace.kt index 1d3647dc38..5c86ed21cf 100644 --- a/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Namespace.kt +++ b/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Namespace.kt @@ -32,3 +32,11 @@ data class NamespaceUpdateRequested( val name: NamespaceName?, val features: NamespaceFeatures? ) : Requested() + + +data class NamespaceDeleteRequested( + override val requestId: RequestId, + override val requestedBy: AuthId, + override var requestStatus: RequestStatus, + val id: NamespaceId +) : Requested() \ No newline at end of file diff --git a/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Requested.kt b/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Requested.kt index 0753ba69f7..79188c7cd3 100644 --- a/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Requested.kt +++ b/platform/lib/domain/src/main/kotlin/io/hamal/lib/domain/request/Requested.kt @@ -60,6 +60,7 @@ sealed class Requested { FuncUpdateRequested::class, NamespaceAppendRequested::class, NamespaceUpdateRequested::class, + NamespaceDeleteRequested::class, RecipeCreateRequested::class, RecipeUpdateRequested::class, StateSetRequested::class, diff --git a/platform/lib/sdk/src/main/kotlin/io/hamal/lib/sdk/api/Namespace.kt b/platform/lib/sdk/src/main/kotlin/io/hamal/lib/sdk/api/Namespace.kt index 09c2e439b9..8bce41cb78 100644 --- a/platform/lib/sdk/src/main/kotlin/io/hamal/lib/sdk/api/Namespace.kt +++ b/platform/lib/sdk/src/main/kotlin/io/hamal/lib/sdk/api/Namespace.kt @@ -30,6 +30,14 @@ data class ApiNamespaceUpdateRequested( val id: NamespaceId, ) : ApiRequested() + +data class ApiNamespaceDeleteRequested( + override val requestId: RequestId, + override val requestStatus: RequestStatus, + val id: NamespaceId, +) : ApiRequested() + + data class ApiNamespaceList( val namespaces: List ) : ApiObject() {