src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java

Fri, 09 Oct 2020 11:46:10 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 09 Oct 2020 11:46:10 +0200
changeset 117
874ff787949b
parent 110
9d0be0b1580f
child 118
d3583959c875
permissions
-rw-r--r--

fixes New Project button not resetting the session cached project ID

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2018 Mike Becker. All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  *
    28  */
    29 package de.uapcore.lightpit.modules;
    32 import de.uapcore.lightpit.*;
    33 import de.uapcore.lightpit.dao.DataAccessObjects;
    34 import de.uapcore.lightpit.entities.*;
    35 import de.uapcore.lightpit.viewmodel.*;
    36 import org.slf4j.Logger;
    37 import org.slf4j.LoggerFactory;
    39 import javax.servlet.annotation.WebServlet;
    40 import javax.servlet.http.HttpServletRequest;
    41 import javax.servlet.http.HttpServletResponse;
    42 import java.io.IOException;
    43 import java.sql.Date;
    44 import java.sql.SQLException;
    45 import java.util.NoSuchElementException;
    46 import java.util.Optional;
    47 import java.util.stream.Collectors;
    48 import java.util.stream.Stream;
    50 import static de.uapcore.lightpit.Functions.fqn;
    52 @WebServlet(
    53         name = "ProjectsModule",
    54         urlPatterns = "/projects/*"
    55 )
    56 public final class ProjectsModule extends AbstractLightPITServlet {
    58     private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
    60     private static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project");
    61     private static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version");
    62     private static final String PARAMETER_SELECTED_PROJECT = "pid";
    63     private static final String PARAMETER_SELECTED_VERSION = "vid";
    65     @Override
    66     protected String getResourceBundleName() {
    67         return "localization.projects";
    68     }
    70     private int syncParamWithSession(HttpServletRequest req, String param, String attr) {
    71         final var session = req.getSession();
    72         final var idParam = getParameter(req, Integer.class, param);
    73         final int id;
    74         if (idParam.isPresent()) {
    75             id = idParam.get();
    76             session.setAttribute(attr, id);
    77         } else {
    78             id = Optional.ofNullable(session.getAttribute(attr)).map(x->(Integer)x).orElse(-1);
    79         }
    80         return id;
    81     }
    83     private void populate(ProjectView viewModel, HttpServletRequest req, DataAccessObjects dao) throws SQLException {
    84         final var projectDao = dao.getProjectDao();
    85         final var versionDao = dao.getVersionDao();
    87         projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
    89         // Select Project
    90         final int pid = syncParamWithSession(req, PARAMETER_SELECTED_PROJECT, SESSION_ATTR_SELECTED_PROJECT);
    91         if (pid >= 0) {
    92             final var project = projectDao.find(pid);
    93             if (project == null) {
    94                 req.setAttribute(SESSION_ATTR_SELECTED_PROJECT, -1);
    95             } else {
    96                 final var info = new ProjectInfo(project);
    97                 info.setVersions(versionDao.list(project));
    98                 info.setIssueSummary(projectDao.getIssueSummary(project));
    99                 viewModel.setProjectInfo(info);
   100             }
   101         }
   103         // Select Version
   104         final int vid = syncParamWithSession(req, PARAMETER_SELECTED_VERSION, SESSION_ATTR_SELECTED_VERSION);
   105         if (vid >= 0) {
   106             viewModel.setVersionFilter(versionDao.find(vid));
   107         }
   108     }
   110     private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) {
   111         setViewModel(req, viewModel);
   112         setContentPage(req, name);
   113         setStylesheet(req, "projects");
   114         setNavigationMenu(req, "project-navmenu");
   115         return ResponseType.HTML;
   116     }
   118     @RequestMapping(method = HttpMethod.GET)
   119     public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   120         final var viewModel = new ProjectView();
   121         populate(viewModel, req, dao);
   123         final var projectDao = dao.getProjectDao();
   124         final var versionDao = dao.getVersionDao();
   126         for (var info : viewModel.getProjectList()) {
   127             info.setVersions(versionDao.list(info.getProject()));
   128             info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
   129         }
   131         return forwardView(req, viewModel, "projects");
   132     }
   134     private void configure(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
   135         viewModel.setProject(project);
   136         viewModel.setUsers(dao.getUserDao().list());
   137     }
   139     @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
   140     public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   141         final var viewModel = new ProjectEditView();
   142         populate(viewModel, req, dao);
   144         final var project = Optional.ofNullable(viewModel.getProjectInfo())
   145                 .map(ProjectInfo::getProject)
   146                 .orElse(new Project(-1));
   147         configure(viewModel, project, dao);
   149         return forwardView(req, viewModel, "project-form");
   150     }
   152     @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   153     public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   155         Project project = new Project(-1);
   156         try {
   157             project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   158             project.setName(getParameter(req, String.class, "name").orElseThrow());
   159             getParameter(req, String.class, "description").ifPresent(project::setDescription);
   160             getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
   161             getParameter(req, Integer.class, "owner").map(
   162                     ownerId -> ownerId >= 0 ? new User(ownerId) : null
   163             ).ifPresent(project::setOwner);
   165             dao.getProjectDao().saveOrUpdate(project);
   167             setRedirectLocation(req, "./projects/versions?pid="+project.getId());
   168             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   169             LOG.debug("Successfully updated project {}", project.getName());
   171             return ResponseType.HTML;
   172         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   173             LOG.warn("Form validation failure: {}", ex.getMessage());
   174             LOG.debug("Details:", ex);
   175             final var viewModel = new ProjectEditView();
   176             populate(viewModel, req, dao);
   177             configure(viewModel, project, dao);
   178             // TODO: error text
   179             return forwardView(req, viewModel, "project-form");
   180         }
   181     }
   183     @RequestMapping(requestPath = "view", method = HttpMethod.GET)
   184     public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
   185         final var viewModel = new ProjectDetailsView();
   186         populate(viewModel, req, dao);
   188         if (viewModel.getProjectInfo() == null) {
   189             resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
   190             return ResponseType.NONE;
   191         }
   193         final var issueDao = dao.getIssueDao();
   195         final var version = viewModel.getVersionFilter();
   197         final var detailView = viewModel.getProjectDetails();
   198         final var issues = issueDao.list(version);
   199         for (var issue : issues) issueDao.joinVersionInformation(issue);
   200         detailView.updateDetails(issues, version);
   202         return forwardView(req, viewModel, "project-details");
   203     }
   205     @RequestMapping(requestPath = "versions", method = HttpMethod.GET)
   206     public ResponseType versions(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
   207         final var viewModel = new VersionsView();
   208         populate(viewModel, req, dao);
   209         viewModel.setVersionFilter(null);
   211         final var projectInfo = viewModel.getProjectInfo();
   212         if (projectInfo == null) {
   213             resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
   214             return ResponseType.NONE;
   215         }
   217         final var issueDao = dao.getIssueDao();
   218         final var issues = issueDao.list(projectInfo.getProject());
   219         for (var issue : issues) issueDao.joinVersionInformation(issue);
   220         viewModel.update(projectInfo.getVersions(), issues);
   222         return forwardView(req, viewModel, "versions");
   223     }
   225     @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
   226     public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
   227         final var viewModel = new VersionEditView();
   228         populate(viewModel, req, dao);
   230         if (viewModel.getProjectInfo() == null) {
   231             resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
   232             return ResponseType.NONE;
   233         }
   235         if (viewModel.getVersionFilter() == null) {
   236             final var version = new Version(-1);
   237             version.setProject(viewModel.getProjectInfo().getProject());
   238             viewModel.setVersion(version);
   239         } else {
   240             viewModel.setVersion(viewModel.getVersionFilter());
   241         }
   243         return forwardView(req, viewModel, "version-form");
   244     }
   246     @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
   247     public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   249         var version = new Version(-1);
   250         try {
   251             version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
   252             version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
   253             version.setName(getParameter(req, String.class, "name").orElseThrow());
   254             getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
   255             version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
   256             dao.getVersionDao().saveOrUpdate(version);
   258             // specifying the pid parameter will purposely reset the session selected version!
   259             setRedirectLocation(req, "./projects/versions?pid=" + version.getProject().getId());
   260             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   261         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   262             LOG.warn("Form validation failure: {}", ex.getMessage());
   263             LOG.debug("Details:", ex);
   264             final var viewModel = new VersionEditView();
   265             populate(viewModel, req, dao);
   266             viewModel.setVersion(version);
   267             // TODO: set Error Text
   268             return forwardView(req, viewModel, "version-form");
   269         }
   271         return ResponseType.HTML;
   272     }
   274     private void configure(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
   275         issue.setProject(viewModel.getProjectInfo().getProject());
   276         viewModel.setIssue(issue);
   277         viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
   278         viewModel.setUsers(dao.getUserDao().list());
   279     }
   281     @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
   282     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   283         final var viewModel = new IssueEditView();
   285         final var issueParam = getParameter(req, Integer.class, "issue");
   286         if (issueParam.isPresent()) {
   287             final var issueDao = dao.getIssueDao();
   288             final var issue = issueDao.find(issueParam.get());
   289             issueDao.joinVersionInformation(issue);
   290             req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, issue.getProject().getId());
   291             populate(viewModel, req, dao);
   292             configure(viewModel, issue, dao);
   293         } else {
   294             populate(viewModel, req, dao);
   295             configure(viewModel, new Issue(-1), dao);
   296         }
   298         return forwardView(req, viewModel, "issue-form");
   299     }
   301     @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
   302     public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
   303         Issue issue = new Issue(-1);
   304         try {
   305             issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
   306             issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
   307             getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
   308             getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
   309             issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
   310             getParameter(req, Integer.class, "assignee").map(
   311                     userid -> userid >= 0 ? new User(userid) : null
   312             ).ifPresent(issue::setAssignee);
   313             getParameter(req, String.class, "description").ifPresent(issue::setDescription);
   314             getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
   316             getParameter(req, Integer[].class, "affected")
   317                     .map(Stream::of)
   318                     .map(stream ->
   319                             stream.map(Version::new).collect(Collectors.toList())
   320                     ).ifPresent(issue::setAffectedVersions);
   321             getParameter(req, Integer[].class, "resolved")
   322                     .map(Stream::of)
   323                     .map(stream ->
   324                             stream.map(Version::new).collect(Collectors.toList())
   325                     ).ifPresent(issue::setResolvedVersions);
   327             dao.getIssueDao().saveOrUpdate(issue);
   329             // specifying the issue parameter keeps the edited issue as menu item
   330             setRedirectLocation(req, "./projects/view?pid=" + issue.getProject().getId());
   331             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   332         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   333             // TODO: set request attribute with error text
   334             LOG.warn("Form validation failure: {}", ex.getMessage());
   335             LOG.debug("Details:", ex);
   336             final var viewModel = new IssueEditView();
   337             configure(viewModel, issue, dao);
   338             // TODO: set Error Text
   339             return forwardView(req, viewModel, "issue-form");
   340         }
   342         return ResponseType.HTML;
   343     }
   344 }

mercurial