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

Thu, 15 Oct 2020 20:02:30 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 15 Oct 2020 20:02:30 +0200
changeset 131
67df332e3146
parent 129
a09d5c59351a
child 133
ef075cd7ce55
permissions
-rw-r--r--

changes request mapping to contain project and version ID as path parameters (this removes the session storage)

     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 de.uapcore.lightpit.viewmodel.util.IssueSorter;
    37 import org.slf4j.Logger;
    38 import org.slf4j.LoggerFactory;
    40 import javax.servlet.annotation.WebServlet;
    41 import javax.servlet.http.HttpServletRequest;
    42 import javax.servlet.http.HttpServletResponse;
    43 import java.io.IOException;
    44 import java.sql.Date;
    45 import java.sql.SQLException;
    46 import java.util.NoSuchElementException;
    47 import java.util.stream.Collectors;
    48 import java.util.stream.Stream;
    50 @WebServlet(
    51         name = "ProjectsModule",
    52         urlPatterns = "/projects/*"
    53 )
    54 public final class ProjectsModule extends AbstractLightPITServlet {
    56     private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
    58     @Override
    59     protected String getResourceBundleName() {
    60         return "localization.projects";
    61     }
    63     private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObjects dao) throws SQLException {
    64         final var projectDao = dao.getProjectDao();
    65         final var versionDao = dao.getVersionDao();
    66         final var componentDao = dao.getComponentDao();
    68         projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
    70         if (pathParameters == null)
    71             return;
    73         // Select Project
    74         final int pid = Functions.parseIntOrZero(pathParameters.get("project"));
    75         if (pid > 0) {
    76             final var project = projectDao.find(pid);
    77             if (project != null) {
    78                 final var info = new ProjectInfo(project);
    79                 info.setVersions(versionDao.list(project));
    80                 info.setComponents(componentDao.list(project));
    81                 info.setIssueSummary(projectDao.getIssueSummary(project));
    82                 viewModel.setProjectInfo(info);
    83             }
    84         }
    86         // Select Version
    87         final int vid = Functions.parseIntOrZero(pathParameters.get("version"));
    88         if (vid > 0) {
    89             viewModel.setVersionFilter(versionDao.find(vid));
    90         }
    91         // TODO: don't treat unknown == unassigned - send 404 for unknown and introduce special word for unassigned
    93         // Select Component
    94         final int cid = Functions.parseIntOrZero(pathParameters.get("component"));
    95         if (cid > 0) {
    96             viewModel.setComponentFilter(componentDao.find(cid));
    97         }
    99         // TODO: distinguish all/unassigned for components
   100     }
   102     private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) {
   103         setViewModel(req, viewModel);
   104         setContentPage(req, name);
   105         setStylesheet(req, "projects");
   106         setNavigationMenu(req, "project-navmenu");
   107         return ResponseType.HTML;
   108     }
   110     @RequestMapping(method = HttpMethod.GET)
   111     public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   112         final var viewModel = new ProjectView();
   113         populate(viewModel, null, dao);
   115         final var projectDao = dao.getProjectDao();
   116         final var versionDao = dao.getVersionDao();
   118         for (var info : viewModel.getProjectList()) {
   119             info.setVersions(versionDao.list(info.getProject()));
   120             info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
   121         }
   123         return forwardView(req, viewModel, "projects");
   124     }
   126     private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
   127         viewModel.setProject(project);
   128         viewModel.setUsers(dao.getUserDao().list());
   129     }
   131     @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
   132     public ResponseType edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws IOException, SQLException {
   133         final var viewModel = new ProjectEditView();
   134         populate(viewModel, pathParams, dao);
   136         if (viewModel.getProjectInfo() == null) {
   137             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   138             return ResponseType.NONE;
   139         }
   141         configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
   142         return forwardView(req, viewModel, "project-form");
   143     }
   145     @RequestMapping(requestPath = "create", method = HttpMethod.GET)
   146     public ResponseType create(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
   147         final var viewModel = new ProjectEditView();
   148         populate(viewModel, null, dao);
   149         configureProjectEditor(viewModel, new Project(-1), dao);
   150         return forwardView(req, viewModel, "project-form");
   151     }
   153     @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
   154     public ResponseType commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
   156         try {
   157             final var 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/");
   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             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   174             // TODO: implement - fix issue #21
   175             return ResponseType.NONE;
   176         }
   177     }
   179     @RequestMapping(requestPath = "$project/versions/$version", method = HttpMethod.GET)
   180     public ResponseType view(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException {
   181         final var viewModel = new ProjectDetailsView();
   182         populate(viewModel, pathParams, dao);
   183         final var version = viewModel.getVersionFilter();
   185         if (viewModel.getProjectInfo() == null || version == null) {
   186             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   187             return ResponseType.NONE;
   188         }
   190         final var issueDao = dao.getIssueDao();
   192         final var detailView = viewModel.getProjectDetails();
   193         final var issues = issueDao.list(version);
   194         for (var issue : issues) issueDao.joinVersionInformation(issue);
   195         issues.sort(new IssueSorter(
   196                 new IssueSorter.Criteria(IssueSorter.Field.PHASE, true),
   197                 new IssueSorter.Criteria(IssueSorter.Field.ETA, true),
   198                 new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false)
   199         ));
   200         detailView.updateDetails(issues, version);
   202         return forwardView(req, viewModel, "project-details");
   203     }
   205     @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
   206     public ResponseType versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   207         final var viewModel = new VersionsView();
   208         populate(viewModel, pathParameters, dao);
   210         final var projectInfo = viewModel.getProjectInfo();
   211         if (projectInfo == null) {
   212             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   213             return ResponseType.NONE;
   214         }
   216         final var issueDao = dao.getIssueDao();
   217         final var issues = issueDao.list(projectInfo.getProject());
   218         for (var issue : issues) issueDao.joinVersionInformation(issue);
   219         viewModel.update(projectInfo.getVersions(), issues);
   221         return forwardView(req, viewModel, "versions");
   222     }
   224     @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
   225     public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   226         final var viewModel = new VersionEditView();
   227         populate(viewModel, pathParameters, dao);
   229         if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) {
   230             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   231             return ResponseType.NONE;
   232         }
   234         viewModel.setVersion(viewModel.getVersionFilter());
   236         return forwardView(req, viewModel, "version-form");
   237     }
   239     @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
   240     public ResponseType createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   241         final var viewModel = new VersionEditView();
   242         populate(viewModel, pathParameters, dao);
   244         if (viewModel.getProjectInfo() == null) {
   245             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   246             return ResponseType.NONE;
   247         }
   249         viewModel.setVersion(viewModel.getVersionFilter());
   251         return forwardView(req, viewModel, "version-form");
   252     }
   254     @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
   255     public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
   257         try {
   258             final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
   259             final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
   260             version.setName(getParameter(req, String.class, "name").orElseThrow());
   261             getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
   262             version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
   263             dao.getVersionDao().saveOrUpdate(version, project);
   265             // TODO: improve building the redirect location
   266             setRedirectLocation(req, "./projects/" + project.getId() + "/versions/");
   267             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   268         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   269             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   270             // TODO: implement - fix issue #21
   271             return ResponseType.NONE;
   272         }
   274         return ResponseType.HTML;
   275     }
   277     private void configureProjectEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
   278         issue.setProject(viewModel.getProjectInfo().getProject());
   279         viewModel.setIssue(issue);
   280         viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
   281         viewModel.setUsers(dao.getUserDao().list());
   282         if (issue.getId() >= 0) {
   283             viewModel.setComments(dao.getIssueDao().listComments(issue));
   284         }
   285     }
   287     @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
   288     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   289         final var viewModel = new IssueEditView();
   290         populate(viewModel, pathParameters, dao);
   292         final var projectInfo = viewModel.getProjectInfo();
   293         if (projectInfo == null) {
   294             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   295             return ResponseType.NONE;
   296         }
   298         final var issueDao = dao.getIssueDao();
   299         final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
   300         if (issue == null) {
   301             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   302             return ResponseType.NONE;
   303         }
   305         issueDao.joinVersionInformation(issue);
   306         configureProjectEditor(viewModel, issue, dao);
   308         return forwardView(req, viewModel, "issue-form");
   309     }
   311     @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
   312     public ResponseType createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
   313         final var viewModel = new IssueEditView();
   314         populate(viewModel, pathParameters, dao);
   316         final var projectInfo = viewModel.getProjectInfo();
   317         if (projectInfo == null) {
   318             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   319             return ResponseType.NONE;
   320         }
   322         final var issue = new Issue(-1);
   323         issue.setProject(projectInfo.getProject());
   324         configureProjectEditor(viewModel, issue, dao);
   326         return forwardView(req, viewModel, "issue-form");
   327     }
   329     @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
   330     public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
   331         try {
   332             final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
   333             issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
   334             getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
   335             getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
   336             issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
   337             getParameter(req, Integer.class, "assignee").map(
   338                     userid -> userid >= 0 ? new User(userid) : null
   339             ).ifPresent(issue::setAssignee);
   340             getParameter(req, String.class, "description").ifPresent(issue::setDescription);
   341             getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
   343             getParameter(req, Integer[].class, "affected")
   344                     .map(Stream::of)
   345                     .map(stream ->
   346                             stream.map(Version::new).collect(Collectors.toList())
   347                     ).ifPresent(issue::setAffectedVersions);
   348             getParameter(req, Integer[].class, "resolved")
   349                     .map(Stream::of)
   350                     .map(stream ->
   351                             stream.map(Version::new).collect(Collectors.toList())
   352                     ).ifPresent(issue::setResolvedVersions);
   354             dao.getIssueDao().saveOrUpdate(issue, issue.getProject());
   356             // TODO: fix issue #14
   357             setRedirectLocation(req, "./projects/" + issue.getProject().getId() + "/versions/");
   358             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   360             return ResponseType.HTML;
   361         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   362             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   363             // TODO: implement - fix issue #21
   364             return ResponseType.NONE;
   365         }
   366     }
   368     @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
   369     public ResponseType commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
   370         final var issueIdParam = getParameter(req, Integer.class, "issueid");
   371         if (issueIdParam.isEmpty()) {
   372             resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
   373             return ResponseType.NONE;
   374         }
   375         final var issue = new Issue(issueIdParam.get());
   376         try {
   377             final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue);
   378             issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
   380             if (issueComment.getComment().isBlank()) {
   381                 throw new IllegalArgumentException("comment.null");
   382             }
   384             LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
   385             if (req.getRemoteUser() != null) {
   386                 dao.getUserDao().findByUsername(req.getRemoteUser()).ifPresent(issueComment::setAuthor);
   387             }
   389             dao.getIssueDao().saveComment(issueComment);
   391             // TODO: fix redirect location (e.g. after fixing #24)
   392             setRedirectLocation(req, "./projects/" + issue.getProject().getId()+"/issues/"+issue.getId()+"/edit");
   393             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
   395             return ResponseType.HTML;
   396         } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
   397             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
   398             // TODO: implement - fix issue #21
   399             return ResponseType.NONE;
   400         }
   401     }
   402 }

mercurial