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

Wed, 06 Jan 2021 15:39:56 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 06 Jan 2021 15:39:56 +0100
changeset 180
009700915269
parent 179
623c340058f3
permissions
-rw-r--r--

merge resource bundles

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2021 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.DataAccessObject;
    34 import de.uapcore.lightpit.entities.*;
    35 import de.uapcore.lightpit.filter.AllFilter;
    36 import de.uapcore.lightpit.filter.IssueFilter;
    37 import de.uapcore.lightpit.filter.NoneFilter;
    38 import de.uapcore.lightpit.filter.SpecificFilter;
    39 import de.uapcore.lightpit.types.IssueCategory;
    40 import de.uapcore.lightpit.types.IssueStatus;
    41 import de.uapcore.lightpit.types.VersionStatus;
    42 import de.uapcore.lightpit.types.WebColor;
    43 import de.uapcore.lightpit.viewmodel.*;
    44 import de.uapcore.lightpit.viewmodel.util.IssueSorter;
    45 import org.slf4j.Logger;
    46 import org.slf4j.LoggerFactory;
    48 import javax.servlet.ServletException;
    49 import javax.servlet.annotation.WebServlet;
    50 import javax.servlet.http.HttpServletRequest;
    51 import javax.servlet.http.HttpServletResponse;
    52 import java.io.IOException;
    53 import java.sql.Date;
    54 import java.sql.SQLException;
    55 import java.util.NoSuchElementException;
    56 import java.util.Optional;
    57 import java.util.stream.Collectors;
    58 import java.util.stream.Stream;
    60 @WebServlet(
    61         name = "ProjectsModule",
    62         urlPatterns = "/projects/*"
    63 )
    64 public final class ProjectsModule extends AbstractServlet {
    66     private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
    68     private static int parseIntOrZero(String str) {
    69         try {
    70             return Integer.parseInt(str);
    71         } catch (NumberFormatException ex) {
    72             return 0;
    73         }
    74     }
    76     private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObject dao) {
    77         dao.listProjects().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
    79         if (pathParameters == null)
    80             return;
    82         // Select Project
    83         final var project = dao.findProjectByNode(pathParameters.get("project"));
    84         if (project == null)
    85             return;
    87         final var info = new ProjectInfo(project);
    88         info.setVersions(dao.listVersions(project));
    89         info.setComponents(dao.listComponents(project));
    90         info.setIssueSummary(dao.collectIssueSummary(project));
    91         viewModel.setProjectInfo(info);
    93         // Select Version
    94         final var versionNode = pathParameters.get("version");
    95         if (versionNode != null) {
    96             if ("no-version".equals(versionNode)) {
    97                 viewModel.setVersionFilter(ProjectView.NO_VERSION);
    98             } else if ("all-versions".equals(versionNode)) {
    99                 viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
   100             } else {
   101                 viewModel.setVersionFilter(dao.findVersionByNode(project, versionNode));
   102             }
   103         }
   105         // Select Component
   106         final var componentNode = pathParameters.get("component");
   107         if (componentNode != null) {
   108             if ("no-component".equals(componentNode)) {
   109                 viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
   110             } else if ("all-components".equals(componentNode)) {
   111                 viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
   112             } else {
   113                 viewModel.setComponentFilter(dao.findComponentByNode(project, componentNode));
   114             }
   115         }
   116     }
   118     private static String sanitizeNode(String node, String defaultValue) {
   119         String result = node == null || node.isBlank() ? defaultValue : node;
   120         result = result.replace('/', '-');
   121         if (result.equals(".") || result.equals("..")) {
   122             return "_"+result;
   123         } else {
   124             return result;
   125         }
   126     }
   128     private void forwardView(HttpServletRequest req, HttpServletResponse resp, ProjectView viewModel, String name) throws ServletException, IOException {
   129         setViewModel(req, viewModel);
   130         setContentPage(req, name);
   131         setStylesheet(req, "projects");
   132         setNavigationMenu(req, "project-navmenu");
   133         renderSite(req, resp);
   134     }
   136     @RequestMapping(method = HttpMethod.GET)
   137     public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws ServletException, IOException {
   138         final var viewModel = new ProjectView();
   139         populate(viewModel, null, dao);
   141         for (var info : viewModel.getProjectList()) {
   142             info.setVersions(dao.listVersions(info.getProject()));
   143             info.setIssueSummary(dao.collectIssueSummary(info.getProject()));
   144         }
   146         forwardView(req, resp, viewModel, "projects");
   147     }
   149     private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObject dao) {
   150         viewModel.setProject(project);
   151         viewModel.setUsers(dao.listUsers());
   152     }
   154     @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
   155     public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws IOException, SQLException, ServletException {
   156         final var viewModel = new ProjectEditView();
   157         populate(viewModel, pathParams, dao);
   159         if (!viewModel.isProjectInfoPresent()) {
   160             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   161             return;
   162         }
   164         configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
   165         forwardView(req, resp, viewModel, "project-form");
   166     }
   168     @RequestMapping(requestPath = "create", method = HttpMethod.GET)
   169     public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws SQLException, ServletException, IOException {
   170         final var viewModel = new ProjectEditView();
   171         populate(viewModel, null, dao);
   172         configureProjectEditor(viewModel, new Project(-1), dao);
   173         forwardView(req, resp, viewModel, "project-form");
   174     }
   176     @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   177     public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   179         try {
   180             final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   181             project.setName(getParameter(req, String.class, "name").orElseThrow());
   183             final var node = getParameter(req, String.class, "node").orElse(null);
   184             project.setNode(sanitizeNode(node, project.getName()));
   185             getParameter(req, Integer.class, "ordinal").ifPresent(project::setOrdinal);
   187             getParameter(req, String.class, "description").ifPresent(project::setDescription);
   188             getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
   189             getParameter(req, Integer.class, "owner").map(
   190                     ownerId -> ownerId >= 0 ? new User(ownerId) : null
   191             ).ifPresent(project::setOwner);
   193             if (project.getId() > 0) {
   194                 dao.updateProject(project);
   195             } else {
   196                 dao.insertProject(project);
   197             }
   199             setRedirectLocation(req, "./projects/");
   200             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   201             LOG.debug("Successfully updated project {}", project.getName());
   203             renderSite(req, resp);
   204         } catch (NoSuchElementException | IllegalArgumentException ex) {
   205             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   206             // TODO: implement - fix issue #21
   207         }
   208     }
   210     @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
   211     public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws SQLException, IOException, ServletException {
   212         final var viewModel = new ProjectDetailsView();
   213         populate(viewModel, pathParams, dao);
   215         if (!viewModel.isEveryFilterValid()) {
   216             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   217             return;
   218         }
   220         final var project = viewModel.getProjectInfo().getProject();
   221         final var version = viewModel.getVersionFilter();
   222         final var component = viewModel.getComponentFilter();
   224         // TODO: use new IssueFilter class for the ViewModel
   226         final var projectFilter = new SpecificFilter<>(project);
   227         final IssueFilter filter;
   228         if (version.equals(ProjectView.NO_VERSION)) {
   229             if (component.equals(ProjectView.ALL_COMPONENTS)) {
   230                 filter = new IssueFilter(projectFilter,
   231                         new NoneFilter<>(),
   232                         new AllFilter<>()
   233                 );
   234             } else if (component.equals(ProjectView.NO_COMPONENT)) {
   235                 filter = new IssueFilter(projectFilter,
   236                         new NoneFilter<>(),
   237                         new NoneFilter<>()
   238                 );
   239             } else {
   240                 filter = new IssueFilter(projectFilter,
   241                         new NoneFilter<>(),
   242                         new SpecificFilter<>(component)
   243                 );
   244             }
   245         } else if (version.equals(ProjectView.ALL_VERSIONS)) {
   246             if (component.equals(ProjectView.ALL_COMPONENTS)) {
   247                 filter = new IssueFilter(projectFilter,
   248                         new AllFilter<>(),
   249                         new AllFilter<>()
   250                 );
   251             } else if (component.equals(ProjectView.NO_COMPONENT)) {
   252                 filter = new IssueFilter(projectFilter,
   253                         new AllFilter<>(),
   254                         new NoneFilter<>()
   255                 );
   256             } else {
   257                 filter = new IssueFilter(projectFilter,
   258                         new AllFilter<>(),
   259                         new SpecificFilter<>(component)
   260                 );
   261             }
   262         } else {
   263             if (component.equals(ProjectView.ALL_COMPONENTS)) {
   264                 filter = new IssueFilter(projectFilter,
   265                         new SpecificFilter<>(version),
   266                         new AllFilter<>()
   267                 );
   268             } else if (component.equals(ProjectView.NO_COMPONENT)) {
   269                 filter = new IssueFilter(projectFilter,
   270                         new SpecificFilter<>(version),
   271                         new NoneFilter<>()
   272                 );
   273             } else {
   274                 filter = new IssueFilter(projectFilter,
   275                         new SpecificFilter<>(version),
   276                         new SpecificFilter<>(component)
   277                 );
   278             }
   279         }
   281         final var issues = dao.listIssues(filter);
   282         issues.sort(new IssueSorter(
   283                 new IssueSorter.Criteria(IssueSorter.Field.DONE, true),
   284                 new IssueSorter.Criteria(IssueSorter.Field.ETA, true),
   285                 new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false)
   286         ));
   289         viewModel.getProjectDetails().updateDetails(issues);
   290         if (version.getId() > 0)
   291             viewModel.getProjectDetails().updateVersionInfo(version);
   293         forwardView(req, resp, viewModel, "project-details");
   294     }
   296     @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
   297     public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
   298         final var viewModel = new VersionsView();
   299         populate(viewModel, pathParameters, dao);
   301         final var projectInfo = viewModel.getProjectInfo();
   302         if (projectInfo == null) {
   303             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   304             return;
   305         }
   307         final var issues = dao.listIssues(
   308                 new IssueFilter(
   309                         new SpecificFilter<>(projectInfo.getProject()),
   310                         new AllFilter<>(),
   311                         new AllFilter<>()
   312                 )
   313         );
   314         viewModel.update(projectInfo.getVersions(), issues);
   316         forwardView(req, resp, viewModel, "versions");
   317     }
   319     @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
   320     public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   321         final var viewModel = new VersionEditView();
   322         populate(viewModel, pathParameters, dao);
   324         if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) {
   325             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   326             return;
   327         }
   329         viewModel.setVersion(viewModel.getVersionFilter());
   331         forwardView(req, resp, viewModel, "version-form");
   332     }
   334     @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
   335     public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   336         final var viewModel = new VersionEditView();
   337         populate(viewModel, pathParameters, dao);
   339         if (viewModel.getProjectInfo() == null) {
   340             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   341             return;
   342         }
   344         viewModel.setVersion(new Version(-1, viewModel.getProjectInfo().getProject().getId()));
   346         forwardView(req, resp, viewModel, "version-form");
   347     }
   349     @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
   350     public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   352         try {
   353             final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
   354             if (project == null) {
   355                 // TODO: improve error handling, because not found is not correct for this POST request
   356                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   357                 return;
   358             }
   359             final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
   360             version.setName(getParameter(req, String.class, "name").orElseThrow());
   362             final var node = getParameter(req, String.class, "node").orElse(null);
   363             version.setNode(sanitizeNode(node, version.getName()));
   365             getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
   366             version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
   368             if (version.getId() > 0) {
   369                 dao.updateVersion(version);
   370             } else {
   371                 dao.insertVersion(version);
   372             }
   374             setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/");
   375             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   377             renderSite(req, resp);
   378         } catch (NoSuchElementException | IllegalArgumentException ex) {
   379             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   380             // TODO: implement - fix issue #21
   381         }
   382     }
   384     @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
   385     public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
   386         final var viewModel = new ComponentsView();
   387         populate(viewModel, pathParameters, dao);
   389         final var projectInfo = viewModel.getProjectInfo();
   390         if (projectInfo == null) {
   391             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   392             return;
   393         }
   395         final var issues = dao.listIssues(
   396                 new IssueFilter(
   397                         new SpecificFilter<>(projectInfo.getProject()),
   398                         new AllFilter<>(),
   399                         new AllFilter<>()
   400                 )
   401         );
   402         viewModel.update(projectInfo.getComponents(), issues);
   404         forwardView(req, resp, viewModel, "components");
   405     }
   407     @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
   408     public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   409         final var viewModel = new ComponentEditView();
   410         populate(viewModel, pathParameters, dao);
   412         if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) {
   413             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   414             return;
   415         }
   417         viewModel.setComponent(viewModel.getComponentFilter());
   418         viewModel.setUsers(dao.listUsers());
   420         forwardView(req, resp, viewModel, "component-form");
   421     }
   423     @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
   424     public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   425         final var viewModel = new ComponentEditView();
   426         populate(viewModel, pathParameters, dao);
   428         if (viewModel.getProjectInfo() == null) {
   429             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   430             return;
   431         }
   433         viewModel.setComponent(new Component(-1, viewModel.getProjectInfo().getProject().getId()));
   434         viewModel.setUsers(dao.listUsers());
   436         forwardView(req, resp, viewModel, "component-form");
   437     }
   439     @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
   440     public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   442         try {
   443             final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
   444             if (project == null) {
   445                 // TODO: improve error handling, because not found is not correct for this POST request
   446                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   447                 return;
   448             }
   449             final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
   450             component.setName(getParameter(req, String.class, "name").orElseThrow());
   452             final var node = getParameter(req, String.class, "node").orElse(null);
   453             component.setNode(sanitizeNode(node, component.getName()));
   455             component.setColor(getParameter(req, WebColor.class, "color").orElseThrow());
   456             getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal);
   457             getParameter(req, Integer.class, "lead").map(
   458                     userid -> userid >= 0 ? new User(userid) : null
   459             ).ifPresent(component::setLead);
   460             getParameter(req, String.class, "description").ifPresent(component::setDescription);
   462             if (component.getId() > 0) {
   463                 dao.updateComponent(component);
   464             } else {
   465                 dao.insertComponent(component);
   466             }
   468             setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
   469             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   471             renderSite(req, resp);
   472         } catch (NoSuchElementException | IllegalArgumentException ex) {
   473             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   474             // TODO: implement - fix issue #21
   475         }
   476     }
   478     private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObject dao) {
   479         final var project = viewModel.getProjectInfo().getProject();
   480         issue.setProject(project); // automatically set current project for new issues
   481         viewModel.setIssue(issue);
   482         viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
   483         viewModel.setUsers(dao.listUsers());
   484         viewModel.setComponents(dao.listComponents(project));
   485     }
   487     @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
   488     public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   489         final var viewModel = new IssueDetailView();
   490         populate(viewModel, pathParameters, dao);
   492         final var projectInfo = viewModel.getProjectInfo();
   493         if (projectInfo == null) {
   494             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   495             return;
   496         }
   498         final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
   499         if (issue == null) {
   500             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   501             return;
   502         }
   504         viewModel.setIssue(issue);
   505         viewModel.setComments(dao.listComments(issue));
   507         viewModel.processMarkdown();
   509         forwardView(req, resp, viewModel, "issue-view");
   510     }
   512     // TODO: why should the issue editor be child of $project?
   513     @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
   514     public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
   515         final var viewModel = new IssueEditView();
   516         populate(viewModel, pathParameters, dao);
   518         final var projectInfo = viewModel.getProjectInfo();
   519         if (projectInfo == null) {
   520             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   521             return;
   522         }
   524         final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
   525         if (issue == null) {
   526             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   527             return;
   528         }
   530         configureIssueEditor(viewModel, issue, dao);
   532         forwardView(req, resp, viewModel, "issue-form");
   533     }
   535     @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
   536     public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
   537         final var viewModel = new IssueEditView();
   538         populate(viewModel, pathParameters, dao);
   540         final var projectInfo = viewModel.getProjectInfo();
   541         if (projectInfo == null) {
   542             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   543             return;
   544         }
   546         setAttributeFromParameter(req, "more");
   547         setAttributeFromParameter(req, "cid");
   548         setAttributeFromParameter(req, "vid");
   550         final var issue = new Issue(-1, projectInfo.getProject(), null);
   551         issue.setProject(projectInfo.getProject());
   552         configureIssueEditor(viewModel, issue, dao);
   554         forwardView(req, resp, viewModel, "issue-form");
   555     }
   557     @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
   558     public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   559         try {
   560             final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
   561             if (project == null) {
   562                 // TODO: improve error handling, because not found is not correct for this POST request
   563                 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   564                 return;
   565             }
   566             final var componentId = getParameter(req, Integer.class, "component");
   567             final Component component;
   568             if (componentId.isPresent()) {
   569                 component = dao.findComponent(componentId.get());
   570             } else {
   571                 component = null;
   572             }
   573             final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), project, component);
   574             getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
   575             getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
   576             issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
   577             getParameter(req, Integer.class, "assignee").map(userid -> {
   578                 if (userid >= 0) {
   579                     return new User(userid);
   580                 } else if (userid == -2) {
   581                     return Optional.ofNullable(component).map(Component::getLead).orElse(null);
   582                 } else {
   583                     return null;
   584                 }
   585             }
   586             ).ifPresent(issue::setAssignee);
   587             getParameter(req, String.class, "description").ifPresent(issue::setDescription);
   588             getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
   590             getParameter(req, Integer[].class, "affected")
   591                     .map(Stream::of)
   592                     .map(stream ->
   593                             stream.map(id -> new Version(id, project.getId()))
   594                                     .collect(Collectors.toList())
   595                     ).ifPresent(issue::setAffectedVersions);
   596             getParameter(req, Integer[].class, "resolved")
   597                     .map(Stream::of)
   598                     .map(stream ->
   599                             stream.map(id -> new Version(id, project.getId()))
   600                                     .collect(Collectors.toList())
   601                     ).ifPresent(issue::setResolvedVersions);
   603             if (issue.getId() > 0) {
   604                 dao.updateIssue(issue);
   605             } else {
   606                 dao.insertIssue(issue);
   607             }
   609             if (getParameter(req, Boolean.class, "create-another").orElse(false)) {
   610                 // TODO: fix #38 - automatically select component (and version)
   611                 setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/create-issue?more=true");
   612             } else{
   613                 setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/issues/" + issue.getId() + "/view");
   614             }
   615             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   617             renderSite(req, resp);
   618         } catch (NoSuchElementException | IllegalArgumentException ex) {
   619             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   620             // TODO: implement - fix issue #21
   621         }
   622     }
   624     @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
   625     public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
   626         final var issueIdParam = getParameter(req, Integer.class, "issueid");
   627         if (issueIdParam.isEmpty()) {
   628             resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
   629             return;
   630         }
   631         final var issue = dao.findIssue(issueIdParam.get());
   632         if (issue == null) {
   633             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   634             return;
   635         }
   636         try {
   637             final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue.getId());
   638             issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
   640             if (issueComment.getComment().isBlank()) {
   641                 throw new IllegalArgumentException("comment.null");
   642             }
   644             LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
   645             if (req.getRemoteUser() != null) {
   646                 Optional.ofNullable(dao.findUserByName(req.getRemoteUser())).ifPresent(issueComment::setAuthor);
   647             }
   649             dao.insertComment(issueComment);
   651             setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
   652             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   654             renderSite(req, resp);
   655         } catch (NoSuchElementException | IllegalArgumentException ex) {
   656             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   657             // TODO: implement - fix issue #21
   658         }
   659     }
   660 }

mercurial