diff -r bcf05cccac6f -r 703591e739f4 src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt --- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Mon Oct 30 10:06:22 2023 +0100 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Mon Oct 30 14:44:36 2023 +0100 @@ -25,11 +25,8 @@ package de.uapcore.lightpit.servlet -import de.uapcore.lightpit.AbstractServlet -import de.uapcore.lightpit.HttpRequest -import de.uapcore.lightpit.boolValidator +import de.uapcore.lightpit.* import de.uapcore.lightpit.dao.DataAccessObject -import de.uapcore.lightpit.dateOptValidator import de.uapcore.lightpit.entities.* import de.uapcore.lightpit.types.* import de.uapcore.lightpit.viewmodel.* @@ -86,53 +83,6 @@ } } - private fun activeProjectNavMenu( - projects: List, - projectInfo: ProjectInfo, - selectedVersion: Version? = null, - selectedComponent: Component? = null - ) = - projectNavMenu( - projects, - projectInfo.versions, - projectInfo.components, - projectInfo.project, - selectedVersion, - selectedComponent - ) - - private sealed interface LookupResult - private class NotFound : LookupResult - private data class Found(val elem: T?) : LookupResult - - private fun HttpRequest.lookupPathParam(paramName: String, list: List): LookupResult { - val node = pathParams[paramName] - return if (node == null || node == "-") { - Found(null) - } else { - val result = list.find { it.node == node } - if (result == null) { - NotFound() - } else { - Found(result) - } - } - } - - private fun obtainProjectInfo(http: HttpRequest, dao: DataAccessObject): ProjectInfo? { - val project = dao.findProjectByNode(http.pathParams["project"] ?: "") ?: return null - - val versions: List = dao.listVersions(project) - val components: List = dao.listComponents(project) - - return ProjectInfo( - project, - versions, - components, - dao.collectIssueSummary(project) - ) - } - private fun sanitizeNode(name: String): String { val san = name.replace(Regex("[/\\\\]"), "-") return if (san.startsWith(".")) { @@ -144,46 +94,9 @@ private fun feedPath(project: Project) = "feed/${project.node}/issues.rss" - private data class PathInfos( - val projectInfo: ProjectInfo, - val version: Version?, - val component: Component? - ) { - val project = projectInfo.project - val issuesHref by lazyOf("projects/${project.node}/issues/${version?.node ?: "-"}/${component?.node ?: "-"}/") - } - - private fun withPathInfo(http: HttpRequest, dao: DataAccessObject): PathInfos? { - val projectInfo = obtainProjectInfo(http, dao) - if (projectInfo == null) { - http.response.sendError(404) - return null - } - - val version = when (val result = http.lookupPathParam("version", projectInfo.versions)) { - is NotFound -> { - http.response.sendError(404) - return null - } - is Found -> { - result.elem - } - } - val component = when (val result = http.lookupPathParam("component", projectInfo.components)) { - is NotFound -> { - http.response.sendError(404) - return null - } - is Found -> { - result.elem - } - } - - return PathInfos(projectInfo, version, component) - } - private fun project(http: HttpRequest, dao: DataAccessObject) { - withPathInfo(http, dao)?.run { + withPathInfo(http, dao)?.let {path -> + val project = path.projectInfo.project val filter = IssueFilter(http) @@ -191,7 +104,12 @@ val relationsMap = if (needRelationsMap) dao.getIssueRelationMap(project, filter.includeDone) else emptyMap() - val issues = dao.listIssues(project, filter.includeDone, version, component) + val specificVersion = path.versionInfo !is OptionalPathInfo.All + val version = if (path.versionInfo is OptionalPathInfo.Specific) path.versionInfo.elem else null + val specificComponent = path.componentInfo !is OptionalPathInfo.All + val component = if (path.componentInfo is OptionalPathInfo.Specific) path.componentInfo.elem else null + + val issues = dao.listIssues(project, filter.includeDone, specificVersion, version, specificComponent, component) .sortedWith(IssueSorter(filter.sortPrimary, filter.sortSecondary, filter.sortTertiary)) .filter { (!filter.onlyMine || (it.assignee?.username ?: "") == (http.remoteUser ?: "")) && @@ -202,14 +120,9 @@ with(http) { pageTitle = project.name - view = ProjectDetails(projectInfo, issues, filter, version, component) + view = ProjectDetails(path, issues, filter) feedPath = feedPath(project) - navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo, - version, - component - ) + navigationMenu = projectNavMenu(dao.listProjects(), path) styleSheets = listOf("projects") javascript = "project-details" render("project-details") @@ -218,23 +131,18 @@ } private fun projectForm(http: HttpRequest, dao: DataAccessObject) { + http.styleSheets = listOf("projects") if (!http.pathParams.containsKey("project")) { http.view = ProjectEditView(Project(-1), dao.listUsers()) http.navigationMenu = projectNavMenu(dao.listProjects()) + http.render("project-form") } else { - val projectInfo = obtainProjectInfo(http, dao) - if (projectInfo == null) { - http.response.sendError(404) - return + withPathInfo(http, dao)?.let { path -> + http.view = ProjectEditView(path.projectInfo.project, dao.listUsers()) + http.navigationMenu = projectNavMenu(dao.listProjects(), path) + http.render("project-form") } - http.view = ProjectEditView(projectInfo.project, dao.listUsers()) - http.navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo - ) } - http.styleSheets = listOf("projects") - http.render("project-form") } private fun projectCommit(http: HttpRequest, dao: DataAccessObject) { @@ -264,77 +172,50 @@ } private fun vcsAnalyze(http: HttpRequest, dao: DataAccessObject) { - val projectInfo = obtainProjectInfo(http, dao) - if (projectInfo == null) { - http.response.sendError(404) - return - } + withPathInfo(http, dao)?.let { path -> + // if analysis is not configured, reject the request + if (path.projectInfo.project.vcs == VcsType.None) { + http.response.sendError(404) + return + } - // if analysis is not configured, reject the request - if (projectInfo.project.vcs == VcsType.None) { - http.response.sendError(404) - return - } + // obtain the list of issues for this project to filter cross-project references + val knownIds = dao.listIssues(path.projectInfo.project, true).map { it.id } - // obtain the list of issues for this project to filter cross-project references - val knownIds = dao.listIssues(projectInfo.project, true).map { it.id } - - // read the provided commit log and merge only the refs that relate issues from the current project - dao.mergeCommitRefs(parseCommitRefs(http.body).filter { knownIds.contains(it.issueId) }) + // read the provided commit log and merge only the refs that relate issues from the current project + dao.mergeCommitRefs(parseCommitRefs(http.body).filter { knownIds.contains(it.issueId) }) + } } private fun versions(http: HttpRequest, dao: DataAccessObject) { - val projectInfo = obtainProjectInfo(http, dao) - if (projectInfo == null) { - http.response.sendError(404) - return - } - - with(http) { - pageTitle = "${projectInfo.project.name} - ${i18n("navmenu.versions")}" - view = VersionsView( - projectInfo, - dao.listVersionSummaries(projectInfo.project) - ) - feedPath = feedPath(projectInfo.project) - navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo - ) - styleSheets = listOf("projects") - javascript = "project-details" - render("versions") + withPathInfo(http, dao)?.let { path -> + with(http) { + pageTitle = "${path.projectInfo.project.name} - ${i18n("navmenu.versions")}" + view = VersionsView( + path.projectInfo, + dao.listVersionSummaries(path.projectInfo.project) + ) + feedPath = feedPath(path.projectInfo.project) + navigationMenu = projectNavMenu(dao.listProjects(), path) + styleSheets = listOf("projects") + javascript = "project-details" + render("versions") + } } } private fun versionForm(http: HttpRequest, dao: DataAccessObject) { - val projectInfo = obtainProjectInfo(http, dao) - if (projectInfo == null) { - http.response.sendError(404) - return - } + withPathInfo(http, dao)?.let { path -> + val version = if (path.versionInfo is OptionalPathInfo.Specific) + path.versionInfo.elem else Version(-1, path.projectInfo.project.id) - val version: Version - when (val result = http.lookupPathParam("version", projectInfo.versions)) { - is NotFound -> { - http.response.sendError(404) - return + with(http) { + view = VersionEditView(path.projectInfo, version) + feedPath = feedPath(path.projectInfo.project) + navigationMenu = projectNavMenu(dao.listProjects(), path) + styleSheets = listOf("projects") + render("version-form") } - is Found -> { - version = result.elem ?: Version(-1, projectInfo.project.id) - } - } - - with(http) { - view = VersionEditView(projectInfo, version) - feedPath = feedPath(projectInfo.project) - navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo, - selectedVersion = version - ) - styleSheets = listOf("projects") - render("version-form") } } @@ -385,57 +266,34 @@ } private fun components(http: HttpRequest, dao: DataAccessObject) { - val projectInfo = obtainProjectInfo(http, dao) - if (projectInfo == null) { - http.response.sendError(404) - return - } - - with(http) { - pageTitle = "${projectInfo.project.name} - ${i18n("navmenu.components")}" - view = ComponentsView( - projectInfo, - dao.listComponentSummaries(projectInfo.project) - ) - feedPath = feedPath(projectInfo.project) - navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo - ) - styleSheets = listOf("projects") - javascript = "project-details" - render("components") + withPathInfo(http, dao)?.let { path -> + with(http) { + pageTitle = "${path.projectInfo.project.name} - ${i18n("navmenu.components")}" + view = ComponentsView( + path.projectInfo, + dao.listComponentSummaries(path.projectInfo.project) + ) + feedPath = feedPath(path.projectInfo.project) + navigationMenu = projectNavMenu(dao.listProjects(), path) + styleSheets = listOf("projects") + javascript = "project-details" + render("components") + } } } private fun componentForm(http: HttpRequest, dao: DataAccessObject) { - val projectInfo = obtainProjectInfo(http, dao) - if (projectInfo == null) { - http.response.sendError(404) - return - } + withPathInfo(http, dao)?.let { path -> + val component = if (path.componentInfo is OptionalPathInfo.Specific) + path.componentInfo.elem else Component(-1, path.projectInfo.project.id) - val component: Component - when (val result = http.lookupPathParam("component", projectInfo.components)) { - is NotFound -> { - http.response.sendError(404) - return + with(http) { + view = ComponentEditView(path.projectInfo, component, dao.listUsers()) + feedPath = feedPath(path.projectInfo.project) + navigationMenu = projectNavMenu(dao.listProjects(), path) + styleSheets = listOf("projects") + render("component-form") } - is Found -> { - component = result.elem ?: Component(-1, projectInfo.project.id) - } - } - - with(http) { - view = ComponentEditView(projectInfo, component, dao.listUsers()) - feedPath = feedPath(projectInfo.project) - navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo, - selectedComponent = component - ) - styleSheets = listOf("projects") - render("component-form") } } @@ -484,29 +342,23 @@ issue: Issue, relationError: String? = null ) { - withPathInfo(http, dao)?.run { + withPathInfo(http, dao)?.let {path -> val comments = dao.listComments(issue) with(http) { - pageTitle = "${projectInfo.project.name}: #${issue.id} ${issue.subject}" + pageTitle = "${path.projectInfo.project.name}: #${issue.id} ${issue.subject}" view = IssueDetailView( + path, issue, comments, - project, - version, - component, - dao.listIssues(project, true), + path.projectInfo.project, + dao.listIssues(path.projectInfo.project, true), dao.listIssueRelations(issue), relationError, dao.listCommitRefs(issue) ) - feedPath = feedPath(projectInfo.project) - navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo, - version, - component - ) + feedPath = feedPath(path.projectInfo.project) + navigationMenu = projectNavMenu(dao.listProjects(), path) styleSheets = listOf("projects") javascript = "issue-editor" render("issue-view") @@ -515,23 +367,25 @@ } private fun issueForm(http: HttpRequest, dao: DataAccessObject) { - withPathInfo(http, dao)?.run { + withPathInfo(http, dao)?.let { path -> val issue = http.pathParams["issue"]?.toIntOrNull()?.let(dao::findIssue) ?: Issue( -1, - project, + path.projectInfo.project, ) // for new issues set some defaults if (issue.id < 0) { // pre-select component, if available in the path info - issue.component = component + if (path.componentInfo is OptionalPathInfo.Specific) { + issue.component = path.componentInfo.elem + } // pre-select version, if available in the path info - if (version != null) { - if (version.status.isReleased) { - issue.affected = version + if (path.versionInfo is OptionalPathInfo.Specific) { + if (path.versionInfo.elem.status.isReleased) { + issue.affected = path.versionInfo.elem } else { - issue.resolved = version + issue.resolved = path.versionInfo.elem } } } @@ -539,20 +393,14 @@ with(http) { view = IssueEditView( issue, - projectInfo.versions, - projectInfo.components, + path.projectInfo.versions, + path.projectInfo.components, dao.listUsers(), - project, - version, - component + path.projectInfo.project, + path ) - feedPath = feedPath(projectInfo.project) - navigationMenu = activeProjectNavMenu( - dao.listProjects(), - projectInfo, - version, - component - ) + feedPath = feedPath(path.projectInfo.project) + navigationMenu = projectNavMenu(dao.listProjects(), path) styleSheets = listOf("projects") javascript = "issue-editor" render("issue-form") @@ -606,7 +454,7 @@ withPathInfo(http, dao)?.run { val issue = Issue( http.param("id")?.toIntOrNull() ?: -1, - project + projectInfo.project ).apply { component = dao.findComponent(http.param("component")?.toIntOrNull() ?: -1) category = IssueCategory.valueOf(http.param("category") ?: "")