src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt

changeset 263
aa22103809cd
parent 254
55ca6cafc3dd
child 265
6a21bb926e02
--- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Fri Dec 30 13:21:09 2022 +0100
+++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt	Fri Dec 30 19:04:34 2022 +0100
@@ -31,10 +31,7 @@
 import de.uapcore.lightpit.dao.DataAccessObject
 import de.uapcore.lightpit.dateOptValidator
 import de.uapcore.lightpit.entities.*
-import de.uapcore.lightpit.types.IssueCategory
-import de.uapcore.lightpit.types.IssueStatus
-import de.uapcore.lightpit.types.VersionStatus
-import de.uapcore.lightpit.types.WebColor
+import de.uapcore.lightpit.types.*
 import de.uapcore.lightpit.viewmodel.*
 import jakarta.servlet.annotation.WebServlet
 import java.sql.Date
@@ -63,6 +60,8 @@
         get("/%project/issues/%version/%component/%issue", this::issue)
         get("/%project/issues/%version/%component/%issue/edit", this::issueForm)
         post("/%project/issues/%version/%component/%issue/comment", this::issueComment)
+        post("/%project/issues/%version/%component/%issue/relation", this::issueRelation)
+        get("/%project/issues/%version/%component/%issue/removeRelation", this::issueRemoveRelation)
         get("/%project/issues/%version/%component/-/create", this::issueForm)
         post("/%project/issues/%version/%component/-/commit", this::issueCommit)
     }
@@ -440,18 +439,35 @@
     }
 
     private fun issue(http: HttpRequest, dao: DataAccessObject) {
+        val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
+        if (issue == null) {
+            http.response.sendError(404)
+            return
+        }
+        renderIssueView(http, dao, issue)
+    }
+
+    private fun renderIssueView(
+        http: HttpRequest,
+        dao: DataAccessObject,
+        issue: Issue,
+        relationError: String? = null
+    ) {
         withPathInfo(http, dao)?.run {
-            val issue = dao.findIssue(http.pathParams["issue"]?.toIntOrNull() ?: -1)
-            if (issue == null) {
-                http.response.sendError(404)
-                return
-            }
-
             val comments = dao.listComments(issue)
 
             with(http) {
                 pageTitle = "${projectInfo.project.name}: #${issue.id} ${issue.subject}"
-                view = IssueDetailView(issue, comments, project, version, component)
+                view = IssueDetailView(
+                    issue,
+                    comments,
+                    project,
+                    version,
+                    component,
+                    dao.listIssues(project),
+                    dao.listIssueRelations(issue),
+                    relationError
+                )
                 feedPath = feedPath(projectInfo.project)
                 navigationMenu = activeProjectNavMenu(
                     dao.listProjects(),
@@ -468,7 +484,7 @@
 
     private fun issueForm(http: HttpRequest, dao: DataAccessObject) {
         withPathInfo(http, dao)?.run {
-            val issue = dao.findIssue(http.pathParams["issue"]?.toIntOrNull() ?: -1) ?: Issue(
+            val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue) ?: Issue(
                 -1,
                 project,
             )
@@ -514,7 +530,7 @@
 
     private fun issueComment(http: HttpRequest, dao: DataAccessObject) {
         withPathInfo(http, dao)?.run {
-            val issue = dao.findIssue(http.pathParams["issue"]?.toIntOrNull() ?: -1)
+            val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
             if (issue == null) {
                 http.response.sendError(404)
                 return
@@ -616,4 +632,88 @@
             }
         }
     }
+
+    private fun issueRelation(http: HttpRequest, dao: DataAccessObject) {
+        withPathInfo(http, dao)?.run {
+            val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
+            if (issue == null) {
+                http.response.sendError(404)
+                return
+            }
+            
+            // determine the relation type
+            val type: Pair<RelationType, Boolean>? = http.param("type")?.let {
+                try {
+                    if (it.startsWith("!")) {
+                        Pair(RelationType.valueOf(it.substring(1)), true)
+                    } else {
+                        Pair(RelationType.valueOf(it), false)
+                    }
+                } catch (_: IllegalArgumentException) {
+                    null
+                }
+            }
+            
+            // if the relation type was invalid, send HTTP 500
+            if (type == null) {
+                http.response.sendError(500)
+                return
+            }
+            
+            // determine the target issue
+            val targetIssue: Issue? = http.param("issue")?.let {
+                if (it.startsWith("#") && it.length > 1) {
+                    it.substring(1).split(" ", limit = 2)[0].toIntOrNull()
+                        ?.let(dao::findIssue)
+                        ?.takeIf { target -> target.project.id == issue.project.id }
+                } else {
+                    null
+                }
+            }
+
+            // check if the target issue is valid
+            if (targetIssue == null) {
+                renderIssueView(http, dao, issue, "issue.relations.target.invalid")
+                return
+            }
+            
+            // commit the result
+            dao.insertIssueRelation(IssueRelation(issue, targetIssue, type.first, type.second))
+            http.renderCommit("${issuesHref}${issue.id}")
+        }
+    }
+
+    private fun issueRemoveRelation(http: HttpRequest, dao: DataAccessObject) {
+        withPathInfo(http, dao)?.run {
+            val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue)
+            if (issue == null) {
+                http.response.sendError(404)
+                return
+            }
+
+            // determine relation
+            val type = http.param("type")?.let {
+                try {RelationType.valueOf(it)}
+                catch (_:IllegalArgumentException) {null}
+            }
+            if (type == null) {
+                http.response.sendError(500)
+                return
+            }
+            val rel = http.param("to")?.toIntOrNull()?.let(dao::findIssue)?.let {
+                IssueRelation(
+                    issue,
+                    it,
+                    type,
+                    http.param("reverse")?.toBoolean() ?: false
+                )
+            }
+
+            // execute removal, if there is something to remove
+            rel?.run(dao::deleteIssueRelation)
+
+            // always pretend that the operation was successful - if there was nothing to remove, it's okay
+            http.renderCommit("${issuesHref}${issue.id}")
+        }
+    }
 }
\ No newline at end of file

mercurial