Fri, 09 Oct 2020 11:43:15 +0200
zero issues in a version should be displayed as DONE and not TODO
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2018 Mike Becker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package de.uapcore.lightpit.modules; import de.uapcore.lightpit.*; import de.uapcore.lightpit.dao.DataAccessObjects; import de.uapcore.lightpit.entities.*; import de.uapcore.lightpit.viewmodel.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Date; import java.sql.SQLException; import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import static de.uapcore.lightpit.Functions.fqn; @WebServlet( name = "ProjectsModule", urlPatterns = "/projects/*" ) public final class ProjectsModule extends AbstractLightPITServlet { private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class); private static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project"); private static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version"); private static final String PARAMETER_SELECTED_PROJECT = "pid"; private static final String PARAMETER_SELECTED_VERSION = "vid"; @Override protected String getResourceBundleName() { return "localization.projects"; } private int syncParamWithSession(HttpServletRequest req, String param, String attr) { final var session = req.getSession(); final var idParam = getParameter(req, Integer.class, param); final int id; if (idParam.isPresent()) { id = idParam.get(); session.setAttribute(attr, id); } else { id = Optional.ofNullable(session.getAttribute(attr)).map(x->(Integer)x).orElse(-1); } return id; } private void populate(ProjectView viewModel, HttpServletRequest req, DataAccessObjects dao) throws SQLException { final var projectDao = dao.getProjectDao(); final var versionDao = dao.getVersionDao(); projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add); // Select Project final int pid = syncParamWithSession(req, PARAMETER_SELECTED_PROJECT, SESSION_ATTR_SELECTED_PROJECT); if (pid >= 0) { final var project = projectDao.find(pid); if (project == null) { req.setAttribute(SESSION_ATTR_SELECTED_PROJECT, -1); } else { final var info = new ProjectInfo(project); info.setVersions(versionDao.list(project)); info.setIssueSummary(projectDao.getIssueSummary(project)); viewModel.setProjectInfo(info); } } // Select Version final int vid = syncParamWithSession(req, PARAMETER_SELECTED_VERSION, SESSION_ATTR_SELECTED_VERSION); if (vid >= 0) { viewModel.setVersionFilter(versionDao.find(vid)); } } private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) { setViewModel(req, viewModel); setContentPage(req, name); setStylesheet(req, "projects"); setNavigationMenu(req, "project-navmenu"); return ResponseType.HTML; } @RequestMapping(method = HttpMethod.GET) public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { final var viewModel = new ProjectView(); populate(viewModel, req, dao); final var projectDao = dao.getProjectDao(); final var versionDao = dao.getVersionDao(); for (var info : viewModel.getProjectList()) { info.setVersions(versionDao.list(info.getProject())); info.setIssueSummary(projectDao.getIssueSummary(info.getProject())); } return forwardView(req, viewModel, "projects"); } private void configure(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException { viewModel.setProject(project); viewModel.setUsers(dao.getUserDao().list()); } @RequestMapping(requestPath = "edit", method = HttpMethod.GET) public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { final var viewModel = new ProjectEditView(); populate(viewModel, req, dao); final var project = Optional.ofNullable(viewModel.getProjectInfo()) .map(ProjectInfo::getProject) .orElse(new Project(-1)); configure(viewModel, project, dao); return forwardView(req, viewModel, "project-form"); } @RequestMapping(requestPath = "commit", method = HttpMethod.POST) public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { Project project = new Project(-1); try { project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); project.setName(getParameter(req, String.class, "name").orElseThrow()); getParameter(req, String.class, "description").ifPresent(project::setDescription); getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl); getParameter(req, Integer.class, "owner").map( ownerId -> ownerId >= 0 ? new User(ownerId) : null ).ifPresent(project::setOwner); dao.getProjectDao().saveOrUpdate(project); setRedirectLocation(req, "./projects/versions?pid="+project.getId()); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); LOG.debug("Successfully updated project {}", project.getName()); return ResponseType.HTML; } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { LOG.warn("Form validation failure: {}", ex.getMessage()); LOG.debug("Details:", ex); final var viewModel = new ProjectEditView(); populate(viewModel, req, dao); configure(viewModel, project, dao); // TODO: error text return forwardView(req, viewModel, "project-form"); } } @RequestMapping(requestPath = "view", method = HttpMethod.GET) public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { final var viewModel = new ProjectDetailsView(); populate(viewModel, req, dao); if (viewModel.getProjectInfo() == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); return ResponseType.NONE; } final var issueDao = dao.getIssueDao(); final var version = viewModel.getVersionFilter(); final var detailView = viewModel.getProjectDetails(); final var issues = issueDao.list(version); for (var issue : issues) issueDao.joinVersionInformation(issue); detailView.updateDetails(issues, version); return forwardView(req, viewModel, "project-details"); } @RequestMapping(requestPath = "versions", method = HttpMethod.GET) public ResponseType versions(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { final var viewModel = new VersionsView(); populate(viewModel, req, dao); viewModel.setVersionFilter(null); final var projectInfo = viewModel.getProjectInfo(); if (projectInfo == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); return ResponseType.NONE; } final var issueDao = dao.getIssueDao(); final var issues = issueDao.list(projectInfo.getProject()); for (var issue : issues) issueDao.joinVersionInformation(issue); viewModel.update(projectInfo.getVersions(), issues); return forwardView(req, viewModel, "versions"); } @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET) public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { final var viewModel = new VersionEditView(); populate(viewModel, req, dao); if (viewModel.getProjectInfo() == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); return ResponseType.NONE; } if (viewModel.getVersionFilter() == null) { final var version = new Version(-1); version.setProject(viewModel.getProjectInfo().getProject()); viewModel.setVersion(version); } else { viewModel.setVersion(viewModel.getVersionFilter()); } return forwardView(req, viewModel, "version-form"); } @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST) public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { var version = new Version(-1); try { version = new Version(getParameter(req, Integer.class, "id").orElseThrow()); version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); version.setName(getParameter(req, String.class, "name").orElseThrow()); getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); dao.getVersionDao().saveOrUpdate(version); // specifying the pid parameter will purposely reset the session selected version! setRedirectLocation(req, "./projects/versions?pid=" + version.getProject().getId()); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { LOG.warn("Form validation failure: {}", ex.getMessage()); LOG.debug("Details:", ex); final var viewModel = new VersionEditView(); populate(viewModel, req, dao); viewModel.setVersion(version); // TODO: set Error Text return forwardView(req, viewModel, "version-form"); } return ResponseType.HTML; } private void configure(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException { issue.setProject(viewModel.getProjectInfo().getProject()); viewModel.setIssue(issue); viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions()); viewModel.setUsers(dao.getUserDao().list()); } @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET) public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { final var viewModel = new IssueEditView(); final var issueParam = getParameter(req, Integer.class, "issue"); if (issueParam.isPresent()) { final var issueDao = dao.getIssueDao(); final var issue = issueDao.find(issueParam.get()); issueDao.joinVersionInformation(issue); req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, issue.getProject().getId()); populate(viewModel, req, dao); configure(viewModel, issue, dao); } else { populate(viewModel, req, dao); configure(viewModel, new Issue(-1), dao); } return forwardView(req, viewModel, "issue-form"); } @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST) public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { Issue issue = new Issue(-1); try { issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow()); issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory); getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus); issue.setSubject(getParameter(req, String.class, "subject").orElseThrow()); getParameter(req, Integer.class, "assignee").map( userid -> userid >= 0 ? new User(userid) : null ).ifPresent(issue::setAssignee); getParameter(req, String.class, "description").ifPresent(issue::setDescription); getParameter(req, Date.class, "eta").ifPresent(issue::setEta); getParameter(req, Integer[].class, "affected") .map(Stream::of) .map(stream -> stream.map(Version::new).collect(Collectors.toList()) ).ifPresent(issue::setAffectedVersions); getParameter(req, Integer[].class, "resolved") .map(Stream::of) .map(stream -> stream.map(Version::new).collect(Collectors.toList()) ).ifPresent(issue::setResolvedVersions); dao.getIssueDao().saveOrUpdate(issue); // specifying the issue parameter keeps the edited issue as menu item setRedirectLocation(req, "./projects/view?pid=" + issue.getProject().getId()); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { // TODO: set request attribute with error text LOG.warn("Form validation failure: {}", ex.getMessage()); LOG.debug("Details:", ex); final var viewModel = new IssueEditView(); configure(viewModel, issue, dao); // TODO: set Error Text return forwardView(req, viewModel, "issue-form"); } return ResponseType.HTML; } }