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

Fri, 23 Oct 2020 13:29:33 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 23 Oct 2020 13:29:33 +0200
changeset 146
b0e83cab0bde
parent 141
8160dfc4dbc3
child 150
822b7e3d064d
permissions
-rw-r--r--

adds issue detail view - fixes #24

     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.types.WebColor;
    36 import de.uapcore.lightpit.viewmodel.*;
    37 import de.uapcore.lightpit.viewmodel.util.IssueSorter;
    38 import org.slf4j.Logger;
    39 import org.slf4j.LoggerFactory;
    41 import javax.servlet.annotation.WebServlet;
    42 import javax.servlet.http.HttpServletRequest;
    43 import javax.servlet.http.HttpServletResponse;
    44 import java.io.IOException;
    45 import java.sql.Date;
    46 import java.sql.SQLException;
    47 import java.util.List;
    48 import java.util.NoSuchElementException;
    49 import java.util.Optional;
    50 import java.util.stream.Collectors;
    51 import java.util.stream.Stream;
    53 @WebServlet(
    54         name = "ProjectsModule",
    55         urlPatterns = "/projects/*"
    56 )
    57 public final class ProjectsModule extends AbstractLightPITServlet {
    59     private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
    61     @Override
    62     protected String getResourceBundleName() {
    63         return "localization.projects";
    64     }
    66     private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObjects dao) throws SQLException {
    67         final var projectDao = dao.getProjectDao();
    68         final var versionDao = dao.getVersionDao();
    69         final var componentDao = dao.getComponentDao();
    71         projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
    73         if (pathParameters == null)
    74             return;
    76         // Select Project
    77         final var project = projectDao.findByNode(pathParameters.get("project"));
    78         if (project == null)
    79             return;
    81         final var info = new ProjectInfo(project);
    82         info.setVersions(versionDao.list(project));
    83         info.setComponents(componentDao.list(project));
    84         info.setIssueSummary(projectDao.getIssueSummary(project));
    85         viewModel.setProjectInfo(info);
    87         // Select Version
    88         final var versionNode = pathParameters.get("version");
    89         if ("no-version".equals(versionNode)) {
    90             viewModel.setVersionFilter(ProjectView.NO_VERSION);
    91         } else if ("all-versions".equals(versionNode)) {
    92             viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
    93         } else {
    94             viewModel.setVersionFilter(versionDao.findByNode(project, versionNode));
    95         }
    97         // Select Component
    98         final var componentNode = pathParameters.get("component");
    99         if ("no-component".equals(componentNode)) {
   100             viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
   101         } else if ("all-components".equals(componentNode)) {
   102             viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
   103         } else {
   104             viewModel.setComponentFilter(componentDao.findByNode(project, componentNode));
   105         }
   106     }
   108     private static String sanitizeNode(String node, String defaultValue) {
   109         String result = node == null || node.isBlank() ? defaultValue : node;
   110         result = result.replace('/', '-');
   111         if (result.equals(".") || result.equals("..")) {
   112             return "_"+result;
   113         } else {
   114             return result;
   115         }
   116     }
   118     private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) {
   119         setViewModel(req, viewModel);
   120         setContentPage(req, name);
   121         setStylesheet(req, "projects");
   122         setNavigationMenu(req, "project-navmenu");
   123         return ResponseType.HTML;
   124     }
   126     @RequestMapping(method = HttpMethod.GET)
   127     public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   128         final var viewModel = new ProjectView();
   129         populate(viewModel, null, dao);
   131         final var projectDao = dao.getProjectDao();
   132         final var versionDao = dao.getVersionDao();
   134         for (var info : viewModel.getProjectList()) {
   135             info.setVersions(versionDao.list(info.getProject()));
   136             info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
   137         }
   139         return forwardView(req, viewModel, "projects");
   140     }
   142     private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
   143         viewModel.setProject(project);
   144         viewModel.setUsers(dao.getUserDao().list());
   145     }
   147     @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
   148     public ResponseType edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws IOException, SQLException {
   149         final var viewModel = new ProjectEditView();
   150         populate(viewModel, pathParams, dao);
   152         if (!viewModel.isProjectInfoPresent()) {
   153             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   154             return ResponseType.NONE;
   155         }
   157         configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
   158         return forwardView(req, viewModel, "project-form");
   159     }
   161     @RequestMapping(requestPath = "create", method = HttpMethod.GET)
   162     public ResponseType create(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   163         final var viewModel = new ProjectEditView();
   164         populate(viewModel, null, dao);
   165         configureProjectEditor(viewModel, new Project(-1), dao);
   166         return forwardView(req, viewModel, "project-form");
   167     }
   169     @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   170     public ResponseType commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
   172         try {
   173             final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   174             project.setName(getParameter(req, String.class, "name").orElseThrow());
   176             final var node = getParameter(req, String.class, "node").orElse(null);
   177             project.setNode(sanitizeNode(node, project.getName()));
   179             getParameter(req, String.class, "description").ifPresent(project::setDescription);
   180             getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
   181             getParameter(req, Integer.class, "owner").map(
   182                     ownerId -> ownerId >= 0 ? new User(ownerId) : null
   183             ).ifPresent(project::setOwner);
   185             dao.getProjectDao().saveOrUpdate(project);
   187             setRedirectLocation(req, "./projects/");
   188             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   189             LOG.debug("Successfully updated project {}", project.getName());
   191             return ResponseType.HTML;
   192         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   193             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   194             // TODO: implement - fix issue #21
   195             return ResponseType.NONE;
   196         }
   197     }
   199     @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
   200     public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException {
   201         final var viewModel = new ProjectDetailsView();
   202         populate(viewModel, pathParams, dao);
   204         if (!viewModel.isEveryFilterValid()) {
   205             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   206             return ResponseType.NONE;
   207         }
   209         final var project = viewModel.getProjectInfo().getProject();
   210         final var version = viewModel.getVersionFilter();
   211         final var component = viewModel.getComponentFilter();
   213         final var issueDao = dao.getIssueDao();
   215         final List<Issue> issues;
   216         if (version.equals(ProjectView.NO_VERSION)) {
   217             if (component.equals(ProjectView.ALL_COMPONENTS)) {
   218                 issues = issueDao.list(project, (Version) null);
   219             } else if (component.equals(ProjectView.NO_COMPONENT)) {
   220                 issues = issueDao.list(project, null, null);
   221             } else {
   222                 issues = issueDao.list(project, component, null);
   223             }
   224         } else if (version.equals(ProjectView.ALL_VERSIONS)) {
   225             if (component.equals(ProjectView.ALL_COMPONENTS)) {
   226                 issues = issueDao.list(project);
   227             } else if (component.equals(ProjectView.NO_COMPONENT)) {
   228                 issues = issueDao.list(project, (Component)null);
   229             } else {
   230                 issues = issueDao.list(project, component);
   231             }
   232         } else {
   233             if (component.equals(ProjectView.ALL_COMPONENTS)) {
   234                 issues = issueDao.list(project, version);
   235             } else if (component.equals(ProjectView.NO_COMPONENT)) {
   236                 issues = issueDao.list(project, null, version);
   237             } else {
   238                 issues = issueDao.list(project, component, version);
   239             }
   240         }
   242         for (var issue : issues) issueDao.joinVersionInformation(issue);
   243         issues.sort(new IssueSorter(
   244                 new IssueSorter.Criteria(IssueSorter.Field.DONE, true),
   245                 new IssueSorter.Criteria(IssueSorter.Field.ETA, true),
   246                 new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false)
   247         ));
   250         viewModel.getProjectDetails().updateDetails(issues);
   251         if (version.getId() > 0)
   252             viewModel.getProjectDetails().updateVersionInfo(version);
   254         return forwardView(req, viewModel, "project-details");
   255     }
   257     @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
   258     public ResponseType versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   259         final var viewModel = new VersionsView();
   260         populate(viewModel, pathParameters, dao);
   262         final var projectInfo = viewModel.getProjectInfo();
   263         if (projectInfo == null) {
   264             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   265             return ResponseType.NONE;
   266         }
   268         final var issueDao = dao.getIssueDao();
   269         final var issues = issueDao.list(projectInfo.getProject());
   270         for (var issue : issues) issueDao.joinVersionInformation(issue);
   271         viewModel.update(projectInfo.getVersions(), issues);
   273         return forwardView(req, viewModel, "versions");
   274     }
   276     @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
   277     public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   278         final var viewModel = new VersionEditView();
   279         populate(viewModel, pathParameters, dao);
   281         if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) {
   282             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   283             return ResponseType.NONE;
   284         }
   286         viewModel.setVersion(viewModel.getVersionFilter());
   288         return forwardView(req, viewModel, "version-form");
   289     }
   291     @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
   292     public ResponseType createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   293         final var viewModel = new VersionEditView();
   294         populate(viewModel, pathParameters, dao);
   296         if (viewModel.getProjectInfo() == null) {
   297             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   298             return ResponseType.NONE;
   299         }
   301         viewModel.setVersion(viewModel.getVersionFilter());
   303         return forwardView(req, viewModel, "version-form");
   304     }
   306     @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
   307     public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
   309         try {
   310             final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
   311             if (project == null) {
   312                 // TODO: improve error handling, because not found is not correct for this POST request
   313                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   314                 return ResponseType.NONE;
   315             }
   316             final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
   317             version.setName(getParameter(req, String.class, "name").orElseThrow());
   319             final var node = getParameter(req, String.class, "node").orElse(null);
   320             version.setNode(sanitizeNode(node, version.getName()));
   322             getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
   323             version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
   324             dao.getVersionDao().saveOrUpdate(version, project);
   326             setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/");
   327             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   328         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   329             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   330             // TODO: implement - fix issue #21
   331             return ResponseType.NONE;
   332         }
   334         return ResponseType.HTML;
   335     }
   337     @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
   338     public ResponseType components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   339         final var viewModel = new ComponentsView();
   340         populate(viewModel, pathParameters, dao);
   342         final var projectInfo = viewModel.getProjectInfo();
   343         if (projectInfo == null) {
   344             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   345             return ResponseType.NONE;
   346         }
   348         final var issueDao = dao.getIssueDao();
   349         final var issues = issueDao.list(projectInfo.getProject());
   350         viewModel.update(projectInfo.getComponents(), issues);
   352         return forwardView(req, viewModel, "components");
   353     }
   355     @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
   356     public ResponseType editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   357         final var viewModel = new ComponentEditView();
   358         populate(viewModel, pathParameters, dao);
   360         if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) {
   361             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   362             return ResponseType.NONE;
   363         }
   365         viewModel.setComponent(viewModel.getComponentFilter());
   366         viewModel.setUsers(dao.getUserDao().list());
   368         return forwardView(req, viewModel, "component-form");
   369     }
   371     @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
   372     public ResponseType createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   373         final var viewModel = new ComponentEditView();
   374         populate(viewModel, pathParameters, dao);
   376         if (viewModel.getProjectInfo() == null) {
   377             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   378             return ResponseType.NONE;
   379         }
   381         viewModel.setComponent(new Component(-1));
   382         viewModel.setUsers(dao.getUserDao().list());
   384         return forwardView(req, viewModel, "component-form");
   385     }
   387     @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
   388     public ResponseType commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
   390         try {
   391             final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
   392             if (project == null) {
   393                 // TODO: improve error handling, because not found is not correct for this POST request
   394                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   395                 return ResponseType.NONE;
   396             }
   397             final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow());
   398             component.setName(getParameter(req, String.class, "name").orElseThrow());
   400             final var node = getParameter(req, String.class, "node").orElse(null);
   401             component.setNode(sanitizeNode(node, component.getName()));
   403             component.setColor(getParameter(req, WebColor.class, "color").orElseThrow());
   404             getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal);
   405             getParameter(req, Integer.class, "lead").map(
   406                     userid -> userid >= 0 ? new User(userid) : null
   407             ).ifPresent(component::setLead);
   408             getParameter(req, String.class, "description").ifPresent(component::setDescription);
   410             dao.getComponentDao().saveOrUpdate(component, project);
   412             setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
   413             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   414         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   415             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   416             // TODO: implement - fix issue #21
   417             return ResponseType.NONE;
   418         }
   420         return ResponseType.HTML;
   421     }
   423     private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
   424         final var project = viewModel.getProjectInfo().getProject();
   425         issue.setProject(project); // automatically set current project for new issues
   426         viewModel.setIssue(issue);
   427         viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
   428         viewModel.setUsers(dao.getUserDao().list());
   429         viewModel.setComponents(dao.getComponentDao().list(project));
   430     }
   432     @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
   433     public ResponseType viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   434         final var viewModel = new IssueDetailView();
   435         populate(viewModel, pathParameters, dao);
   437         final var projectInfo = viewModel.getProjectInfo();
   438         if (projectInfo == null) {
   439             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   440             return ResponseType.NONE;
   441         }
   443         final var issueDao = dao.getIssueDao();
   444         final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
   445         if (issue == null) {
   446             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   447             return ResponseType.NONE;
   448         }
   450         issueDao.joinVersionInformation(issue);
   451         viewModel.setIssue(issue);
   452         viewModel.setComments(issueDao.listComments(issue));
   454         return forwardView(req, viewModel, "issue-view");
   455     }
   457     // TODO: why should the issue editor be child of $project?
   458     @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
   459     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   460         final var viewModel = new IssueEditView();
   461         populate(viewModel, pathParameters, dao);
   463         final var projectInfo = viewModel.getProjectInfo();
   464         if (projectInfo == null) {
   465             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   466             return ResponseType.NONE;
   467         }
   469         final var issueDao = dao.getIssueDao();
   470         final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
   471         if (issue == null) {
   472             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   473             return ResponseType.NONE;
   474         }
   476         issueDao.joinVersionInformation(issue);
   477         configureIssueEditor(viewModel, issue, dao);
   479         return forwardView(req, viewModel, "issue-form");
   480     }
   482     @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
   483     public ResponseType createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   484         final var viewModel = new IssueEditView();
   485         populate(viewModel, pathParameters, dao);
   487         final var projectInfo = viewModel.getProjectInfo();
   488         if (projectInfo == null) {
   489             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   490             return ResponseType.NONE;
   491         }
   493         final var issue = new Issue(-1);
   494         issue.setProject(projectInfo.getProject());
   495         configureIssueEditor(viewModel, issue, dao);
   497         return forwardView(req, viewModel, "issue-form");
   498     }
   500     @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
   501     public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
   502         try {
   503             final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
   504             final var componentId = getParameter(req, Integer.class, "component");
   505             final Component component;
   506             if (componentId.isPresent()) {
   507                 component = dao.getComponentDao().find(componentId.get());
   508             } else {
   509                 component = null;
   510             }
   511             final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
   512             if (project == null) {
   513                 // TODO: improve error handling, because not found is not correct for this POST request
   514                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   515                 return ResponseType.NONE;
   516             }
   517             issue.setProject(project);
   518             getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
   519             getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
   520             issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
   521             issue.setComponent(component);
   522             getParameter(req, Integer.class, "assignee").map(userid -> {
   523                 if (userid >= 0) {
   524                     return new User(userid);
   525                 } else if (userid == -2) {
   526                     return Optional.ofNullable(component).map(Component::getLead).orElse(null);
   527                 } else {
   528                     return null;
   529                 }
   530             }
   531             ).ifPresent(issue::setAssignee);
   532             getParameter(req, String.class, "description").ifPresent(issue::setDescription);
   533             getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
   535             getParameter(req, Integer[].class, "affected")
   536                     .map(Stream::of)
   537                     .map(stream ->
   538                             stream.map(Version::new).collect(Collectors.toList())
   539                     ).ifPresent(issue::setAffectedVersions);
   540             getParameter(req, Integer[].class, "resolved")
   541                     .map(Stream::of)
   542                     .map(stream ->
   543                             stream.map(Version::new).collect(Collectors.toList())
   544                     ).ifPresent(issue::setResolvedVersions);
   546             dao.getIssueDao().saveOrUpdate(issue, issue.getProject());
   548             // TODO: fix redirect location
   549             setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
   550             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   552             return ResponseType.HTML;
   553         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   554             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   555             // TODO: implement - fix issue #21
   556             return ResponseType.NONE;
   557         }
   558     }
   560     @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
   561     public ResponseType commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
   562         final var issueIdParam = getParameter(req, Integer.class, "issueid");
   563         if (issueIdParam.isEmpty()) {
   564             resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
   565             return ResponseType.NONE;
   566         }
   567         final var issue = dao.getIssueDao().find(issueIdParam.get());
   568         if (issue == null) {
   569             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   570             return ResponseType.NONE;
   571         }
   572         try {
   573             final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue);
   574             issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
   576             if (issueComment.getComment().isBlank()) {
   577                 throw new IllegalArgumentException("comment.null");
   578             }
   580             LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
   581             if (req.getRemoteUser() != null) {
   582                 dao.getUserDao().findByUsername(req.getRemoteUser()).ifPresent(issueComment::setAuthor);
   583             }
   585             dao.getIssueDao().saveComment(issueComment);
   587             // TODO: fix redirect location
   588             setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
   589             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   591             return ResponseType.HTML;
   592         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   593             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   594             // TODO: implement - fix issue #21
   595             return ResponseType.NONE;
   596         }
   597     }
   598 }

mercurial